weaver-work-cli 0.1.40 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/dist/chunks/{archive-JHTONRST.js → archive-J4I6MCKF.js} +2 -2
  2. package/dist/chunks/{archive-oa-FCHLCPHC.js → archive-oa-BRZKKTO4.js} +2 -2
  3. package/dist/chunks/{asset-RLIHC7DB.js → asset-L7D4FAOZ.js} +2 -2
  4. package/dist/chunks/{asset-idle-KVOFTLCP.js → asset-idle-DADLLV75.js} +2 -2
  5. package/dist/chunks/{attachment-content-RZYDB3MB.js → attachment-content-K4C7VLWQ.js} +3 -3
  6. package/dist/chunks/{blog-42OZTJNP.js → blog-BVGQ7NZL.js} +2 -2
  7. package/dist/chunks/{caizhilian-KRK547K5.js → caizhilian-SAADNFD6.js} +2 -2
  8. package/dist/chunks/{calendar-RTWAMKHD.js → calendar-Q7NQY5Z2.js} +61 -23
  9. package/dist/chunks/{chunk-LFMSDBZN.js → chunk-3KTFS4IA.js} +308 -153
  10. package/dist/chunks/{chunk-G2JSIUWF.js → chunk-4PD5NWAX.js} +89 -0
  11. package/dist/chunks/{chunk-T6EGVBKU.js → chunk-4RAE6JXP.js} +1 -1
  12. package/dist/chunks/{chunk-7BRU4EFH.js → chunk-72CKAUAG.js} +29 -2
  13. package/dist/chunks/{chunk-NETCCARD.js → chunk-APVFJUQH.js} +2 -2
  14. package/dist/chunks/{chunk-CAQNBBQA.js → chunk-BBKDXQEC.js} +2 -2
  15. package/dist/chunks/{chunk-57IECMWX.js → chunk-BIPYLLVS.js} +26 -12
  16. package/dist/chunks/{chunk-LN447PY2.js → chunk-EKWAQSLU.js} +3 -2
  17. package/dist/chunks/{chunk-4TEDNCON.js → chunk-FOAA27U5.js} +338 -52
  18. package/dist/chunks/{chunk-7U577Q7U.js → chunk-G4UR6NXN.js} +2 -2
  19. package/dist/chunks/{chunk-NJKREDA3.js → chunk-GEWYMQME.js} +1 -1
  20. package/dist/chunks/{chunk-I566ZYNF.js → chunk-GHXCDPTM.js} +1 -1
  21. package/dist/chunks/{chunk-4CTESSKI.js → chunk-IZ7S7QUS.js} +4 -4
  22. package/dist/chunks/{chunk-VNBU55SL.js → chunk-JPRO6ER2.js} +57 -2432
  23. package/dist/chunks/{chunk-IZSSVYAG.js → chunk-LADPYOWC.js} +2 -2
  24. package/dist/chunks/chunk-LDZQPUIA.js +1238 -0
  25. package/dist/chunks/chunk-NKZRQXST.js +2546 -0
  26. package/dist/chunks/{chunk-JWRZM2H2.js → chunk-QKNKR4RH.js} +3 -3
  27. package/dist/chunks/{chunk-FLS4DPGP.js → chunk-R4CHUSKC.js} +28 -15
  28. package/dist/chunks/{chunk-WFFNM65B.js → chunk-SA6VBIHY.js} +73 -19
  29. package/dist/chunks/{chunk-SPRHHSVC.js → chunk-TTPJJJC4.js} +2 -2
  30. package/dist/chunks/{chunk-7E6MUOEK.js → chunk-W7BEHOSR.js} +2 -2
  31. package/dist/chunks/{chunk-CXRQWOZ5.js → chunk-YRSZQUD2.js} +2 -2
  32. package/dist/chunks/{conflict-VR55AOOO.js → conflict-F2OMZRTO.js} +2 -2
  33. package/dist/chunks/{contract-HI4FNPYE.js → contract-5BETHRSG.js} +2 -2
  34. package/dist/chunks/{cowork-HJLI353D.js → cowork-3MN6EQQO.js} +22 -12
  35. package/dist/chunks/create-flow-5FQNNMVO.js +55 -0
  36. package/dist/chunks/{ebuilder-form-HTGI63UU.js → ebuilder-form-SBHOYBUD.js} +3 -3
  37. package/dist/chunks/{ehr-GYXIYH3A.js → ehr-KMOJULKG.js} +432 -1234
  38. package/dist/chunks/{env-DW3G4HUO.js → env-LB7NBL2O.js} +2 -2
  39. package/dist/chunks/{esb-I5INCEFE.js → esb-W2PJCFCX.js} +4 -4
  40. package/dist/chunks/{factory-TKXGXS6L.js → factory-6ZFQXQ46.js} +1 -1
  41. package/dist/chunks/{fna-K2MHTQ24.js → fna-ZQPCUV4R.js} +2 -2
  42. package/dist/chunks/{group-chat-VYBNMJLG.js → group-chat-2HHAJN46.js} +5 -3
  43. package/dist/chunks/{host-ZUQTJYYK.js → host-4CBHGOHT.js} +3 -3
  44. package/dist/chunks/{host-D5SERMAJ.js → host-ATKVNZAT.js} +3 -3
  45. package/dist/chunks/{host-JM4YVHFM.js → host-BQH22NTQ.js} +3 -3
  46. package/dist/chunks/{host-L3VLITMT.js → host-X2CSLFM6.js} +3 -3
  47. package/dist/chunks/{host-256GVCAH.js → host-YTODKMSL.js} +3 -3
  48. package/dist/chunks/{hrm-VLRBKTZB.js → hrm-FFM4UJFS.js} +4 -4
  49. package/dist/chunks/{im-QK3VTLXK.js → im-VEHKDQPB.js} +7 -7
  50. package/dist/chunks/{invoice-T5JTA32H.js → invoice-LVIKSIIF.js} +3 -3
  51. package/dist/chunks/{jiuchuanhui-P2FNEQVZ.js → jiuchuanhui-X6DINO5P.js} +2 -2
  52. package/dist/chunks/{lazy-dispatch-XRRI46M6.js → lazy-dispatch-KSWTN5L6.js} +4 -4
  53. package/dist/chunks/{mail-3Y7MJ6PS.js → mail-VDKRGUD3.js} +2 -2
  54. package/dist/chunks/{meeting-M7LTXGJE.js → meeting-CLC6HP3Z.js} +12 -11
  55. package/dist/chunks/{okr-OBC6S67L.js → okr-CHG4OO3U.js} +2 -2
  56. package/dist/chunks/{plan-5H6I53SK.js → plan-XWR75667.js} +2 -2
  57. package/dist/chunks/{project-L75QZV4S.js → project-5BXOOKMG.js} +40 -23
  58. package/dist/chunks/recruit-stage-DGMFNNDE.js +4507 -0
  59. package/dist/chunks/{registry-RXPN6BBT.js → registry-7FFOXIAW.js} +4 -4
  60. package/dist/chunks/{registry-KMHCPAAD.js → registry-BKBZPBAJ.js} +10 -9
  61. package/dist/chunks/{registry-QGHFNUWS.js → registry-OZSMDVPP.js} +2 -2
  62. package/dist/chunks/{registry-UGRBCBHO.js → registry-RHMW6ANW.js} +2 -2
  63. package/dist/chunks/{registry-NYPYMZXG.js → registry-Z43434VG.js} +1 -1
  64. package/dist/chunks/{room-recommend-A3UDC3CA.js → room-recommend-62H7ZQQ6.js} +4 -4
  65. package/dist/chunks/{schedule-5GCACCK7.js → schedule-FNXKH3DQ.js} +4 -4
  66. package/dist/chunks/{workflow-M5ZJ7DWF.js → workflow-ADHKCAJK.js} +6 -6
  67. package/dist/index.js +33 -28
  68. package/dist/internal/e10/auth/commands.js +1 -0
  69. package/dist/internal/e10/auth/messages.js +6 -0
  70. package/dist/internal/e10/auth/session.js +17 -0
  71. package/dist/internal/e10/auth/xiaoe.js +286 -44
  72. package/dist/internal/e10/auth-recovery.js +36 -5
  73. package/dist/internal/e10/context.js +56 -12
  74. package/dist/internal/e10/index.js +1 -1
  75. package/dist/internal/e10/network-error.js +93 -0
  76. package/dist/internal/e10/request-runtime.js +6 -0
  77. package/dist/shortcuts/calendar/operations/read.js +31 -22
  78. package/dist/shortcuts/calendar/operations/write.js +45 -1
  79. package/dist/shortcuts/cowork/operations/discuss-write.js +5 -1
  80. package/dist/shortcuts/cowork/operations/item-write.js +1 -7
  81. package/dist/shortcuts/cowork/operations/member-write.js +3 -1
  82. package/dist/shortcuts/cowork/operations/permission-write.js +5 -1
  83. package/dist/shortcuts/cowork/operations/shared.js +7 -0
  84. package/dist/shortcuts/ehr/operations/attend-write.js +43 -1
  85. package/dist/shortcuts/ehr/operations/hr-contact.js +7 -4
  86. package/dist/shortcuts/ehr/operations/hr-report.js +13 -7
  87. package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +134 -59
  88. package/dist/shortcuts/ehr/operations/recruit-write.js +262 -17
  89. package/dist/shortcuts/ehr/operations/recruit.js +10 -4
  90. package/dist/shortcuts/ehr/operations/shared.js +47 -0
  91. package/dist/shortcuts/hrm/operations/org.js +118 -0
  92. package/dist/shortcuts/index.js +5 -0
  93. package/dist/shortcuts/meeting/operations/eb/catalog.js +4 -1
  94. package/dist/shortcuts/meeting/operations/eb/create-flow.js +85 -28
  95. package/dist/shortcuts/meeting/operations/eb/factory.js +33 -0
  96. package/dist/shortcuts/meeting/operations/eb/group-chat.js +25 -3
  97. package/dist/shortcuts/meeting/operations/eb/person-cache.js +96 -0
  98. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +33 -11
  99. package/dist/shortcuts/project/operations/read.js +6 -25
  100. package/dist/shortcuts/project/operations/shared.js +22 -0
  101. package/dist/shortcuts/project/operations/write.js +28 -2
  102. package/dist/shortcuts/recruit-stage/context.js +119 -0
  103. package/dist/shortcuts/recruit-stage/continuation.js +73 -0
  104. package/dist/shortcuts/recruit-stage/errors.js +65 -0
  105. package/dist/shortcuts/recruit-stage/host.js +109 -0
  106. package/dist/shortcuts/recruit-stage/index.js +115 -0
  107. package/dist/shortcuts/recruit-stage/manifest.js +29 -0
  108. package/dist/shortcuts/recruit-stage/operations/interview-arrange.js +722 -0
  109. package/dist/shortcuts/recruit-stage/operations/interview-feedback.js +269 -0
  110. package/dist/shortcuts/recruit-stage/operations/jd-create.js +630 -0
  111. package/dist/shortcuts/recruit-stage/operations/list.js +431 -0
  112. package/dist/shortcuts/recruit-stage/operations/registry.js +38 -0
  113. package/dist/shortcuts/recruit-stage/operations/resume-import.js +1198 -0
  114. package/dist/shortcuts/recruit-stage/operations/search.js +158 -0
  115. package/dist/shortcuts/recruit-stage/operations/types.js +25 -0
  116. package/dist/shortcuts/recruit-stage/school-tiers.json +318 -0
  117. package/dist/shortcuts/recruit-stage/shared.js +608 -0
  118. package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +4 -2
  119. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +13 -6
  120. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +17 -24
  121. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +14 -9
  122. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +50 -8
  123. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +2 -1
  124. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +17 -11
  125. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +7 -39
  126. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +3 -2
  127. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +51 -15
  128. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.pipeline.js +16 -1
  129. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +151 -63
  130. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.subGroup.js +56 -0
  131. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +39 -14
  132. package/docs/e10-auth.md +7 -1
  133. package/docs/operation-manual.md +8 -0
  134. package/package.json +1 -1
  135. package/scripts/install-skill-to-xiaoe.mjs +105 -0
  136. package/scripts/package-skill.mjs +4 -2
  137. package/skill-template/business-info.json +24 -0
  138. package/skill-template/domains/recruit-stage.md +29 -0
  139. package/skills/weaver-e10-calendar/SKILL.md +2 -1
  140. package/skills/weaver-e10-calendar/references/calendar-write.md +54 -0
  141. package/skills/weaver-e10-cowork/SKILL.md +1 -1
  142. package/skills/weaver-e10-cowork/references/cowork-write.md +3 -1
  143. package/skills/weaver-e10-jucailin/SKILL.md +1 -1
  144. package/skills/weaver-e10-jucailin/references/attend-write.md +8 -0
  145. package/skills/weaver-e10-jucailin/references/hr-contact.md +8 -0
  146. package/skills/weaver-e10-jucailin/references/hr-report.md +8 -0
  147. package/skills/weaver-e10-jucailin/references/recruit-create.md +2 -2
  148. package/skills/weaver-e10-jucailin/references/recruit-read.md +1 -1
  149. package/skills/weaver-e10-jucailin/references/recruit-write.md +4 -3
  150. package/skills/weaver-e10-mail/SKILL.md +1 -1
  151. package/skills/weaver-e10-meeting/SKILL.md +32 -2
  152. package/skills/weaver-e10-meeting.zip +0 -0
  153. package/skills/weaver-e10-recruit-stage/SKILL.md +226 -0
  154. package/skills/weaver-e10-recruit-stage/agents/openai.yaml +119 -0
  155. package/skills/weaver-e10-recruit-stage/lib/recruit-xiaoe-file.mjs +188 -0
  156. package/skills/weaver-e10-recruit-stage/references/recruit-arrange-interview.md +322 -0
  157. package/skills/weaver-e10-recruit-stage/references/recruit-capability-check.md +127 -0
  158. package/skills/weaver-e10-recruit-stage/references/recruit-check-install.md +65 -0
  159. package/skills/weaver-e10-recruit-stage/references/recruit-detail-process.md +63 -0
  160. package/skills/weaver-e10-recruit-stage/references/recruit-edit-interview-feedback.md +142 -0
  161. package/skills/weaver-e10-recruit-stage/references/recruit-jd-create.md +73 -0
  162. package/skills/weaver-e10-recruit-stage/references/recruit-jd-template.md +56 -0
  163. package/skills/weaver-e10-recruit-stage/references/recruit-list-candidate.md +101 -0
  164. package/skills/weaver-e10-recruit-stage/references/recruit-list-interview.md +148 -0
  165. package/skills/weaver-e10-recruit-stage/references/recruit-match-candidates.md +77 -0
  166. package/skills/weaver-e10-recruit-stage/references/recruit-output-links.md +59 -0
  167. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-creator.md +69 -0
  168. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-metadata.md +241 -0
  169. package/skills/weaver-e10-recruit-stage/references/recruit-resume-import.md +438 -0
  170. package/skills/weaver-e10-recruit-stage/references/recruit-resume-parse.md +128 -0
  171. package/skills/weaver-e10-recruit-stage/references/recruit-search-candidate.md +146 -0
  172. package/skills/weaver-e10-recruit-stage/references/recruit-urge-feedback.md +69 -0
  173. package/skills/weaver-e10-shared/SKILL.md +1 -1
  174. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -2
  175. package/skills/weaver-e10-shijingran/SKILL.md +3 -2
  176. package/skills/weaver-e10-shijingran/references/operations.md +3 -1
  177. package/skills/weaver-e10-shijingran/references/source-manifest.json +41 -40
  178. package/skills/weaver-e10-shijingran/references/write-operations.md +2 -0
  179. package/skills/weaver-e10-workflow/SKILL.md +4 -2
  180. package/skills/weaver-e10-workflow/aigroup/default.json +11 -9
  181. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +23 -13
  182. package/skills/weaver-e10-workflow/tools/todo-summary.cjs +62 -20
  183. package/skills/weaver-e10-yepiaotong/SKILL.md +9 -10
  184. package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +6 -3
  185. package/dist/chunks/{chunk-E7OK5WD5.js → chunk-2LV74IWP.js} +5 -5
@@ -0,0 +1,4507 @@
1
+ import { createRequire as __esbuildCreateRequire } from 'node:module';
2
+ const require = __esbuildCreateRequire(import.meta.url);
3
+ import {
4
+ createRecruitContextLoader
5
+ } from "./chunk-LDZQPUIA.js";
6
+ import {
7
+ E10ApiError,
8
+ createE10RequestRuntime,
9
+ isE10SessionExpiredResponse,
10
+ requireE10Session
11
+ } from "./chunk-SA6VBIHY.js";
12
+ import {
13
+ E10_SESSION_EXPIRED_MESSAGE,
14
+ getActiveProfile
15
+ } from "./chunk-FOAA27U5.js";
16
+ import {
17
+ CliError,
18
+ isCliError,
19
+ toErrorEnvelope
20
+ } from "./chunk-LUSPKZKY.js";
21
+ import "./chunk-BUSC6UAE.js";
22
+
23
+ // src/shortcuts/recruit-stage/index.ts
24
+ import { readFile as readFile3 } from "node:fs/promises";
25
+
26
+ // src/shortcuts/recruit-stage/continuation.ts
27
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
28
+ import { existsSync } from "node:fs";
29
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
30
+ import { homedir } from "node:os";
31
+ import { resolve } from "node:path";
32
+
33
+ // src/shortcuts/recruit-stage/errors.ts
34
+ var RecruitStageCliError = class extends CliError {
35
+ type;
36
+ subtype;
37
+ code;
38
+ retryable;
39
+ stage;
40
+ details;
41
+ partialData;
42
+ constructor(type, subtype, message, options = {}) {
43
+ super(message, options.exitCode ?? 5 /* InternalError */);
44
+ this.name = "RecruitStageCliError";
45
+ this.type = type;
46
+ this.subtype = subtype;
47
+ this.code = options.code;
48
+ this.retryable = options.retryable ?? false;
49
+ this.stage = options.stage;
50
+ this.details = options.details;
51
+ this.partialData = options.partialData;
52
+ }
53
+ };
54
+ function rsValidationError(subtype, message, details) {
55
+ return new RecruitStageCliError("validation", subtype, message, {
56
+ details,
57
+ exitCode: 2 /* UsageError */
58
+ });
59
+ }
60
+ function rsAuthError(subtype, message, details) {
61
+ return new RecruitStageCliError("authentication", subtype, message, {
62
+ details,
63
+ exitCode: 3 /* AuthError */
64
+ });
65
+ }
66
+ function rsNetworkError(subtype, message, options = {}) {
67
+ return new RecruitStageCliError("network", subtype, message, {
68
+ ...options,
69
+ retryable: options.retryable ?? true,
70
+ exitCode: 4 /* NetworkError */
71
+ });
72
+ }
73
+ function rsConfirmationError(action, details) {
74
+ return new RecruitStageCliError("confirmation", "required", "\u8BE5\u64CD\u4F5C\u9700\u8981\u660E\u786E\u786E\u8BA4", {
75
+ details: { action, ...details },
76
+ exitCode: 10 /* ConfirmationRequired */
77
+ });
78
+ }
79
+ function rsFail(message, details) {
80
+ return new RecruitStageCliError("api", "business_error", message, {
81
+ details,
82
+ exitCode: 1 /* GeneralError */
83
+ });
84
+ }
85
+
86
+ // src/shortcuts/recruit-stage/continuation.ts
87
+ function stateRoot(ctx) {
88
+ const configured = ctx?.config?.path();
89
+ if (configured) return resolve(configured, "..");
90
+ return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), ".weaver-work-cli");
91
+ }
92
+ async function loadKey(ctx) {
93
+ const keysDir = resolve(stateRoot(ctx), "keys");
94
+ const filename = resolve(keysDir, "recruit-stage-continuation.key");
95
+ await mkdir(keysDir, { recursive: true, mode: 448 });
96
+ if (existsSync(filename)) return readFile(filename);
97
+ const key = randomBytes(32);
98
+ try {
99
+ await writeFile(filename, key, { flag: "wx", mode: 384 });
100
+ if (process.platform !== "win32") await chmod(filename, 384).catch(() => void 0);
101
+ return key;
102
+ } catch {
103
+ return readFile(filename);
104
+ }
105
+ }
106
+ function encode(value) {
107
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
108
+ }
109
+ function signature(payload, key) {
110
+ return createHmac("sha256", key).update(payload).digest("base64url");
111
+ }
112
+ function createRecruitStageContinuationSigner(ctx) {
113
+ return {
114
+ async issue(payload, ttlMs = 10 * 6e4) {
115
+ const issuedAt = Date.now();
116
+ const body = encode({ ...payload, issuedAt, expiresAt: issuedAt + ttlMs });
117
+ const sig = signature(body, await loadKey(ctx));
118
+ return `${body}.${sig}`;
119
+ },
120
+ async verify(token, expectedOperation) {
121
+ if (typeof token !== "string" || !token.includes(".")) {
122
+ throw rsValidationError("continuation_invalid", "continuation \u65E0\u6548");
123
+ }
124
+ const [body, provided] = token.split(".");
125
+ const expected = signature(body, await loadKey(ctx));
126
+ const valid = provided.length === expected.length && timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
127
+ if (!valid) throw rsValidationError("continuation_invalid", "continuation \u7B7E\u540D\u6821\u9A8C\u5931\u8D25");
128
+ let payload;
129
+ try {
130
+ payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
131
+ } catch {
132
+ throw rsValidationError("continuation_invalid", "continuation \u8F7D\u8377\u89E3\u6790\u5931\u8D25");
133
+ }
134
+ if (!payload || typeof payload !== "object") {
135
+ throw rsValidationError("continuation_invalid", "continuation \u8F7D\u8377\u5FC5\u987B\u662F\u5BF9\u8C61");
136
+ }
137
+ if (typeof payload.expiresAt === "number" && Date.now() > payload.expiresAt) {
138
+ throw rsValidationError("continuation_expired", "continuation \u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0 prepare");
139
+ }
140
+ if (payload.operation && String(payload.operation) !== String(expectedOperation)) {
141
+ throw rsValidationError(
142
+ "continuation_operation_mismatch",
143
+ `continuation \u5C5E\u4E8E\u64CD\u4F5C ${String(payload.operation)}\uFF0C\u4E0D\u80FD\u7528\u4E8E ${expectedOperation}`
144
+ );
145
+ }
146
+ return payload;
147
+ }
148
+ };
149
+ }
150
+
151
+ // src/shortcuts/recruit-stage/host.ts
152
+ function inferProfile(ctx) {
153
+ const options = ctx.getGlobalOptions();
154
+ return options.profile || getActiveProfile();
155
+ }
156
+ function assertPath(requestPath) {
157
+ if (typeof requestPath !== "string" || !requestPath.startsWith("/") || requestPath.startsWith("//")) {
158
+ throw rsValidationError("path_invalid", "\u8BF7\u6C42\u8DEF\u5F84\u5FC5\u987B\u662F\u76F8\u5BF9\u7EDD\u5BF9\u8DEF\u5F84");
159
+ }
160
+ if (/^https?:/i.test(requestPath) || /[\r\n]/.test(requestPath) || requestPath.includes("/../")) {
161
+ throw rsValidationError("path_invalid", "\u8BF7\u6C42\u8DEF\u5F84\u4E0D\u5141\u8BB8\u8DE8\u57DF\u6216\u8DEF\u5F84\u9003\u9038");
162
+ }
163
+ if (!requestPath.startsWith("/api/")) {
164
+ throw rsValidationError("path_not_allowed", "\u53EA\u80FD\u8C03\u7528 /api/ \u8DEF\u5F84");
165
+ }
166
+ }
167
+ function createRecruitStageHost(ctx, dependencies = {}) {
168
+ let e10Session = null;
169
+ let sessionContext = null;
170
+ const requireSession = dependencies.requireSession || ((innerCtx) => requireE10Session(innerCtx, { skipProfileEnrichment: true }));
171
+ async function ensureSession() {
172
+ if (!e10Session) {
173
+ e10Session = await requireSession(ctx);
174
+ sessionContext = {
175
+ baseUrl: e10Session.getBaseUrl(),
176
+ profile: inferProfile(ctx),
177
+ userId: e10Session.userId,
178
+ tenantKey: e10Session.tenantKey,
179
+ eteamsId: e10Session.eteamsId
180
+ };
181
+ }
182
+ return e10Session;
183
+ }
184
+ async function getSession() {
185
+ await ensureSession();
186
+ return sessionContext;
187
+ }
188
+ async function runtime() {
189
+ return createE10RequestRuntime(ctx, await ensureSession(), {
190
+ interceptors: dependencies.interceptors,
191
+ fetch: dependencies.fetch
192
+ });
193
+ }
194
+ async function request(request2) {
195
+ const {
196
+ method,
197
+ path: path2,
198
+ query,
199
+ body,
200
+ operation,
201
+ multipart,
202
+ headers: extraHeaders = {},
203
+ timeoutMs = 3e4
204
+ } = request2;
205
+ assertPath(path2);
206
+ const resolvedMethod = method || (body === void 0 && multipart === void 0 ? "GET" : "POST");
207
+ let response;
208
+ try {
209
+ response = await (await runtime()).requestJson({
210
+ module: "recruit-stage",
211
+ operation,
212
+ method: resolvedMethod,
213
+ path: path2,
214
+ query,
215
+ body: multipart === void 0 ? body : void 0,
216
+ formData: multipart,
217
+ headers: { "X-Requested-With": "XMLHttpRequest", ...extraHeaders },
218
+ timeoutMs
219
+ });
220
+ } catch (error) {
221
+ if (error instanceof E10ApiError && error.type === "network") {
222
+ throw rsNetworkError(error.subtype, error.message, {
223
+ retryable: error.retryable,
224
+ details: error.details
225
+ });
226
+ }
227
+ throw error;
228
+ }
229
+ if (isE10SessionExpiredResponse(response)) {
230
+ throw rsAuthError("session_expired", E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
231
+ }
232
+ if (response.parseError) {
233
+ if (response.status < 200 || response.status >= 300) {
234
+ throw new RecruitStageCliError("api", "http_error", `\u6CDB\u5FAE\u63A5\u53E3 HTTP ${response.status}`, {
235
+ code: response.status,
236
+ details: { status: response.status },
237
+ exitCode: 1
238
+ });
239
+ }
240
+ throw rsNetworkError("protocol", `\u63A5\u53E3\u8FD4\u56DE\u4E0D\u662F\u5408\u6CD5 JSON\uFF08HTTP ${response.status}\uFF09`, { retryable: false });
241
+ }
242
+ if (response.status < 200 || response.status >= 300) {
243
+ throw new RecruitStageCliError("api", "http_error", `\u6CDB\u5FAE\u63A5\u53E3 HTTP ${response.status}`, {
244
+ code: response.status,
245
+ details: { status: response.status, message: response.value?.msg },
246
+ exitCode: 1
247
+ });
248
+ }
249
+ return response.value;
250
+ }
251
+ return { request, getSession };
252
+ }
253
+
254
+ // src/shortcuts/recruit-stage/operations/jd-create.ts
255
+ import { createHash, randomBytes as randomBytes2 } from "node:crypto";
256
+
257
+ // src/shortcuts/recruit-stage/operations/types.ts
258
+ function rsSuccess(action, data, meta) {
259
+ const payload = { ok: true, action, data };
260
+ if (meta && Object.keys(meta).length > 0) payload.meta = meta;
261
+ return payload;
262
+ }
263
+ function schemaOf(properties, options = {}) {
264
+ return {
265
+ type: "object",
266
+ additionalProperties: false,
267
+ properties,
268
+ ...options.required?.length ? { required: options.required } : {},
269
+ ...options.description ? { description: options.description } : {}
270
+ };
271
+ }
272
+
273
+ // src/shortcuts/recruit-stage/shared.ts
274
+ import { randomUUID } from "node:crypto";
275
+ function randHex32() {
276
+ return randomUUID().replace(/-/g, "");
277
+ }
278
+ function objIdOf(cache, key) {
279
+ const fo = cache.forms[key];
280
+ if (!fo?.objId) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u8868\u5355 " + key + " \u7684 objId\uFF08\u8BE5\u8868\u5355\u53EF\u80FD\u672A\u542F\u7528\uFF09");
281
+ return String(fo.objId);
282
+ }
283
+ function formIdOf(cache, key) {
284
+ const fo = cache.forms[key];
285
+ if (!fo?.formId) return objIdOf(cache, key);
286
+ return String(fo.formId);
287
+ }
288
+ var __layoutMemo = /* @__PURE__ */ new Map();
289
+ function fetchLayoutData(host, formId) {
290
+ if (!__layoutMemo.has(formId)) {
291
+ const p = fetchLayoutDataRaw(host, formId).catch((e) => {
292
+ __layoutMemo.delete(formId);
293
+ throw e;
294
+ });
295
+ __layoutMemo.set(formId, p);
296
+ }
297
+ return __layoutMemo.get(formId);
298
+ }
299
+ async function fetchLayoutDataRaw(host, formId) {
300
+ try {
301
+ const r = await host.request({ path: "/api/ebuilder/form/form/core/getFormLayout?formId=" + formId, method: "GET" });
302
+ if (r?.data) return r.data;
303
+ } catch {
304
+ }
305
+ try {
306
+ const r = await host.request({
307
+ path: "/api/ebuilder/form/form/core/getFormLayout",
308
+ method: "POST",
309
+ body: { module: "ebuilderform", formId, terminalType: "PC" }
310
+ });
311
+ if (r?.data) return r.data;
312
+ } catch {
313
+ }
314
+ return null;
315
+ }
316
+ async function getFormLayout(host, formId) {
317
+ const d = await fetchLayoutData(host, formId);
318
+ return d?.formLayout?.id || d?.id || formId;
319
+ }
320
+ async function getFormLayoutMeta(host, formId) {
321
+ return fetchLayoutData(host, formId);
322
+ }
323
+ async function getFormButtons(host, objId, layoutId) {
324
+ const body = {
325
+ layoutType: "formLayout",
326
+ objId,
327
+ dataId: "",
328
+ formDataId: "",
329
+ type: "1",
330
+ fromRecycle: false,
331
+ otherOperator: "",
332
+ objectAppId: "",
333
+ browser: "Chrome",
334
+ isTamper: "0"
335
+ };
336
+ if (layoutId) body.layoutId = layoutId;
337
+ const r = await host.request({ path: "/api/ebuilder/form/data/getFormButtons", body });
338
+ const btns = r?.data?.buttons || r?.data || [];
339
+ const actions = [];
340
+ for (const b of btns) {
341
+ for (const a of b?.actions || []) {
342
+ actions.push({ ...a, _btn: b.name, _buttonKey: b.buttonKey, _buttonId: b.id, _buttonOrder: b.showOrder });
343
+ }
344
+ }
345
+ return actions;
346
+ }
347
+ function pickSaveButtonActions(actions, { includeDisabled = false } = {}) {
348
+ const all = actions || [];
349
+ let own = all.filter((a) => a._buttonKey === "save");
350
+ if (!own.length) {
351
+ const names = new Set(all.filter((a) => a.actionType === "system" && a._btn).map((a) => a._btn));
352
+ own = all.filter((a) => a._btn && names.has(a._btn));
353
+ }
354
+ const sorted = [...own].sort((x, y) => (x.showOrder ?? 0) - (y.showOrder ?? 0));
355
+ if (includeDisabled) return sorted;
356
+ return sorted.filter((a) => a.enable === void 0 || a.enable === null || Number(a.enable) !== 0);
357
+ }
358
+ async function saveFormData(host, opts) {
359
+ const { appId, formId, objId, name, dataDetails, rightType = "0", formDataId, updateTimeStamp, layoutId, extraCustomParam = {} } = opts;
360
+ const finalLayout = layoutId || await getFormLayout(host, formId);
361
+ const customParam = {
362
+ objId,
363
+ name: name || "",
364
+ firstDataName: name || "",
365
+ isFlowEnd: false,
366
+ otherOperator: "",
367
+ dataStatus: "submit",
368
+ rightType,
369
+ appId,
370
+ ...extraCustomParam
371
+ };
372
+ const formData = {
373
+ module: "ebuilderform",
374
+ client: "pc",
375
+ dataStatus: "submit",
376
+ form: { id: formId },
377
+ formLayout: { id: finalLayout }
378
+ };
379
+ if (formDataId) formData.id = formDataId;
380
+ if (updateTimeStamp) formData.updateTimeStamp = updateTimeStamp;
381
+ if (dataDetails) formData.dataDetails = dataDetails;
382
+ const r = await host.request({
383
+ path: "/api/ebuilder/form/form/core/saveFormData?formId=" + formId,
384
+ body: { module: "ebuilderform", appId, customParam, formData }
385
+ });
386
+ if (!r?.status && r?.code !== 200) {
387
+ throw rsFail("saveFormData \u5931\u8D25: " + (r?.msg || JSON.stringify(r).slice(0, 200)));
388
+ }
389
+ return String(r?.data?.id);
390
+ }
391
+ async function getCandidateByPhone(host, phone) {
392
+ const r = await host.request({ path: "/api/recruit/candidate/getCandidateByPhone", method: "POST", body: { phone: String(phone) } });
393
+ const d = r?.data || {};
394
+ return {
395
+ talentId: d.talentId ? String(d.talentId) : "",
396
+ candidateId: d.candidateId ? String(d.candidateId) : "",
397
+ batchNo: d.batchNo ? String(d.batchNo) : ""
398
+ };
399
+ }
400
+ function buildTalentViewUrl(baseUrl, candidateId) {
401
+ return `${baseUrl}/sp/recruit/talentView?batchId=${candidateId}&recruitType=useSelf`;
402
+ }
403
+ function talentLinkExtra(cache) {
404
+ return String(cache && cache.selfUse) === "1" ? "&recruitType=useSelf" : "";
405
+ }
406
+ async function checkUnique(host, opts) {
407
+ const { objId, fieldId, value, dataId } = opts;
408
+ const dataDetail = { content: String(value), formField: { id: String(fieldId) } };
409
+ const customParam = { objId: String(objId) };
410
+ if (dataId) {
411
+ dataDetail.formData = { id: String(dataId) };
412
+ customParam.dataId = String(dataId);
413
+ }
414
+ const r = await host.request({
415
+ path: "/api/ebuilder/form/form/core/checkUnique",
416
+ method: "POST",
417
+ body: { dataDetail, module: "ebuilderform", customParam }
418
+ });
419
+ const d = r?.data || {};
420
+ let ids = [];
421
+ const rawList = d.formDataIds;
422
+ if (Array.isArray(rawList)) ids = rawList.map((x) => String(x)).filter((x) => /^\d{10,}$/.test(x));
423
+ if (!ids.length) {
424
+ const text = typeof r?.__raw === "string" ? r.__raw : "";
425
+ const m = text.match(/"formDataIds"\s*:\s*\[([^\]]*)\]/);
426
+ if (m) ids = m[1].match(/\d{10,}/g) || [];
427
+ }
428
+ return { unique: d.unique !== false, formDataIds: [...new Set(ids)] };
429
+ }
430
+ async function getTableMapping(host, objId) {
431
+ const r = await host.request({ path: "/api/ebuilder/form/dataset/v1/getTableMapping?objId=" + encodeURIComponent(objId), method: "GET" });
432
+ const d = r?.data;
433
+ if (!d || !d.mainTable) throw rsFail("getTableMapping \u672A\u8FD4\u56DE\u6620\u5C04\uFF08objId=" + objId + "\uFF09\uFF1A" + JSON.stringify(r).slice(0, 200));
434
+ return d;
435
+ }
436
+ function invertTableMapping(mapping) {
437
+ const out = {};
438
+ for (const [node, tableId] of Object.entries(mapping || {})) {
439
+ if (node === "mainTable") continue;
440
+ out[String(tableId)] = node;
441
+ }
442
+ return out;
443
+ }
444
+ async function datasetSaveFormData(host, opts) {
445
+ const { objId, mainTable, details, doAction = false, fieldNoFindIgnore = true } = opts;
446
+ const record = { mainTable };
447
+ for (const [node, rows] of Object.entries(details || {})) {
448
+ if (Array.isArray(rows) && rows.length) record[node] = rows;
449
+ }
450
+ const r = await host.request({
451
+ path: "/api/ebuilder/form/dataset/v2/saveFormData",
452
+ method: "POST",
453
+ body: {
454
+ datajson: {
455
+ datas: [record],
456
+ header: { objId },
457
+ operationinfo: {
458
+ fieldNoFindIgnore: fieldNoFindIgnore ? "true" : "false",
459
+ doAction: doAction ? "true" : "false"
460
+ }
461
+ }
462
+ }
463
+ });
464
+ const dj = r?.data?.datajson || {};
465
+ const code = r?.message?.code;
466
+ const ok = (code === void 0 || String(code) === "0") && dj.status !== false;
467
+ if (!ok) {
468
+ throw rsFail("dataset saveFormData \u5931\u8D25\uFF1A" + (dj.message || r?.message?.msg || JSON.stringify(r).slice(0, 300)));
469
+ }
470
+ const ids = Array.isArray(dj.dataIds) ? dj.dataIds.map(String) : [];
471
+ if (!ids.length) throw rsFail("dataset saveFormData \u672A\u8FD4\u56DE dataIds\uFF1A" + JSON.stringify(r).slice(0, 300));
472
+ return { dataId: ids[0], dataIds: ids };
473
+ }
474
+ async function datasetUpdateFormData(host, opts) {
475
+ const { objId, dataId, mainTable, details, coverDetails = [] } = opts;
476
+ const record = { mainTable: { id: String(dataId), ...mainTable || {} } };
477
+ const operateType = {};
478
+ for (const [node, rows] of Object.entries(details || {})) {
479
+ if (!Array.isArray(rows)) continue;
480
+ record[node] = rows;
481
+ if (coverDetails.includes(node)) operateType[node] = "cover";
482
+ }
483
+ const operationinfo = { updateType: "ids", fieldNoFindIgnore: "true", needAdd: "false", doAction: "false" };
484
+ if (Object.keys(operateType).length) operationinfo.operateType = operateType;
485
+ const r = await host.request({
486
+ path: "/api/ebuilder/form/dataset/v2/updateFormData",
487
+ method: "POST",
488
+ body: { datajson: { datas: [record], header: { objId }, operationinfo } }
489
+ });
490
+ const dj = r?.data?.datajson || {};
491
+ const ok = (r?.message?.code === void 0 || String(r?.message?.code) === "0") && dj.status !== false;
492
+ if (!ok) throw rsFail("dataset updateFormData \u5931\u8D25\uFF1A" + (dj.message || r?.message?.msg || JSON.stringify(r).slice(0, 300)));
493
+ return { dataIds: (dj.dataIds || []).map(String) };
494
+ }
495
+ async function runEsb(host, opts) {
496
+ const r = await host.request({
497
+ path: "/api/ebuilder/form/esb/runEsb?objId=" + opts.objId,
498
+ body: { dataIds: opts.dataIds, esbActionIds: opts.esbActionIds, objId: opts.objId }
499
+ });
500
+ return r?.data || {};
501
+ }
502
+ async function runSaveButtonEsbActions(host, opts) {
503
+ const { objId, formId, dataId } = opts;
504
+ const layoutId = await getFormLayout(host, formId);
505
+ const acts = pickSaveButtonActions(await getFormButtons(host, objId, layoutId));
506
+ const done = [];
507
+ for (const a of acts) {
508
+ if (a.actionType !== "esb") continue;
509
+ const r = await runEsb(host, { objId, dataIds: dataId, esbActionIds: a.id });
510
+ const ok = r?.status !== false;
511
+ done.push({ order: a.showOrder, name: a.name, ok, msg: r?.successMsg || r?.failMsg || "" });
512
+ if (!ok) throw rsFail("\u300C\u4FDD\u5B58\u300D\u540E\u7EED\u52A8\u4F5C\u300C" + (a.name || a.id) + "\u300D\u6267\u884C\u5931\u8D25\uFF1A" + (r?.failMsg || "\u672A\u77E5\u539F\u56E0"));
513
+ }
514
+ return done;
515
+ }
516
+ async function resolveCurrentUser(host) {
517
+ try {
518
+ const r = await host.request({ path: "/api/baseserver/layout/teamsCheck", method: "POST", body: {} });
519
+ const cu = r && (r.currentUser || r.data && r.data.currentUser) || null;
520
+ return cu && cu.id ? { id: String(cu.id), name: cu.username || String(cu.id) } : null;
521
+ } catch {
522
+ return null;
523
+ }
524
+ }
525
+ async function resolvePersonId(host, fieldId, keyword) {
526
+ const r = await host.request({
527
+ path: "/api/ebuilder/form/common/browser/complete/resource",
528
+ body: { formParam: { fieldId, searchValue: keyword }, "formParam.fieldId": fieldId, "formParam.searchValue": keyword }
529
+ });
530
+ const list = r?.data?.data || r?.data || [];
531
+ return list[0]?.id || list[0]?.value || null;
532
+ }
533
+ async function getDsOptions(host, objId, fieldId, groupId) {
534
+ const r = await host.request({
535
+ path: "/api/ebuilder/common/ds/options?fieldName=" + fieldId + "&objId=" + objId + "&sourceType=FORM&groupId=" + groupId,
536
+ method: "GET"
537
+ });
538
+ const list = r?.data || [];
539
+ const map = {};
540
+ for (const x of list) map[x.name] = x.id;
541
+ return map;
542
+ }
543
+ function cachedDsMap(cache, objId, fieldKey) {
544
+ const fidv = (cache.fields[objId] || {})[fieldKey];
545
+ const opts = fidv && cache.field_options?.[objId] && cache.field_options[objId][fidv];
546
+ if (!opts || !Object.keys(opts).length) return null;
547
+ const map = {};
548
+ for (const [id, name] of Object.entries(opts)) map[name] = id;
549
+ return map;
550
+ }
551
+ async function getFormButtonsForData(host, objId, formId, dataId, layoutId) {
552
+ const body = {
553
+ layoutType: "formLayout",
554
+ objId,
555
+ dataId,
556
+ formDataId: dataId,
557
+ type: "2",
558
+ fromRecycle: false,
559
+ otherOperator: "",
560
+ objectAppId: "",
561
+ browser: "Chrome",
562
+ isTamper: "0"
563
+ };
564
+ if (layoutId) body.layoutId = layoutId;
565
+ const r = await host.request({ path: "/api/ebuilder/form/data/getFormButtons", body });
566
+ const data = r?.data;
567
+ const btns = Array.isArray(data) ? data : data?.buttons || [];
568
+ return btns;
569
+ }
570
+ function findSubmitButton(btns) {
571
+ for (const b of btns || []) {
572
+ const acts = b?.actions || [];
573
+ if (acts.some((a) => a.actionType === "system") || b?.buttonKey === "editSave") return b;
574
+ }
575
+ return null;
576
+ }
577
+ function extractJsRequests(ecode) {
578
+ if (!ecode) return [];
579
+ const calls = [];
580
+ const re = /(?:weappUtils\.request|\$\.ajax|axios\.\w+|fetch)\s*\(\s*\{[\s\S]*?\}/g;
581
+ let m;
582
+ while (m = re.exec(ecode)) {
583
+ const block = m[0];
584
+ const url = (block.match(/url\s*:\s*["']([^"']+)["']/) || [])[1];
585
+ if (!url) continue;
586
+ const method = (block.match(/method\s*:\s*["']([^"']+)["']/) || [])[1] || "post";
587
+ const data = {};
588
+ const dm = block.match(/data\s*:\s*\{([\s\S]*?)\}/);
589
+ if (dm) {
590
+ for (const kv of dm[1].split(",")) {
591
+ const kk = kv.match(/["']?([A-Za-z_][A-Za-z0-9_]*)["']?\s*:/);
592
+ if (kk) data[kk[1]] = kv.split(":").slice(1).join(":").trim().slice(0, 60);
593
+ }
594
+ }
595
+ calls.push({ url, method, data });
596
+ }
597
+ return calls;
598
+ }
599
+ function analyzeButtonActions(btn) {
600
+ const acts = btn?.actions || [];
601
+ const esbIds = acts.filter((a) => a.actionType === "esb").map((a) => String(a.id));
602
+ const jsCalls = [];
603
+ for (const a of acts) {
604
+ if (a.actionType === "jsCode" && a.ecode) jsCalls.push(...extractJsRequests(a.ecode));
605
+ }
606
+ return {
607
+ system: acts.some((a) => a.actionType === "system"),
608
+ esbIds,
609
+ jsCalls,
610
+ names: acts.map((a) => a.actionType === "jsCode" ? `jsCode(${a.name})` : `${a.actionType}(${a.name})`)
611
+ };
612
+ }
613
+ async function getFormDataRecord(host, formId, dataId, objId, layoutId) {
614
+ const r = await host.request({
615
+ path: "/api/ebuilder/form/form/core/getFormDataAndLayout?formId=" + formId + "&dataId=" + dataId,
616
+ method: "POST",
617
+ body: {
618
+ terminalType: "PC",
619
+ dataId,
620
+ formId,
621
+ module: "ebuilderform",
622
+ newLayout: true,
623
+ cacheUid: "",
624
+ layoutMultiId: "",
625
+ dataCrossLayout: false,
626
+ permissionId: "",
627
+ allReadOnly: false,
628
+ initialDatas: [],
629
+ dynamicData: false,
630
+ cost: false,
631
+ ...layoutId ? { layoutId } : {},
632
+ customParam: { objId }
633
+ }
634
+ });
635
+ const fd = r?.data?.formData || {};
636
+ const fl = r?.data?.formLayout || {};
637
+ return { id: fd.id, updateTimeStamp: fd.updateTimeStamp, dataDetails: fd.dataDetails || [], formLayoutId: fl.id };
638
+ }
639
+ function toSubmitDetails(details) {
640
+ const out = [];
641
+ for (const x of details || []) {
642
+ const fid = x.fieldId || x.formField?.id;
643
+ if (!fid) continue;
644
+ const o = { formField: { id: fid } };
645
+ if (x.dataOptions) o.dataOptions = x.dataOptions;
646
+ if (x.dataText) o.dataText = x.dataText;
647
+ if (x.value !== void 0) o.dataText = { content: String(x.value) };
648
+ if (!o.dataOptions && !o.dataText) continue;
649
+ out.push(o);
650
+ }
651
+ return out;
652
+ }
653
+ function findMenuNode(menus, aliases, filter) {
654
+ const list = Array.isArray(menus) ? menus : [];
655
+ const nameOf = (m) => String(m && m.menuName || "").trim();
656
+ const ok = (m) => typeof filter === "function" ? !!filter(m) : true;
657
+ for (const a of aliases || []) {
658
+ const hit = list.find((m) => nameOf(m) === a && ok(m));
659
+ if (hit) return hit;
660
+ }
661
+ for (const a of aliases || []) {
662
+ const hit = list.find((m) => nameOf(m).includes(a) && ok(m));
663
+ if (hit) return hit;
664
+ }
665
+ return null;
666
+ }
667
+ function computeRequiredHint(opts) {
668
+ const { meta, fields, typeName, typeOptionId, typeOptions, dataDetails } = opts;
669
+ const rev = {};
670
+ for (const [dk, fid] of Object.entries(fields || {})) rev[String(fid)] = dk;
671
+ const nm = (fid) => rev[String(fid)] || String(fid);
672
+ const curName = (id) => (typeOptions || {})[String(id)] || "";
673
+ const norm = (s) => String(s || "").replace(/[((][^))]*[))]/g, "").trim();
674
+ const matchTypeCondition = (cd) => {
675
+ if (!cd) return false;
676
+ let optId = "";
677
+ let optText = "";
678
+ try {
679
+ const sj = JSON.parse(cd.sourceContent || "{}");
680
+ const o = (sj.dataOptions || [])[0];
681
+ if (o) {
682
+ optId = String(o.optionId || "");
683
+ optText = String(o.content || "");
684
+ }
685
+ if (!optText) optText = String(sj.content || "");
686
+ } catch {
687
+ }
688
+ const actual = curName(optId);
689
+ let eq;
690
+ if (actual) {
691
+ eq = actual === typeName;
692
+ } else {
693
+ eq = !/[((]/.test(String(typeName || "")) && norm(optText) === norm(typeName);
694
+ }
695
+ if (cd.detailSymbol === "UN_EQUAL") return !eq;
696
+ if (cd.detailSymbol === "IN") {
697
+ const names = optText.split(",").map((s) => norm(s));
698
+ return names.includes(norm(typeName)) || Boolean(typeOptionId && optId === String(typeOptionId));
699
+ }
700
+ return eq;
701
+ };
702
+ const required = /* @__PURE__ */ new Set();
703
+ for (const x of meta?.viewRelateConfig || []) {
704
+ if (x.required) required.add(nm(x.fieldId));
705
+ }
706
+ const typeFid = String((fields || {}).interview_type || "");
707
+ for (const lk of meta?.viewLinkages || []) {
708
+ const details = (lk.condition || {}).conditionDetails || [];
709
+ if (!details.length) continue;
710
+ let hit = true;
711
+ for (const cd of details) {
712
+ if (!typeFid || String(cd.targetId) !== typeFid) {
713
+ hit = false;
714
+ break;
715
+ }
716
+ if (!matchTypeCondition(cd)) {
717
+ hit = false;
718
+ break;
719
+ }
720
+ }
721
+ if (!hit) continue;
722
+ for (const ld of lk.linkageDetails || []) {
723
+ if (ld.linkageProp === "required") {
724
+ for (const v of String(ld.linkageValue || "").split(",")) if (v.trim()) required.add(nm(v.trim()));
725
+ }
726
+ }
727
+ }
728
+ const filled = new Set((dataDetails || []).map((d) => nm((d.formField || {}).id)));
729
+ const defaults = new Set((meta?.defaultDetails || []).map((x) => nm(x.fieldId)));
730
+ const defaultValues = {};
731
+ for (const x of meta?.defaultDetails || []) {
732
+ const dk = nm(x.fieldId);
733
+ const o = (x.dataOptions || [])[0];
734
+ defaultValues[dk] = o ? { optionId: String(o.optionId || ""), content: o.content || "" } : { optionId: "", content: x.content || "" };
735
+ }
736
+ let missing = [...required].filter((k) => !filled.has(k) && !defaults.has(k));
737
+ const DIGITAL_EXCLUDE = ["interview_paper_id", "send_channel"];
738
+ const isDigitalHuman = String(typeName || "").trim() === "AI\u9762\u8BD5";
739
+ let excludedByBusiness = [];
740
+ if (isDigitalHuman) {
741
+ excludedByBusiness = missing.filter((k) => DIGITAL_EXCLUDE.includes(k));
742
+ missing = missing.filter((k) => !DIGITAL_EXCLUDE.includes(k));
743
+ }
744
+ return { available: true, required: [...required], missing, hasDefault: [...defaults], defaultValues, excludedByBusiness };
745
+ }
746
+ function buildCardUrl(baseUrl, objId, dataId) {
747
+ if (!baseUrl || !objId || !dataId) return null;
748
+ return String(baseUrl).replace(/\/+$/, "") + "/sp/ebdfpage/card/0/" + objId + "/" + dataId;
749
+ }
750
+ function buildListLocateUrl(baseUrl, appId, listId, objId, dataId) {
751
+ if (!baseUrl || !listId || !objId || !dataId) return null;
752
+ const base = String(baseUrl).replace(/\/+$/, "");
753
+ return `${base}/sp/ebdfpage/list/${listId}/card/0/${objId}/${dataId}/${listId}?id=${dataId}&openSource=field&openSourceId=${dataId}&openSourceAppId=${appId}`;
754
+ }
755
+
756
+ // src/shortcuts/recruit-stage/operations/jd-create.ts
757
+ var SCHEMA_ID_STR = { type: "string", description: "18 \u4F4D\u8BB0\u5F55 id\uFF08\u5B57\u7B26\u4E32\uFF09" };
758
+ var DATAKEYS_TEXT = ["position_name"];
759
+ var DATAKEYS_MTEXT = ["position_duty", "work_request"];
760
+ var DEF_TEXT = { recruit_count: "1", number_of_pay_months: "12" };
761
+ var DEF_OPTION = { salary_type: "\u6708\u85AA", recruit_type: "\u793E\u4F1A\u62DB\u8058" };
762
+ var DEF_CURRENT_USER = ["recr_principal", "employ_manager"];
763
+ var DEF_FROM_REF = [
764
+ "position_nature",
765
+ "job_category",
766
+ "education",
767
+ "work_years",
768
+ "work_location",
769
+ "salary_min",
770
+ "salary_max"
771
+ ];
772
+ var DEF_STATUS = "\u62DB\u8058\u4E2D";
773
+ var NUM_KEYS = /* @__PURE__ */ new Set(["recruit_count", "number_of_pay_months", "salary_min", "salary_max", "internship_duration", "working_days"]);
774
+ var PERSON_KEYS = /* @__PURE__ */ new Set(["recr_principal", "recr_helper", "employ_manager", "interviewer"]);
775
+ var OPTION_KEYS = /* @__PURE__ */ new Set(["salary_type", "recruit_type", "status"]);
776
+ var DICT_KEYS = /* @__PURE__ */ new Set(["position_nature", "job_category", "education", "work_years"]);
777
+ var MTEXT_KEYS = /* @__PURE__ */ new Set(["position_duty", "work_request"]);
778
+ function fingerprint(value) {
779
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex");
780
+ }
781
+ function detailValueId(d) {
782
+ if (!d) return "";
783
+ if (d.dataOptions && d.dataOptions[0]) return String(d.dataOptions[0].optionId || "");
784
+ if (d.dataText) return String(d.dataText.content || "");
785
+ if (d.content !== void 0) return String(d.content);
786
+ return "";
787
+ }
788
+ function dictGroupOf(cache, valueId) {
789
+ const row = (cache.dict?.raw || []).find((x) => String(x.option_id) === String(valueId));
790
+ return row ? String(row.dict_type) : "";
791
+ }
792
+ function dictIdByName(cache, name, group) {
793
+ const rows = (cache.dict?.raw || []).filter((x) => x.selection_name === name && (!group || String(x.dict_type) === group));
794
+ return rows.length ? String(rows[0].option_id) : "";
795
+ }
796
+ async function fetchRefByFid(ctx) {
797
+ const { host, cache } = ctx;
798
+ const objId = objIdOf(cache, "position_manage");
799
+ const formId = formIdOf(cache, "position_manage");
800
+ const posMenu = cache.lists?.position || null;
801
+ if (!posMenu) return {};
802
+ const body = {
803
+ current: 1,
804
+ pageSize: 20,
805
+ tableuid: randHex32(),
806
+ listId: posMenu.listId,
807
+ searchParamData: { searchType: "3", type: "and" },
808
+ terminal: "PC",
809
+ browser: "Chrome",
810
+ requiredMatch: 1,
811
+ mapResult: 1,
812
+ recheckQuery: 1,
813
+ isFollow: 0,
814
+ isShareToMe: 0,
815
+ filterCondition: {},
816
+ cusMenuId: posMenu.cusMenuId,
817
+ urlPageTitle: "\u62DB\u8058\u804C\u4F4D",
818
+ menuName: "\u62DB\u8058\u804C\u4F4D"
819
+ };
820
+ const r = await host.request({
821
+ path: `/api/ebuilder/form/list/getListDatas?passid=${objId}&cid=${objId}`,
822
+ body,
823
+ headers: { ebBusinessId: posMenu.listId }
824
+ });
825
+ const rows = r?.data?.data || [];
826
+ const ref = rows.find((x) => x.form_data_id && x.position_nature);
827
+ if (!ref) return {};
828
+ const rec = await getFormDataRecord(host, formId, String(ref.form_data_id), objId);
829
+ const byFid = {};
830
+ for (const d of rec.dataDetails || []) {
831
+ const f = String(d.fieldId || d.formField?.id || "");
832
+ if (f) byFid[f] = d;
833
+ }
834
+ return byFid;
835
+ }
836
+ function refToSubmit(d) {
837
+ const o = { formField: { id: String(d.fieldId || d.formField?.id) } };
838
+ if (d.dataOptions) o.dataOptions = d.dataOptions;
839
+ else if (d.dataText) o.dataText = d.dataText;
840
+ else if (d.content !== void 0 && d.content !== "") o.content = d.content;
841
+ else if (d.value !== void 0 && d.value !== "") o.dataText = { content: String(d.value) };
842
+ else return null;
843
+ return o;
844
+ }
845
+ async function resolveWorkLocation(ctx, value) {
846
+ const { host, appId, cache } = ctx;
847
+ const t = await host.request({ path: `/api/ebuilder/form/obj/getObjsByTag?appId=${appId}&tag=uf_rcrt_work_place`, method: "GET" });
848
+ const d = t?.data;
849
+ const pObj = Array.isArray(d) && d[0] ? String(d[0].id || "") : "";
850
+ const menu = findMenuNode(cache.menus || [], ["\u529E\u516C\u5730\u70B9", "\u5DE5\u4F5C\u5730\u70B9"], (m) => m.listId && m.cusMenuId);
851
+ if (!pObj || !menu) return null;
852
+ const body = {
853
+ current: 1,
854
+ pageSize: 200,
855
+ tableuid: randHex32(),
856
+ listId: menu.listId,
857
+ searchParamData: { searchType: "3", type: "and" },
858
+ terminal: "PC",
859
+ browser: "Chrome",
860
+ requiredMatch: 1,
861
+ mapResult: 1,
862
+ recheckQuery: 1,
863
+ isFollow: 0,
864
+ isShareToMe: 0,
865
+ filterCondition: {},
866
+ cusMenuId: menu.cusMenuId,
867
+ urlPageTitle: menu.menuName,
868
+ menuName: menu.menuName
869
+ };
870
+ const r = await host.request({ path: `/api/ebuilder/form/list/getListDatas?passid=${pObj}&cid=${pObj}`, body, headers: { ebBusinessId: menu.listId } });
871
+ const rows = r?.data?.data || [];
872
+ const v = String(value).trim();
873
+ const hit = rows.find((x) => {
874
+ const name = String(x.name || ""), detail = String(x.detail_location || ""), area = String(x.area_text || "");
875
+ return name.includes(v) || detail.includes(v) || area.includes(v) || detail && v.includes(detail);
876
+ });
877
+ return hit ? { id: String(hit.form_data_id || hit.id), name: String(hit.detail_location || hit.name || v) } : null;
878
+ }
879
+ async function resolveSetValue(ctx, key, value, refByFid) {
880
+ const { host, appId, cache } = ctx;
881
+ const objId = objIdOf(cache, "position_manage");
882
+ const id = (cache.fields[objId] || {})[key];
883
+ if (!id) return null;
884
+ if (NUM_KEYS.has(key)) return { formField: { id }, content: String(value) };
885
+ if (MTEXT_KEYS.has(key)) return { formField: { id }, dataText: { content: String(value) } };
886
+ if (key === "position_name") return { formField: { id }, content: String(value) };
887
+ if (PERSON_KEYS.has(key)) {
888
+ const uid = await resolvePersonId(host, id, value);
889
+ return uid ? { formField: { id }, dataOptions: [{ optionId: String(uid), content: value }] } : null;
890
+ }
891
+ if (OPTION_KEYS.has(key)) {
892
+ const m = cachedDsMap(cache, objId, key) || await getDsOptions(host, objId, id, appId);
893
+ const oid = m[value];
894
+ return oid ? { formField: { id }, dataOptions: [{ optionId: String(oid), content: value }] } : null;
895
+ }
896
+ if (DICT_KEYS.has(key)) {
897
+ const group = dictGroupOf(cache, detailValueId(refByFid[String(id)]));
898
+ const did = dictIdByName(cache, value, group) || dictIdByName(cache, value, "");
899
+ return did ? { formField: { id }, dataOptions: [{ optionId: String(did), content: value }] } : null;
900
+ }
901
+ if (key === "work_location") {
902
+ const loc = await resolveWorkLocation(ctx, value);
903
+ return loc ? { formField: { id }, dataOptions: [{ optionId: loc.id, content: loc.name }] } : null;
904
+ }
905
+ return { formField: { id }, content: String(value) };
906
+ }
907
+ function toPrefillFields(dataDetails, fields) {
908
+ const inv = {};
909
+ for (const [k, v] of Object.entries(fields)) inv[String(v)] = k;
910
+ const personFids = new Set(Object.keys(fields).filter((k) => PERSON_KEYS.has(k)).map((k) => String(fields[k])));
911
+ const out = [];
912
+ for (const d of dataDetails) {
913
+ const fid = String(d.formField.id);
914
+ let info = null;
915
+ if (d.dataOptions) info = { specialObj: d.dataOptions.map((o) => ({ id: String(o.optionId), name: o.content, ...personFids.has(fid) ? { type: "user" } : {} })) };
916
+ else if (d.dataText) info = { value: String(d.dataText.content) };
917
+ else if (d.content !== void 0) info = { value: String(d.content) };
918
+ if (info) out.push({ mark: "field" + fid, info, label: inv[fid] || fid });
919
+ }
920
+ return out;
921
+ }
922
+ async function resolvePositionNewUrl(ctx) {
923
+ const { host, appId, baseUrl, cache } = ctx;
924
+ const cached = cache.menus?.find?.((m) => m.combinationId && m.id && m.pageId && ["\u62DB\u8058\u804C\u4F4D", "\u804C\u4F4D\u5217\u8868", "\u804C\u4F4D\u7BA1\u7406"].some((n) => (m.menuName || "").includes(n)));
925
+ let node = cached || null;
926
+ if (!node) {
927
+ const r = await host.request({ path: `/api/cusapp/app/allMenus?isFront=1&appId=${appId}&terminalType=PC`, method: "GET" });
928
+ const flat = [];
929
+ (function walk(ns) {
930
+ for (const n of ns || []) if (n && typeof n === "object") {
931
+ flat.push(n);
932
+ walk(n.children || n.childs);
933
+ }
934
+ })(r?.data?.cusAppMenus || r?.data);
935
+ node = findMenuNode(flat, ["\u62DB\u8058\u804C\u4F4D", "\u804C\u4F4D\u5217\u8868", "\u804C\u4F4D\u7BA1\u7406"], (n) => n.combinationId && n.id && n.pageId);
936
+ }
937
+ if (!node) return null;
938
+ return `${baseUrl}/ebdapp/view/${appId}/combination/${node.combinationId}-${node.id}/search/${node.pageId}?terminal=PC&cusMenuId=${node.id}`;
939
+ }
940
+ async function resolveHomePageUrl(ctx) {
941
+ const { host, appId, baseUrl } = ctx;
942
+ const r = await host.request({ path: `/api/cusapp/app/allMenus?isFront=1&appId=${appId}&terminalType=PC`, method: "GET" });
943
+ const flat = [];
944
+ (function walk(ns) {
945
+ for (const n of ns || []) if (n && typeof n === "object") {
946
+ flat.push(n);
947
+ walk(n.children || n.childs);
948
+ }
949
+ })(r?.data?.cusAppMenus || r?.data);
950
+ const node = findMenuNode(flat, ["\u62DB\u8058\u9996\u9875", "\u62DB\u8058\u4E3B\u9875"], (n) => n.pageType === "PAGE" && n.pageId && n.id) || flat.find((n) => n.pageType === "PAGE" && String(n.menuName || "").includes("\u9996\u9875"));
951
+ if (!node || !node.pageId || !node.id) return null;
952
+ return `${baseUrl}/ebdapp/view/${appId}/PAGE/${node.pageId}-${node.id}?terminal=PC&cusMenuId=${node.id}&appId_${node.pageId}=${appId}`;
953
+ }
954
+ var REF_DETAIL_KEYS = [
955
+ "salary_type",
956
+ "salary_min",
957
+ "salary_max",
958
+ "number_of_pay_months",
959
+ "education",
960
+ "work_years",
961
+ "recruit_count",
962
+ "recruit_type",
963
+ "position_nature",
964
+ "work_location",
965
+ "department",
966
+ "sub_company"
967
+ ];
968
+ var REF_TEXT_KEYS = ["position_duty", "work_request"];
969
+ async function fetchRecentReferences(ctx, jd, keyword) {
970
+ const { host, cache } = ctx;
971
+ const listParam = cache.lists?.position || null;
972
+ if (!listParam?.listId) return { available: false, reason: "\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u804C\u4F4D\u5217\u8868\u53C2\u6570", items: [] };
973
+ const r = await host.request({
974
+ path: "/api/ebuilder/form/list/getListDatas?passid=" + jd.objId + "&cid=" + jd.objId,
975
+ body: {
976
+ current: 1,
977
+ pageSize: 30,
978
+ tableuid: randHex32(),
979
+ listId: listParam.listId,
980
+ searchParamData: { searchType: "3", type: "and" },
981
+ terminal: "PC",
982
+ browser: "Chrome",
983
+ requiredMatch: 1,
984
+ mapResult: 1,
985
+ recheckQuery: 1,
986
+ isFollow: 0,
987
+ isShareToMe: 0,
988
+ filterCondition: {},
989
+ cusMenuId: listParam.cusMenuId,
990
+ urlPageTitle: listParam.menuName,
991
+ menuName: listParam.menuName
992
+ },
993
+ headers: { ebBusinessId: listParam.listId }
994
+ });
995
+ const rows = r?.data?.displayData || r?.data?.data || [];
996
+ const kw = String(keyword || "").trim();
997
+ const tokens = [...new Set(kw.split(/[^A-Za-z0-9\u4e00-\u9fff]+/).concat(kw).map((t) => t.trim()).filter((t) => t.length >= 2))];
998
+ const match = (row) => {
999
+ if (!tokens.length) return true;
1000
+ const name = String(row.position_name || row.name || "");
1001
+ const hay = name + " " + Object.values(row.displayData || {}).map(String).join(" ");
1002
+ return tokens.some((t) => hay.toUpperCase().includes(t.toUpperCase()));
1003
+ };
1004
+ const fields = cache.fields[jd.objId] || {};
1005
+ const rev = {};
1006
+ for (const [k, v] of Object.entries(fields)) rev[String(v)] = k;
1007
+ const picked = [];
1008
+ for (const row of rows) {
1009
+ if (!match(row) || picked.length >= 5) continue;
1010
+ const dataId = String(row.form_data_id || row.id || "");
1011
+ if (!dataId || dataId === String(jd.dataId || "")) continue;
1012
+ const item = {
1013
+ \u804C\u4F4D: String(row.position_name || row.name || "").trim(),
1014
+ \u72B6\u6001: String(row.status || row.data_status_value || "").trim(),
1015
+ \u521B\u5EFA\u65F6\u95F4: String(row.create_time || "").trim()
1016
+ };
1017
+ try {
1018
+ const rec = await getFormDataRecord(host, jd.formId, dataId, jd.objId, "");
1019
+ for (const d of rec.dataDetails || []) {
1020
+ const dk = rev[String(d.fieldId ?? d.formField?.id)] || "";
1021
+ if (!dk) continue;
1022
+ let v = "";
1023
+ if (Array.isArray(d.dataOptions) && d.dataOptions.length) v = d.dataOptions.map((o) => o?.content || "").join(",");
1024
+ else if (d.dataText?.content) v = String(d.dataText.content);
1025
+ else if (d.content !== void 0 && d.content !== "") v = String(d.content);
1026
+ if (v === "") continue;
1027
+ if (REF_DETAIL_KEYS.includes(dk)) item[dk] = v.slice(0, 60);
1028
+ else if (REF_TEXT_KEYS.includes(dk)) item[dk] = v.replace(/\s+/g, " ").slice(0, 150);
1029
+ }
1030
+ } catch {
1031
+ }
1032
+ if (Object.keys(item).length > 3) picked.push(item);
1033
+ }
1034
+ return { available: true, keyword: kw, scanned: rows.length, items: picked };
1035
+ }
1036
+ async function buildJd(opts) {
1037
+ const { ctx, positionName, duty, requestText, demandId, demandName, sets } = opts;
1038
+ const { host, appId, cache } = ctx;
1039
+ const objId = objIdOf(cache, "position_manage");
1040
+ const formId = formIdOf(cache, "position_manage");
1041
+ const fields = cache.fields[objId] || {};
1042
+ const fid = (k) => fields[k];
1043
+ for (const k of DATAKEYS_TEXT) if (!fid(k)) throw rsFail("\u804C\u4F4D\u7BA1\u7406\u7F3A\u5C11\u5B57\u6BB5 " + k + "\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF09");
1044
+ for (const k of DATAKEYS_MTEXT) if (!fid(k)) throw rsFail("\u804C\u4F4D\u7BA1\u7406\u7F3A\u5C11\u5B57\u6BB5 " + k + "\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF09");
1045
+ const refByFid = await fetchRefByFid(ctx);
1046
+ const map = /* @__PURE__ */ new Map();
1047
+ const put = (d) => {
1048
+ if (d && d.formField && d.formField.id) map.set(String(d.formField.id), d);
1049
+ };
1050
+ const hasKey = (k) => {
1051
+ const id = fid(k);
1052
+ return !!(id && map.has(String(id)));
1053
+ };
1054
+ put({ formField: { id: fid("position_name") }, content: positionName });
1055
+ if (duty) put({ formField: { id: fid("position_duty") }, dataText: { content: duty } });
1056
+ if (requestText) put({ formField: { id: fid("work_request") }, dataText: { content: requestText } });
1057
+ if (demandId) {
1058
+ if (!fid("demand")) throw rsFail("\u804C\u4F4D\u7BA1\u7406\u7F3A\u5C11\u5B57\u6BB5 demand\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF0C\u5173\u8054\u9700\u6C42\u65E0\u6CD5\u8BBE\u7F6E\uFF09");
1059
+ put({ formField: { id: fid("demand") }, dataOptions: [{ optionId: String(demandId), content: demandName || String(demandId) }] });
1060
+ }
1061
+ const appliedSets = [];
1062
+ const skippedSets = [];
1063
+ for (const [k, v] of sets) {
1064
+ const d = await resolveSetValue(ctx, k, v, refByFid);
1065
+ if (d) {
1066
+ put(d);
1067
+ appliedSets.push(k + "=" + v);
1068
+ } else {
1069
+ skippedSets.push(k + "=" + v);
1070
+ }
1071
+ }
1072
+ if (!hasKey("status")) {
1073
+ if (!fid("status")) throw rsFail("\u804C\u4F4D\u7BA1\u7406\u7F3A\u5C11\u5B57\u6BB5 status\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF09");
1074
+ const statusMap = cachedDsMap(cache, objId, "status") || await getDsOptions(host, objId, fid("status"), appId);
1075
+ const statusId = statusMap[DEF_STATUS];
1076
+ if (!statusId) throw rsFail("\u804C\u4F4D\u7BA1\u7406\u300C\u62DB\u8058\u72B6\u6001\u300D\u672A\u89E3\u6790\u5230\u300C" + DEF_STATUS + "\u300D\u9009\u9879\uFF08ds/options \u8FD4\u56DE\u4E3A\u7A7A\uFF09");
1077
+ put({ formField: { id: fid("status") }, dataOptions: [{ optionId: String(statusId), content: DEF_STATUS }] });
1078
+ }
1079
+ const defFlow = cache.defaultRecruitFlow;
1080
+ if (!hasKey("recruit_flow") && fid("recruit_flow") && defFlow && defFlow.id) {
1081
+ put({ formField: { id: fid("recruit_flow") }, dataOptions: [{ optionId: String(defFlow.id), content: defFlow.name }] });
1082
+ }
1083
+ const filled = [];
1084
+ let currentUser = null;
1085
+ if (DEF_CURRENT_USER.some((k) => fid(k) && !hasKey(k))) {
1086
+ currentUser = await resolveCurrentUser(host);
1087
+ if (currentUser && currentUser.id) {
1088
+ for (const k of DEF_CURRENT_USER) {
1089
+ if (fid(k) && !hasKey(k)) {
1090
+ put({ formField: { id: fid(k) }, dataOptions: [{ optionId: String(currentUser.id), content: currentUser.name }] });
1091
+ filled.push(k);
1092
+ }
1093
+ }
1094
+ }
1095
+ }
1096
+ for (const [k, v] of Object.entries(DEF_TEXT)) {
1097
+ if (fid(k) && !hasKey(k)) {
1098
+ put({ formField: { id: fid(k) }, content: v });
1099
+ filled.push(k);
1100
+ }
1101
+ }
1102
+ for (const [k, name] of Object.entries(DEF_OPTION)) {
1103
+ if (fid(k) && !hasKey(k)) {
1104
+ const m = cachedDsMap(cache, objId, k) || await getDsOptions(host, objId, fid(k), appId);
1105
+ const oid = m[name];
1106
+ if (oid) {
1107
+ put({ formField: { id: fid(k) }, dataOptions: [{ optionId: String(oid), content: name }] });
1108
+ filled.push(k);
1109
+ }
1110
+ }
1111
+ }
1112
+ for (const k of DEF_FROM_REF) {
1113
+ const id = fid(k);
1114
+ if (id && !map.has(String(id)) && refByFid[String(id)]) {
1115
+ const d = refToSubmit(refByFid[String(id)]);
1116
+ if (d) {
1117
+ put(d);
1118
+ filled.push(k);
1119
+ }
1120
+ }
1121
+ }
1122
+ const inv = {};
1123
+ for (const [k, v] of Object.entries(fields)) inv[String(v)] = k;
1124
+ const requiredDefaultsFilled = [...map.keys()].map((f) => inv[f] || f).filter((k) => filled.includes(k));
1125
+ return {
1126
+ objId,
1127
+ formId,
1128
+ appId,
1129
+ dataDetails: [...map.values()],
1130
+ summary: {
1131
+ action: "\u751F\u6210\u804C\u4F4DJD\uFF08\u521B\u5EFA\u804C\u4F4D\uFF09",
1132
+ positionName,
1133
+ dutyLen: (duty || "").length,
1134
+ requestLen: (requestText || "").length,
1135
+ demand: demandId ? demandName || demandId : "\uFF08\u672A\u5173\u8054\u9700\u6C42\uFF09",
1136
+ status: DEF_STATUS,
1137
+ recruitFlow: defFlow && defFlow.id ? defFlow.name : "\uFF08\u672A\u89E3\u6790\u5230\u9ED8\u8BA4\u6D41\u7A0B\uFF09",
1138
+ currentUser: currentUser ? currentUser.name : null,
1139
+ setApplied: appliedSets,
1140
+ setSkipped: skippedSets,
1141
+ requiredDefaultsFilled
1142
+ }
1143
+ };
1144
+ }
1145
+ function setsOf(args) {
1146
+ const raw = args.sets;
1147
+ if (Array.isArray(raw)) {
1148
+ return raw.map((item) => {
1149
+ if (typeof item === "string") {
1150
+ const j = item.indexOf("=");
1151
+ if (j <= 0) throw rsValidationError("set_invalid", `sets \u9879\u5FC5\u987B\u662F "\u5B57\u6BB5=\u503C" \u5F62\u5F0F\uFF1A${item}`);
1152
+ return [item.slice(0, j).trim().replace(/-/g, "_"), item.slice(j + 1)];
1153
+ }
1154
+ if (item && typeof item === "object" && item.key !== void 0) {
1155
+ return [String(item.key).trim().replace(/-/g, "_"), String(item.value ?? "")];
1156
+ }
1157
+ throw rsValidationError("set_invalid", 'sets \u9879\u5FC5\u987B\u662F\u5B57\u7B26\u4E32 "\u5B57\u6BB5=\u503C" \u6216 {key,value} \u5BF9\u8C61');
1158
+ });
1159
+ }
1160
+ return [];
1161
+ }
1162
+ async function prepare(args, env) {
1163
+ const host = env.host;
1164
+ const session = await host.getSession();
1165
+ const ctx = { host, baseUrl: session.baseUrl.replace(/\/+$/, ""), appId: "", cache: await env.recruitContext() };
1166
+ ctx.appId = ctx.cache.appId;
1167
+ const positionName = String(args.positionName || "").trim();
1168
+ if (!positionName) throw rsValidationError("position_name_required", "positionName \u5FC5\u586B");
1169
+ const jd = await buildJd({
1170
+ ctx,
1171
+ positionName,
1172
+ duty: args.positionDuty ? String(args.positionDuty) : void 0,
1173
+ requestText: args.workRequest ? String(args.workRequest) : void 0,
1174
+ demandId: args.demandId ? String(args.demandId) : void 0,
1175
+ demandName: args.demandName ? String(args.demandName) : void 0,
1176
+ sets: setsOf(args)
1177
+ });
1178
+ const refs = await fetchRecentReferences(ctx, jd, String(args.referenceKeyword || positionName)).catch((e) => ({ available: false, reason: e.message, items: [] }));
1179
+ const willSubmit = { formId: jd.formId, objId: jd.objId, dataDetails: jd.dataDetails };
1180
+ const fp = fingerprint(willSubmit);
1181
+ const continuation = await env.continuation.issue({ operation: "recruit-stage.jd.create.apply", fingerprint: fp });
1182
+ return rsSuccess("recruit-stage.jd.create.prepare", {
1183
+ mode: "prepare",
1184
+ ...jd.summary,
1185
+ recentReferences: refs.items,
1186
+ recentReferencesNote: refs.available ? `\u5DF2\u6309\u300C${refs.keyword}\u300D\u5728\u5F53\u524D\u73AF\u5883\u804C\u4F4D\u5217\u8868\u4E2D\u68C0\u7D22\uFF08\u626B\u63CF ${refs.scanned} \u6761\uFF0C\u547D\u4E2D ${refs.items.length} \u6761\uFF09\u2014\u2014\u8BF7\u5BF9\u6807\u8FD9\u4E9B\u540C\u7C7B\u804C\u4F4D\u7684\u85AA\u916C/\u4EBA\u6570/\u8981\u6C42\u7ED9\u51FA\u63A8\u8350\u503C\uFF0C\u4E00\u5E76\u653E\u8FDB\u786E\u8BA4\u6458\u8981\uFF0C\u4E0D\u8981\u9010\u5B57\u6BB5\u8BE2\u95EE\u7528\u6237` : "\u53C2\u8003\u68C0\u7D22\u4E0D\u53EF\u7528\uFF1A" + (refs.reason || ""),
1187
+ willSubmit
1188
+ }, {
1189
+ continuation,
1190
+ fingerprint: fp,
1191
+ confirmHint: "\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u643A\u5E26 confirm:true \u4E0E\u672C continuation \u8C03\u7528 recruit-stage.jd.create.apply"
1192
+ });
1193
+ }
1194
+ async function apply(args, env) {
1195
+ if (args.confirm !== true) throw rsConfirmationError("recruit-stage.jd.create.apply", { hint: "\u5199\u64CD\u4F5C\u5FC5\u987B\u663E\u5F0F confirm:true" });
1196
+ if (!args.continuation) throw rsValidationError("continuation_required", "\u7F3A\u5C11 continuation\uFF08\u6765\u81EA prepare \u7684 meta\uFF09");
1197
+ const payload = await env.continuation.verify(String(args.continuation), "recruit-stage.jd.create.apply");
1198
+ const host = env.host;
1199
+ const session = await host.getSession();
1200
+ const ctx = { host, baseUrl: session.baseUrl.replace(/\/+$/, ""), appId: "", cache: await env.recruitContext() };
1201
+ ctx.appId = ctx.cache.appId;
1202
+ const positionName = String(args.positionName || "").trim();
1203
+ if (!positionName) throw rsValidationError("position_name_required", "positionName \u5FC5\u586B");
1204
+ const jd = await buildJd({
1205
+ ctx,
1206
+ positionName,
1207
+ duty: args.positionDuty ? String(args.positionDuty) : void 0,
1208
+ requestText: args.workRequest ? String(args.workRequest) : void 0,
1209
+ demandId: args.demandId ? String(args.demandId) : void 0,
1210
+ demandName: args.demandName ? String(args.demandName) : void 0,
1211
+ sets: setsOf(args)
1212
+ });
1213
+ const willSubmit = { formId: jd.formId, objId: jd.objId, dataDetails: jd.dataDetails };
1214
+ if (payload.fingerprint !== fingerprint(willSubmit)) {
1215
+ throw rsValidationError("fingerprint_mismatch", "\u5F85\u63D0\u4EA4\u5185\u5BB9\u4E0E prepare \u786E\u8BA4\u6458\u8981\u4E0D\u4E00\u81F4\uFF08\u5B57\u6BB5\u503C\u5DF2\u53D8\u5316\uFF09\uFF0C\u8BF7\u91CD\u65B0 prepare \u786E\u8BA4\u540E\u518D apply");
1216
+ }
1217
+ const formDataId = await saveFormData(host, {
1218
+ appId: jd.appId,
1219
+ formId: jd.formId,
1220
+ objId: jd.objId,
1221
+ name: "\u804C\u4F4D\u7BA1\u7406-" + positionName,
1222
+ dataDetails: jd.dataDetails
1223
+ });
1224
+ const r2 = await host.request({ path: "/api/recruit/position/v2/addPosition?formDataId=" + formDataId, method: "POST" });
1225
+ const data = r2?.data || {};
1226
+ const positionPostId = data.positionPostId;
1227
+ const checkRpa = data.checkRpa;
1228
+ const rpaValidMsg = data.rpaValidMsg || null;
1229
+ const created = r2?.status !== false && !!positionPostId;
1230
+ const listUrl = await resolvePositionNewUrl(ctx).catch(() => null);
1231
+ const cardUrl = buildCardUrl(ctx.baseUrl, jd.objId, formDataId);
1232
+ const locateUrl = buildListLocateUrl(ctx.baseUrl, ctx.appId, ctx.cache.lists?.position?.listId || "", jd.objId, formDataId);
1233
+ return rsSuccess("recruit-stage.jd.create.apply", {
1234
+ mode: "apply",
1235
+ result: created ? "success" : "failed",
1236
+ formDataId,
1237
+ positionPostId: positionPostId || null,
1238
+ rpaChecked: checkRpa === void 0 ? null : !!checkRpa,
1239
+ rpaValidMsg,
1240
+ positionListUrl: listUrl || null,
1241
+ positionCardUrl: cardUrl,
1242
+ positionListLocateUrl: locateUrl,
1243
+ // 产物链接(Agent 原样搬运成 Markdown 链接,按顺序:详情在前、列表在后)
1244
+ viewLink: cardUrl ? `[\u67E5\u770B ${jd.summary.positionName || "\u65B0\u5EFA\u804C\u4F4D"} \u804C\u4F4D\u8BE6\u60C5](${cardUrl})` : null,
1245
+ listLocateLink: locateUrl || listUrl ? `[\u6253\u5F00\u62DB\u8058\u5C97\u4F4D\u5217\u8868\uFF08\u5DF2\u5B9A\u4F4D\u65B0\u5EFA\u804C\u4F4D\uFF09](${locateUrl || listUrl})` : null,
1246
+ agentNote: "\u67E5\u770B\u5165\u53E3 = \u4E24\u4E2A Markdown \u94FE\u63A5\uFF08\u5E73\u53F0\u5B9E\u8BC1\u53EF\u70B9\u51FB\uFF09\uFF1A\u6309\u987A\u5E8F\u539F\u6837\u642C\u8FD0 viewLink \u4E0E listLocateLink\uFF1B\u4E0D\u8981\u5305 browsertool \u6807\u7B7E\u3002\u94FE\u63A5\u53EA\u662F\u67E5\u770B\u5165\u53E3\uFF0C\u91CD\u590D\u70B9\u51FB\u4E0D\u4F1A\u91CD\u590D\u521B\u5EFA\u3002",
1247
+ ...jd.summary
1248
+ });
1249
+ }
1250
+ async function prefill(args, env) {
1251
+ const host = env.host;
1252
+ const session = await host.getSession();
1253
+ const ctx = { host, baseUrl: session.baseUrl.replace(/\/+$/, ""), appId: "", cache: await env.recruitContext() };
1254
+ ctx.appId = ctx.cache.appId;
1255
+ const positionName = String(args.positionName || "").trim();
1256
+ if (!positionName) throw rsValidationError("position_name_required", "positionName \u5FC5\u586B");
1257
+ const jd = await buildJd({
1258
+ ctx,
1259
+ positionName,
1260
+ duty: args.positionDuty ? String(args.positionDuty) : void 0,
1261
+ requestText: args.workRequest ? String(args.workRequest) : void 0,
1262
+ demandId: args.demandId ? String(args.demandId) : void 0,
1263
+ demandName: args.demandName ? String(args.demandName) : void 0,
1264
+ sets: setsOf(args)
1265
+ });
1266
+ const fields = toPrefillFields(jd.dataDetails, ctx.cache.fields[jd.objId] || {});
1267
+ const listUrl = await resolvePositionNewUrl(ctx);
1268
+ const homeUrl = await resolveHomePageUrl(ctx);
1269
+ const jsExpr = `(() => { const W = window.WeFormSDK; const i = W && W.getWeFormInstance('ebuilderform', '${jd.formId}'); if (!i) return 'no-form-instance(\u8BF7\u5148\u6253\u5F00\u804C\u4F4D\u7BA1\u7406\u65B0\u5EFA\u8868\u5355)'; const fields = ${JSON.stringify(fields)}; let ok = 0, fail = []; for (const f of fields) { try { i.changeFieldValue(f.mark, f.info); ok++; } catch (e) { fail.push(f.label); } } return 'prefilled ' + ok + '/' + fields.length + (fail.length ? ' fail=' + fail.join(',') : ''); })()`;
1270
+ return rsSuccess("recruit-stage.jd.create.prefill", {
1271
+ mode: "prefill",
1272
+ ...jd.summary,
1273
+ formId: jd.formId,
1274
+ fields,
1275
+ jsExpr,
1276
+ openMethods: {
1277
+ homeFullscreen: { url: homeUrl, openAction: "\u70B9\u300C\u5FEB\u901F\u5165\u53E3 \u2192 \u521B\u5EFA\u804C\u4F4D\u300D\u6253\u5F00\u8868\u5355\uFF0C\u518D\u70B9\u8868\u5355\u53F3\u4E0A\u89D2\u300C\u653E\u5927\u300D\u6309\u94AE \u2192 \u5168\u5C4F\u5355\u9875" },
1278
+ listDialog: { url: listUrl, openAction: "\u70B9\u51FB\u9875\u9762\u4E0A\u7684\u300C\u65B0\u5EFA\u300D" }
1279
+ },
1280
+ steps: [
1281
+ "1) \u6D4F\u89C8\u5668\u6253\u5F00 openMethods.homeFullscreen.url\uFF08\u6216 listDialog.url\uFF09\uFF0C\u9700\u5DF2\u767B\u5F55\u8BE5\u73AF\u5883",
1282
+ "2) \u6253\u5F00\u65B0\u5EFA\u8868\u5355\uFF1AB = \u9996\u9875\u70B9\u300C\u521B\u5EFA\u804C\u4F4D\u300D+ \u70B9\u8868\u5355\u53F3\u4E0A\u300C\u653E\u5927\u300D\uFF08\u5168\u5C4F\u5355\u9875\uFF09\uFF1BA = \u5217\u8868\u70B9\u300C\u65B0\u5EFA\u300D\uFF08\u5F39\u7A97\uFF09",
1283
+ '3) \u5728\u9875\u9762\u4E0A\u4E0B\u6587\u6267\u884C jsExpr\uFF08\u5982 browser-skill: bsk evaluate "<jsExpr>"\uFF09\u2192 \u8FD4\u56DE prefilled N/N \u5373\u5B8C\u6210',
1284
+ "4) \u7531\u64CD\u4F5C\u8005\u6838\u5BF9\u5185\u5BB9\u540E\u81EA\u884C\u70B9\u300C\u4FDD\u5B58/\u4FDD\u5B58\u5E76\u53D1\u5E03\u300D\uFF1B\u672C\u6A21\u5F0F\u4E0D\u5199\u4EFB\u4F55\u6570\u636E"
1285
+ ]
1286
+ });
1287
+ }
1288
+ var baseInput = {
1289
+ positionName: { type: "string", description: "\u804C\u4F4D\u540D\u79F0\uFF08\u5FC5\u586B\uFF09" },
1290
+ positionDuty: { type: "string", description: "\u804C\u4F4D\u804C\u8D23/JD \u6B63\u6587\uFF08\u591A\u884C\u6587\u672C\uFF09" },
1291
+ workRequest: { type: "string", description: "\u4EFB\u804C\u8981\u6C42\uFF08\u591A\u884C\u6587\u672C\uFF09" },
1292
+ demandId: { ...SCHEMA_ID_STR, description: "\u5173\u8054\u62DB\u8058\u9700\u6C42 id\uFF08\u53EF\u9009\uFF09" },
1293
+ demandName: { type: "string", description: "\u5173\u8054\u62DB\u8058\u9700\u6C42\u540D\u79F0\uFF08\u53EF\u9009\uFF0C\u4EC5\u5C55\u793A\uFF09" },
1294
+ sets: {
1295
+ type: "array",
1296
+ maxItems: 30,
1297
+ items: { oneOf: [{ type: "string" }, { type: "object", properties: { key: { type: "string" }, value: { type: "string" } } }] },
1298
+ description: 'AI \u4F9D JD \u6307\u5B9A\u7684\u5B57\u6BB5\u503C\uFF08"\u5B57\u6BB5dataKey=\u503C" \u6216 {key,value}\uFF09'
1299
+ },
1300
+ referenceKeyword: { type: "string", description: "\u540C\u7C7B\u804C\u4F4D\u53C2\u8003\u68C0\u7D22\u5173\u952E\u8BCD\uFF08\u9ED8\u8BA4\u7528\u804C\u4F4D\u540D\uFF09" }
1301
+ };
1302
+ var jdCreateOperations = [
1303
+ {
1304
+ name: "recruit-stage.jd.create.prepare",
1305
+ resource: "recruit.jd",
1306
+ method: "prepare",
1307
+ action: "jd.create.prepare",
1308
+ summary: "\u751F\u6210\u804C\u4F4D\u786E\u8BA4\u6458\u8981\uFF1A\u89E3\u6790\u804C\u4F4D\u8868\u5355\u5B57\u6BB5\u3001\u5957\u7528\u9ED8\u8BA4\u503C\uFF08\u72B6\u6001/\u6D41\u7A0B/\u8D1F\u8D23\u4EBA/\u5173\u8054\u5B57\u6BB5\u53D6\u73AF\u5883\u73B0\u6210\u503C\uFF09\u3001\u68C0\u7D22\u6700\u8FD1\u540C\u7C7B\u804C\u4F4D\u53C2\u8003\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6570\u636E",
1309
+ capability: "recruit.jd.create",
1310
+ risk: "write",
1311
+ requiresConfirmation: true,
1312
+ inputSchema: schemaOf(baseInput, { required: ["positionName"] }),
1313
+ handler: prepare
1314
+ },
1315
+ {
1316
+ name: "recruit-stage.jd.create.apply",
1317
+ resource: "recruit.jd",
1318
+ method: "apply",
1319
+ action: "jd.create.apply",
1320
+ summary: "\u7528\u6237\u786E\u8BA4\u540E\u521B\u5EFA\u804C\u4F4D\uFF1AsaveFormData\uFF08\u804C\u4F4D\u7BA1\u7406\uFF09\u2192 addPosition \u751F\u6210\u804C\u4F4D\uFF0C\u4E0E\u9875\u9762\u300C\u4FDD\u5B58\u300D\u7B49\u4EF7\uFF1B\u8FD4\u56DE\u804C\u4F4D\u8BE6\u60C5\u4E0E\u5217\u8868\u5B9A\u4F4D\u53CC\u94FE\u63A5",
1321
+ capability: "recruit.jd.create",
1322
+ risk: "write",
1323
+ requiresConfirmation: true,
1324
+ inputSchema: schemaOf({ ...baseInput, confirm: { type: "boolean", description: "\u5FC5\u987B\u663E\u5F0F true" }, continuation: { type: "string", description: "prepare \u8FD4\u56DE\u7684 continuation" } }, { required: ["positionName", "confirm", "continuation"] }),
1325
+ handler: apply
1326
+ },
1327
+ {
1328
+ name: "recruit-stage.jd.create.prefill",
1329
+ resource: "recruit.jd",
1330
+ method: "prefill",
1331
+ action: "jd.create.prefill",
1332
+ summary: "\u9884\u586B\u6A21\u5F0F\uFF08\u4EBA\u5DE5\u4FDD\u5B58\uFF09\uFF1A\u8F93\u51FA\u6253\u5F00\u804C\u4F4D\u65B0\u5EFA\u9875\u7684 URL + WeFormSDK \u6CE8\u5165 jsExpr\uFF0C\u4E0D\u843D\u5E93\uFF0C\u7531\u64CD\u4F5C\u8005\u6838\u5BF9\u540E\u81EA\u884C\u4FDD\u5B58",
1333
+ capability: "recruit.jd.create",
1334
+ risk: "read",
1335
+ inputSchema: schemaOf(baseInput, { required: ["positionName"] }),
1336
+ handler: prefill
1337
+ }
1338
+ ];
1339
+
1340
+ // src/shortcuts/recruit-stage/operations/resume-import.ts
1341
+ import { createHash as createHash2 } from "node:crypto";
1342
+ import fs from "node:fs";
1343
+ import path from "node:path";
1344
+
1345
+ // src/shortcuts/recruit-stage/school-tiers.json
1346
+ var school_tiers_default = {
1347
+ source: "985\uFF1A39\u6240\uFF08\u56FA\u5B9A\uFF09\uFF1B211\uFF1A\u6559\u80B2\u90E8\u5B98\u7F51\u300E211\u5DE5\u7A0B\u5B66\u6821\u540D\u5355\u300F\uFF08moe.gov.cn\uFF0C2005-12\uFF09\uFF1B\u53CC\u4E00\u6D41\uFF1A\u6559\u80B2\u90E8\u7B2C\u4E8C\u8F6E\u300E\u53CC\u4E00\u6D41\u300F\u5EFA\u8BBE\u9AD8\u6821\u540D\u5355\uFF08\u6559\u7814\u51FD\u30142022\u30151\u53F7\uFF0C147\u6240\uFF09\u3002\u4EC5\u7528\u4E8E\u4EBA\u624D\u6807\u7B7E\u81EA\u52A8\u63A8\u5BFC\uFF0C\u4E0D\u53C2\u4E0E\u5176\u4ED6\u4E1A\u52A1\u3002",
1348
+ aliases: {
1349
+ \u7B2C\u4E8C\u519B\u533B\u5927\u5B66: "\u6D77\u519B\u519B\u533B\u5927\u5B66",
1350
+ \u7B2C\u56DB\u519B\u533B\u5927\u5B66: "\u7A7A\u519B\u519B\u533B\u5927\u5B66",
1351
+ \u56FD\u9632\u79D1\u5B66\u6280\u672F\u5927\u5B66: "\u56FD\u9632\u79D1\u6280\u5927\u5B66",
1352
+ \u4E2D\u56FD\u534F\u548C\u533B\u79D1\u5927\u5B66: "\u5317\u4EAC\u534F\u548C\u533B\u5B66\u9662",
1353
+ \u5317\u65B9\u4EA4\u901A\u5927\u5B66: "\u5317\u4EAC\u4EA4\u901A\u5927\u5B66",
1354
+ \u5317\u4EAC\u5E7F\u64AD\u5B66\u9662: "\u4E2D\u56FD\u4F20\u5A92\u5927\u5B66",
1355
+ \u4E0A\u6D77\u7B2C\u4E8C\u533B\u79D1\u5927\u5B66: "\u4E0A\u6D77\u4EA4\u901A\u5927\u5B66",
1356
+ \u4E0A\u6D77\u4F53\u80B2\u5927\u5B66: "\u4E0A\u6D77\u4F53\u80B2\u5B66\u9662",
1357
+ \u77F3\u6CB9\u5927\u5B66: "\u4E2D\u56FD\u77F3\u6CB9\u5927\u5B66"
1358
+ },
1359
+ "985": [
1360
+ "\u5317\u4EAC\u5927\u5B66",
1361
+ "\u4E2D\u56FD\u4EBA\u6C11\u5927\u5B66",
1362
+ "\u6E05\u534E\u5927\u5B66",
1363
+ "\u5317\u4EAC\u822A\u7A7A\u822A\u5929\u5927\u5B66",
1364
+ "\u5317\u4EAC\u7406\u5DE5\u5927\u5B66",
1365
+ "\u4E2D\u56FD\u519C\u4E1A\u5927\u5B66",
1366
+ "\u5317\u4EAC\u5E08\u8303\u5927\u5B66",
1367
+ "\u4E2D\u592E\u6C11\u65CF\u5927\u5B66",
1368
+ "\u5357\u5F00\u5927\u5B66",
1369
+ "\u5929\u6D25\u5927\u5B66",
1370
+ "\u5927\u8FDE\u7406\u5DE5\u5927\u5B66",
1371
+ "\u4E1C\u5317\u5927\u5B66",
1372
+ "\u5409\u6797\u5927\u5B66",
1373
+ "\u54C8\u5C14\u6EE8\u5DE5\u4E1A\u5927\u5B66",
1374
+ "\u590D\u65E6\u5927\u5B66",
1375
+ "\u540C\u6D4E\u5927\u5B66",
1376
+ "\u4E0A\u6D77\u4EA4\u901A\u5927\u5B66",
1377
+ "\u534E\u4E1C\u5E08\u8303\u5927\u5B66",
1378
+ "\u5357\u4EAC\u5927\u5B66",
1379
+ "\u4E1C\u5357\u5927\u5B66",
1380
+ "\u6D59\u6C5F\u5927\u5B66",
1381
+ "\u4E2D\u56FD\u79D1\u5B66\u6280\u672F\u5927\u5B66",
1382
+ "\u53A6\u95E8\u5927\u5B66",
1383
+ "\u5C71\u4E1C\u5927\u5B66",
1384
+ "\u4E2D\u56FD\u6D77\u6D0B\u5927\u5B66",
1385
+ "\u6B66\u6C49\u5927\u5B66",
1386
+ "\u534E\u4E2D\u79D1\u6280\u5927\u5B66",
1387
+ "\u6E56\u5357\u5927\u5B66",
1388
+ "\u4E2D\u5357\u5927\u5B66",
1389
+ "\u56FD\u9632\u79D1\u6280\u5927\u5B66",
1390
+ "\u4E2D\u5C71\u5927\u5B66",
1391
+ "\u534E\u5357\u7406\u5DE5\u5927\u5B66",
1392
+ "\u56DB\u5DDD\u5927\u5B66",
1393
+ "\u7535\u5B50\u79D1\u6280\u5927\u5B66",
1394
+ "\u91CD\u5E86\u5927\u5B66",
1395
+ "\u897F\u5B89\u4EA4\u901A\u5927\u5B66",
1396
+ "\u897F\u5317\u5DE5\u4E1A\u5927\u5B66",
1397
+ "\u897F\u5317\u519C\u6797\u79D1\u6280\u5927\u5B66",
1398
+ "\u5170\u5DDE\u5927\u5B66"
1399
+ ],
1400
+ "211": [
1401
+ "\u5317\u4EAC\u5927\u5B66",
1402
+ "\u4E2D\u56FD\u4EBA\u6C11\u5927\u5B66",
1403
+ "\u6E05\u534E\u5927\u5B66",
1404
+ "\u5317\u4EAC\u4EA4\u901A\u5927\u5B66",
1405
+ "\u5317\u4EAC\u5DE5\u4E1A\u5927\u5B66",
1406
+ "\u5317\u4EAC\u822A\u7A7A\u822A\u5929\u5927\u5B66",
1407
+ "\u5317\u4EAC\u7406\u5DE5\u5927\u5B66",
1408
+ "\u5317\u4EAC\u79D1\u6280\u5927\u5B66",
1409
+ "\u5317\u4EAC\u5316\u5DE5\u5927\u5B66",
1410
+ "\u5317\u4EAC\u90AE\u7535\u5927\u5B66",
1411
+ "\u4E2D\u56FD\u519C\u4E1A\u5927\u5B66",
1412
+ "\u5317\u4EAC\u6797\u4E1A\u5927\u5B66",
1413
+ "\u5317\u4EAC\u4E2D\u533B\u836F\u5927\u5B66",
1414
+ "\u5317\u4EAC\u5E08\u8303\u5927\u5B66",
1415
+ "\u5317\u4EAC\u5916\u56FD\u8BED\u5927\u5B66",
1416
+ "\u4E2D\u56FD\u4F20\u5A92\u5927\u5B66",
1417
+ "\u4E2D\u592E\u8D22\u7ECF\u5927\u5B66",
1418
+ "\u5BF9\u5916\u7ECF\u6D4E\u8D38\u6613\u5927\u5B66",
1419
+ "\u5317\u4EAC\u4F53\u80B2\u5927\u5B66",
1420
+ "\u4E2D\u592E\u97F3\u4E50\u5B66\u9662",
1421
+ "\u4E2D\u592E\u6C11\u65CF\u5927\u5B66",
1422
+ "\u4E2D\u56FD\u653F\u6CD5\u5927\u5B66",
1423
+ "\u534E\u5317\u7535\u529B\u5927\u5B66",
1424
+ "\u5357\u5F00\u5927\u5B66",
1425
+ "\u5929\u6D25\u5927\u5B66",
1426
+ "\u5929\u6D25\u533B\u79D1\u5927\u5B66",
1427
+ "\u6CB3\u5317\u5DE5\u4E1A\u5927\u5B66",
1428
+ "\u592A\u539F\u7406\u5DE5\u5927\u5B66",
1429
+ "\u5185\u8499\u53E4\u5927\u5B66",
1430
+ "\u8FBD\u5B81\u5927\u5B66",
1431
+ "\u5927\u8FDE\u7406\u5DE5\u5927\u5B66",
1432
+ "\u4E1C\u5317\u5927\u5B66",
1433
+ "\u5927\u8FDE\u6D77\u4E8B\u5927\u5B66",
1434
+ "\u5409\u6797\u5927\u5B66",
1435
+ "\u5EF6\u8FB9\u5927\u5B66",
1436
+ "\u4E1C\u5317\u5E08\u8303\u5927\u5B66",
1437
+ "\u54C8\u5C14\u6EE8\u5DE5\u4E1A\u5927\u5B66",
1438
+ "\u54C8\u5C14\u6EE8\u5DE5\u7A0B\u5927\u5B66",
1439
+ "\u4E1C\u5317\u519C\u4E1A\u5927\u5B66",
1440
+ "\u4E1C\u5317\u6797\u4E1A\u5927\u5B66",
1441
+ "\u590D\u65E6\u5927\u5B66",
1442
+ "\u540C\u6D4E\u5927\u5B66",
1443
+ "\u4E0A\u6D77\u4EA4\u901A\u5927\u5B66",
1444
+ "\u534E\u4E1C\u7406\u5DE5\u5927\u5B66",
1445
+ "\u4E1C\u534E\u5927\u5B66",
1446
+ "\u534E\u4E1C\u5E08\u8303\u5927\u5B66",
1447
+ "\u4E0A\u6D77\u5916\u56FD\u8BED\u5927\u5B66",
1448
+ "\u4E0A\u6D77\u8D22\u7ECF\u5927\u5B66",
1449
+ "\u4E0A\u6D77\u5927\u5B66",
1450
+ "\u7B2C\u4E8C\u519B\u533B\u5927\u5B66",
1451
+ "\u5357\u4EAC\u5927\u5B66",
1452
+ "\u82CF\u5DDE\u5927\u5B66",
1453
+ "\u4E1C\u5357\u5927\u5B66",
1454
+ "\u5357\u4EAC\u822A\u7A7A\u822A\u5929\u5927\u5B66",
1455
+ "\u5357\u4EAC\u7406\u5DE5\u5927\u5B66",
1456
+ "\u4E2D\u56FD\u77FF\u4E1A\u5927\u5B66",
1457
+ "\u6CB3\u6D77\u5927\u5B66",
1458
+ "\u6C5F\u5357\u5927\u5B66",
1459
+ "\u5357\u4EAC\u519C\u4E1A\u5927\u5B66",
1460
+ "\u4E2D\u56FD\u836F\u79D1\u5927\u5B66",
1461
+ "\u5357\u4EAC\u5E08\u8303\u5927\u5B66",
1462
+ "\u6D59\u6C5F\u5927\u5B66",
1463
+ "\u5B89\u5FBD\u5927\u5B66",
1464
+ "\u4E2D\u56FD\u79D1\u5B66\u6280\u672F\u5927\u5B66",
1465
+ "\u5408\u80A5\u5DE5\u4E1A\u5927\u5B66",
1466
+ "\u53A6\u95E8\u5927\u5B66",
1467
+ "\u798F\u5DDE\u5927\u5B66",
1468
+ "\u5357\u660C\u5927\u5B66",
1469
+ "\u5C71\u4E1C\u5927\u5B66",
1470
+ "\u4E2D\u56FD\u6D77\u6D0B\u5927\u5B66",
1471
+ "\u4E2D\u56FD\u77F3\u6CB9\u5927\u5B66",
1472
+ "\u90D1\u5DDE\u5927\u5B66",
1473
+ "\u6B66\u6C49\u5927\u5B66",
1474
+ "\u534E\u4E2D\u79D1\u6280\u5927\u5B66",
1475
+ "\u4E2D\u56FD\u5730\u8D28\u5927\u5B66",
1476
+ "\u6B66\u6C49\u7406\u5DE5\u5927\u5B66",
1477
+ "\u534E\u4E2D\u519C\u4E1A\u5927\u5B66",
1478
+ "\u534E\u4E2D\u5E08\u8303\u5927\u5B66",
1479
+ "\u4E2D\u5357\u8D22\u7ECF\u653F\u6CD5\u5927\u5B66",
1480
+ "\u6E56\u5357\u5927\u5B66",
1481
+ "\u4E2D\u5357\u5927\u5B66",
1482
+ "\u6E56\u5357\u5E08\u8303\u5927\u5B66",
1483
+ "\u56FD\u9632\u79D1\u5B66\u6280\u672F\u5927\u5B66",
1484
+ "\u4E2D\u5C71\u5927\u5B66",
1485
+ "\u66A8\u5357\u5927\u5B66",
1486
+ "\u534E\u5357\u7406\u5DE5\u5927\u5B66",
1487
+ "\u534E\u5357\u5E08\u8303\u5927\u5B66",
1488
+ "\u5E7F\u897F\u5927\u5B66",
1489
+ "\u6D77\u5357\u5927\u5B66",
1490
+ "\u56DB\u5DDD\u5927\u5B66",
1491
+ "\u897F\u5357\u4EA4\u901A\u5927\u5B66",
1492
+ "\u7535\u5B50\u79D1\u6280\u5927\u5B66",
1493
+ "\u56DB\u5DDD\u519C\u4E1A\u5927\u5B66",
1494
+ "\u897F\u5357\u8D22\u7ECF\u5927\u5B66",
1495
+ "\u91CD\u5E86\u5927\u5B66",
1496
+ "\u897F\u5357\u5927\u5B66",
1497
+ "\u8D35\u5DDE\u5927\u5B66",
1498
+ "\u4E91\u5357\u5927\u5B66",
1499
+ "\u897F\u85CF\u5927\u5B66",
1500
+ "\u897F\u5317\u5927\u5B66",
1501
+ "\u897F\u5B89\u4EA4\u901A\u5927\u5B66",
1502
+ "\u897F\u5317\u5DE5\u4E1A\u5927\u5B66",
1503
+ "\u897F\u5B89\u7535\u5B50\u79D1\u6280\u5927\u5B66",
1504
+ "\u957F\u5B89\u5927\u5B66",
1505
+ "\u897F\u5317\u519C\u6797\u79D1\u6280\u5927\u5B66",
1506
+ "\u9655\u897F\u5E08\u8303\u5927\u5B66",
1507
+ "\u7B2C\u56DB\u519B\u533B\u5927\u5B66",
1508
+ "\u5170\u5DDE\u5927\u5B66",
1509
+ "\u9752\u6D77\u5927\u5B66",
1510
+ "\u5B81\u590F\u5927\u5B66",
1511
+ "\u65B0\u7586\u5927\u5B66",
1512
+ "\u77F3\u6CB3\u5B50\u5927\u5B66"
1513
+ ],
1514
+ \u53CC\u4E00\u6D41: [
1515
+ "\u5317\u4EAC\u5927\u5B66",
1516
+ "\u4E2D\u56FD\u4EBA\u6C11\u5927\u5B66",
1517
+ "\u6E05\u534E\u5927\u5B66",
1518
+ "\u5317\u4EAC\u4EA4\u901A\u5927\u5B66",
1519
+ "\u5317\u4EAC\u5DE5\u4E1A\u5927\u5B66",
1520
+ "\u5317\u4EAC\u822A\u7A7A\u822A\u5929\u5927\u5B66",
1521
+ "\u5317\u4EAC\u7406\u5DE5\u5927\u5B66",
1522
+ "\u5317\u4EAC\u79D1\u6280\u5927\u5B66",
1523
+ "\u5317\u4EAC\u5316\u5DE5\u5927\u5B66",
1524
+ "\u5317\u4EAC\u90AE\u7535\u5927\u5B66",
1525
+ "\u4E2D\u56FD\u519C\u4E1A\u5927\u5B66",
1526
+ "\u5317\u4EAC\u6797\u4E1A\u5927\u5B66",
1527
+ "\u5317\u4EAC\u534F\u548C\u533B\u5B66\u9662",
1528
+ "\u5317\u4EAC\u4E2D\u533B\u836F\u5927\u5B66",
1529
+ "\u5317\u4EAC\u5E08\u8303\u5927\u5B66",
1530
+ "\u9996\u90FD\u5E08\u8303\u5927\u5B66",
1531
+ "\u5317\u4EAC\u5916\u56FD\u8BED\u5927\u5B66",
1532
+ "\u4E2D\u56FD\u4F20\u5A92\u5927\u5B66",
1533
+ "\u4E2D\u592E\u8D22\u7ECF\u5927\u5B66",
1534
+ "\u5BF9\u5916\u7ECF\u6D4E\u8D38\u6613\u5927\u5B66",
1535
+ "\u5916\u4EA4\u5B66\u9662",
1536
+ "\u4E2D\u56FD\u4EBA\u6C11\u516C\u5B89\u5927\u5B66",
1537
+ "\u5317\u4EAC\u4F53\u80B2\u5927\u5B66",
1538
+ "\u4E2D\u592E\u97F3\u4E50\u5B66\u9662",
1539
+ "\u4E2D\u56FD\u97F3\u4E50\u5B66\u9662",
1540
+ "\u4E2D\u592E\u7F8E\u672F\u5B66\u9662",
1541
+ "\u4E2D\u592E\u620F\u5267\u5B66\u9662",
1542
+ "\u4E2D\u592E\u6C11\u65CF\u5927\u5B66",
1543
+ "\u4E2D\u56FD\u653F\u6CD5\u5927\u5B66",
1544
+ "\u5357\u5F00\u5927\u5B66",
1545
+ "\u5929\u6D25\u5927\u5B66",
1546
+ "\u5929\u6D25\u5DE5\u4E1A\u5927\u5B66",
1547
+ "\u5929\u6D25\u533B\u79D1\u5927\u5B66",
1548
+ "\u5929\u6D25\u4E2D\u533B\u836F\u5927\u5B66",
1549
+ "\u534E\u5317\u7535\u529B\u5927\u5B66",
1550
+ "\u6CB3\u5317\u5DE5\u4E1A\u5927\u5B66",
1551
+ "\u5C71\u897F\u5927\u5B66",
1552
+ "\u592A\u539F\u7406\u5DE5\u5927\u5B66",
1553
+ "\u5185\u8499\u53E4\u5927\u5B66",
1554
+ "\u8FBD\u5B81\u5927\u5B66",
1555
+ "\u5927\u8FDE\u7406\u5DE5\u5927\u5B66",
1556
+ "\u4E1C\u5317\u5927\u5B66",
1557
+ "\u5927\u8FDE\u6D77\u4E8B\u5927\u5B66",
1558
+ "\u5409\u6797\u5927\u5B66",
1559
+ "\u5EF6\u8FB9\u5927\u5B66",
1560
+ "\u4E1C\u5317\u5E08\u8303\u5927\u5B66",
1561
+ "\u54C8\u5C14\u6EE8\u5DE5\u4E1A\u5927\u5B66",
1562
+ "\u54C8\u5C14\u6EE8\u5DE5\u7A0B\u5927\u5B66",
1563
+ "\u4E1C\u5317\u519C\u4E1A\u5927\u5B66",
1564
+ "\u4E1C\u5317\u6797\u4E1A\u5927\u5B66",
1565
+ "\u590D\u65E6\u5927\u5B66",
1566
+ "\u540C\u6D4E\u5927\u5B66",
1567
+ "\u4E0A\u6D77\u4EA4\u901A\u5927\u5B66",
1568
+ "\u534E\u4E1C\u7406\u5DE5\u5927\u5B66",
1569
+ "\u4E1C\u534E\u5927\u5B66",
1570
+ "\u4E0A\u6D77\u6D77\u6D0B\u5927\u5B66",
1571
+ "\u4E0A\u6D77\u4E2D\u533B\u836F\u5927\u5B66",
1572
+ "\u534E\u4E1C\u5E08\u8303\u5927\u5B66",
1573
+ "\u4E0A\u6D77\u5916\u56FD\u8BED\u5927\u5B66",
1574
+ "\u4E0A\u6D77\u8D22\u7ECF\u5927\u5B66",
1575
+ "\u4E0A\u6D77\u4F53\u80B2\u5B66\u9662",
1576
+ "\u4E0A\u6D77\u97F3\u4E50\u5B66\u9662",
1577
+ "\u4E0A\u6D77\u5927\u5B66",
1578
+ "\u5357\u4EAC\u5927\u5B66",
1579
+ "\u82CF\u5DDE\u5927\u5B66",
1580
+ "\u4E1C\u5357\u5927\u5B66",
1581
+ "\u5357\u4EAC\u822A\u7A7A\u822A\u5929\u5927\u5B66",
1582
+ "\u5357\u4EAC\u7406\u5DE5\u5927\u5B66",
1583
+ "\u4E2D\u56FD\u77FF\u4E1A\u5927\u5B66",
1584
+ "\u5357\u4EAC\u90AE\u7535\u5927\u5B66",
1585
+ "\u6CB3\u6D77\u5927\u5B66",
1586
+ "\u6C5F\u5357\u5927\u5B66",
1587
+ "\u5357\u4EAC\u6797\u4E1A\u5927\u5B66",
1588
+ "\u5357\u4EAC\u4FE1\u606F\u5DE5\u7A0B\u5927\u5B66",
1589
+ "\u5357\u4EAC\u519C\u4E1A\u5927\u5B66",
1590
+ "\u5357\u4EAC\u533B\u79D1\u5927\u5B66",
1591
+ "\u5357\u4EAC\u4E2D\u533B\u836F\u5927\u5B66",
1592
+ "\u4E2D\u56FD\u836F\u79D1\u5927\u5B66",
1593
+ "\u5357\u4EAC\u5E08\u8303\u5927\u5B66",
1594
+ "\u6D59\u6C5F\u5927\u5B66",
1595
+ "\u4E2D\u56FD\u7F8E\u672F\u5B66\u9662",
1596
+ "\u5B89\u5FBD\u5927\u5B66",
1597
+ "\u4E2D\u56FD\u79D1\u5B66\u6280\u672F\u5927\u5B66",
1598
+ "\u5408\u80A5\u5DE5\u4E1A\u5927\u5B66",
1599
+ "\u53A6\u95E8\u5927\u5B66",
1600
+ "\u798F\u5DDE\u5927\u5B66",
1601
+ "\u5357\u660C\u5927\u5B66",
1602
+ "\u5C71\u4E1C\u5927\u5B66",
1603
+ "\u4E2D\u56FD\u6D77\u6D0B\u5927\u5B66",
1604
+ "\u4E2D\u56FD\u77F3\u6CB9\u5927\u5B66(\u534E\u4E1C)",
1605
+ "\u90D1\u5DDE\u5927\u5B66",
1606
+ "\u6CB3\u5357\u5927\u5B66",
1607
+ "\u6B66\u6C49\u5927\u5B66",
1608
+ "\u534E\u4E2D\u79D1\u6280\u5927\u5B66",
1609
+ "\u4E2D\u56FD\u5730\u8D28\u5927\u5B66(\u6B66\u6C49)",
1610
+ "\u6B66\u6C49\u7406\u5DE5\u5927\u5B66",
1611
+ "\u534E\u4E2D\u519C\u4E1A\u5927\u5B66",
1612
+ "\u534E\u4E2D\u5E08\u8303\u5927\u5B66",
1613
+ "\u4E2D\u5357\u8D22\u7ECF\u653F\u6CD5\u5927\u5B66",
1614
+ "\u6E58\u6F6D\u5927\u5B66",
1615
+ "\u6E56\u5357\u5927\u5B66",
1616
+ "\u4E2D\u5357\u5927\u5B66",
1617
+ "\u6E56\u5357\u5E08\u8303\u5927\u5B66",
1618
+ "\u4E2D\u5C71\u5927\u5B66",
1619
+ "\u66A8\u5357\u5927\u5B66",
1620
+ "\u534E\u5357\u7406\u5DE5\u5927\u5B66",
1621
+ "\u534E\u5357\u519C\u4E1A\u5927\u5B66",
1622
+ "\u5E7F\u5DDE\u533B\u79D1\u5927\u5B66",
1623
+ "\u5E7F\u5DDE\u4E2D\u533B\u836F\u5927\u5B66",
1624
+ "\u534E\u5357\u5E08\u8303\u5927\u5B66",
1625
+ "\u6D77\u5357\u5927\u5B66",
1626
+ "\u5E7F\u897F\u5927\u5B66",
1627
+ "\u56DB\u5DDD\u5927\u5B66",
1628
+ "\u91CD\u5E86\u5927\u5B66",
1629
+ "\u897F\u5357\u4EA4\u901A\u5927\u5B66",
1630
+ "\u7535\u5B50\u79D1\u6280\u5927\u5B66",
1631
+ "\u897F\u5357\u77F3\u6CB9\u5927\u5B66",
1632
+ "\u6210\u90FD\u7406\u5DE5\u5927\u5B66",
1633
+ "\u56DB\u5DDD\u519C\u4E1A\u5927\u5B66",
1634
+ "\u6210\u90FD\u4E2D\u533B\u836F\u5927\u5B66",
1635
+ "\u897F\u5357\u5927\u5B66",
1636
+ "\u897F\u5357\u8D22\u7ECF\u5927\u5B66",
1637
+ "\u8D35\u5DDE\u5927\u5B66",
1638
+ "\u4E91\u5357\u5927\u5B66",
1639
+ "\u897F\u85CF\u5927\u5B66",
1640
+ "\u897F\u5317\u5927\u5B66",
1641
+ "\u897F\u5B89\u4EA4\u901A\u5927\u5B66",
1642
+ "\u897F\u5317\u5DE5\u4E1A\u5927\u5B66",
1643
+ "\u897F\u5B89\u7535\u5B50\u79D1\u6280\u5927\u5B66",
1644
+ "\u957F\u5B89\u5927\u5B66",
1645
+ "\u897F\u5317\u519C\u6797\u79D1\u6280\u5927\u5B66",
1646
+ "\u9655\u897F\u5E08\u8303\u5927\u5B66",
1647
+ "\u5170\u5DDE\u5927\u5B66",
1648
+ "\u9752\u6D77\u5927\u5B66",
1649
+ "\u5B81\u590F\u5927\u5B66",
1650
+ "\u65B0\u7586\u5927\u5B66",
1651
+ "\u77F3\u6CB3\u5B50\u5927\u5B66",
1652
+ "\u4E2D\u56FD\u77FF\u4E1A\u5927\u5B66(\u5317\u4EAC)",
1653
+ "\u4E2D\u56FD\u77F3\u6CB9\u5927\u5B66(\u5317\u4EAC)",
1654
+ "\u4E2D\u56FD\u5730\u8D28\u5927\u5B66(\u5317\u4EAC)",
1655
+ "\u5B81\u6CE2\u5927\u5B66",
1656
+ "\u5357\u65B9\u79D1\u6280\u5927\u5B66",
1657
+ "\u4E0A\u6D77\u79D1\u6280\u5927\u5B66",
1658
+ "\u4E2D\u56FD\u79D1\u5B66\u9662\u5927\u5B66",
1659
+ "\u56FD\u9632\u79D1\u6280\u5927\u5B66",
1660
+ "\u6D77\u519B\u519B\u533B\u5927\u5B66",
1661
+ "\u7A7A\u519B\u519B\u533B\u5927\u5B66"
1662
+ ]
1663
+ };
1664
+
1665
+ // src/shortcuts/recruit-stage/operations/resume-import.ts
1666
+ var PROTECTED_UPDATE_KEYS = ["is_blacklist", "recruit_status", "candidate_status", "data_status"];
1667
+ async function uploadResume(host, baseUrl, filePath, fieldId, formId) {
1668
+ const buf = fs.readFileSync(filePath);
1669
+ const filename = path.basename(filePath);
1670
+ const baseForm = () => {
1671
+ const fd2 = new FormData();
1672
+ fd2.append("compressImg", "false");
1673
+ fd2.append("source", "form");
1674
+ fd2.append("fieldId", fieldId);
1675
+ fd2.append("sourceRefId", formId);
1676
+ fd2.append("module", "ebuilderform");
1677
+ fd2.append("authModule", "ebuilderform");
1678
+ fd2.append("formId", formId);
1679
+ fd2.append("refId", formId);
1680
+ fd2.append("size", String(buf.length));
1681
+ fd2.append("commonText", JSON.stringify({ fieldId, formId }));
1682
+ return fd2;
1683
+ };
1684
+ const headers = {
1685
+ langType: "zh_CN",
1686
+ timeZoneOffset: "-480",
1687
+ ebBusinessId: formId,
1688
+ Accept: "application/json, text/plain, */*",
1689
+ "X-Requested-With": "XMLHttpRequest"
1690
+ };
1691
+ const withFile = () => {
1692
+ const fd2 = baseForm();
1693
+ fd2.append("file", new Blob([buf]), filename);
1694
+ return fd2;
1695
+ };
1696
+ const c = await host.request({ path: "/api/file/preUploadCheck", method: "POST", multipart: withFile(), headers });
1697
+ const preUploadId = c?.data?.preUploadId;
1698
+ const uploadType = c?.data?.uploadType || "common";
1699
+ if (!preUploadId) throw rsFail("\u7B80\u5386\u9884\u4E0A\u4F20\u68C0\u67E5\u5931\u8D25\uFF1A" + JSON.stringify(c || {}).slice(0, 200));
1700
+ const fd = baseForm();
1701
+ const md5 = createHash2("md5").update(buf).digest("hex");
1702
+ const lm = String(Math.round(fs.statSync(filePath).mtimeMs || Date.now()));
1703
+ fd.append("name", filename);
1704
+ fd.append("MD5", md5);
1705
+ fd.append("lastModified", lm);
1706
+ fd.append("lastModifiedDate", lm);
1707
+ fd.append("preUploadId", String(preUploadId));
1708
+ fd.append("uploadType", String(uploadType));
1709
+ fd.append("file", new Blob([buf]), filename);
1710
+ const j = await host.request({ path: "/api/file/module/upload", method: "POST", multipart: fd, headers });
1711
+ if (!j?.data?.fileid) throw rsFail("\u7B80\u5386\u4E0A\u4F20\u5931\u8D25\uFF1A" + JSON.stringify(j || {}).slice(0, 200));
1712
+ return { fileid: String(j.data.fileid), name: j.data.name || filename };
1713
+ }
1714
+ async function extractResume(host, fileid) {
1715
+ const b = await host.request({ path: "/api/recruit/ocr/checkResumeExtraction", method: "POST", body: {} });
1716
+ if (b?.data !== true) throw rsFail("\u5F53\u524D\u73AF\u5883\u672A\u5F00\u542F\u7B80\u5386\u62BD\u53D6\uFF08checkResumeExtraction=false\uFF09\uFF1AOCR \u515C\u5E95\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7531\u5927\u6A21\u578B\u89E3\u6790\u540E\u8D70 structured \u8F93\u5165");
1717
+ const r = await host.request({ path: "/api/recruit/ocr/resumeExtraction?fileId=" + encodeURIComponent(fileid), method: "POST" });
1718
+ const d = r?.data?.data;
1719
+ if (!d || typeof d !== "object") throw rsFail("\u672A\u4ECE\u6587\u4EF6\u4E2D\u89E3\u6790\u51FA\u7B80\u5386\u4FE1\u606F\uFF08\u53EF\u80FD\u662F\u975E\u7B80\u5386\u6587\u4EF6\uFF09");
1720
+ return d;
1721
+ }
1722
+ function mapToTalent(fields, s) {
1723
+ const out = [];
1724
+ const put = (k, val, opts) => {
1725
+ if (val === void 0 || val === null || val === "") return;
1726
+ const id = fields[k];
1727
+ if (!id) return;
1728
+ out.push(opts ? { formField: { id }, dataOptions: opts } : { formField: { id }, content: String(val) });
1729
+ };
1730
+ const putLong = (k, val) => {
1731
+ if (val === void 0 || val === null || val === "") return;
1732
+ const id = fields[k];
1733
+ if (!id) return;
1734
+ out.push({ formField: { id }, dataText: { content: String(val) } });
1735
+ };
1736
+ put("candidate_name", s.candidateName);
1737
+ put("telephone", s.telephone);
1738
+ put("email", s.email);
1739
+ if (s.gender) put("gender", s.genderName || s.gender, [{ optionId: String(s.gender), content: s.genderName || String(s.gender) }]);
1740
+ put("birthday", s.birthday);
1741
+ put("age", s.age);
1742
+ if (s.highest_degree) put("highest_degree", s.highest_degree_name || s.highest_degree, [{ optionId: String(s.highest_degree), content: s.highest_degree_name || String(s.highest_degree) }]);
1743
+ if (/^\d{6,}$/.test(String(s.highest_degree_new || ""))) put("highest_degree_new", s.highest_degree_new);
1744
+ put("college", s.college);
1745
+ put("joinworkdate", s.joinworkdate);
1746
+ put("graduate_date", s.graduate_date);
1747
+ put("intend_position", s.intend_position);
1748
+ put("intended_workplace", s.intended_workplace);
1749
+ putLong("self_desc", s.self_desc);
1750
+ putLong("work_ability", s.work_ability);
1751
+ const job0 = Array.isArray(s.jobInformations) ? s.jobInformations[0] : null;
1752
+ if (job0) {
1753
+ put("current_position", job0.work_position);
1754
+ put("current_industry", job0.co_name);
1755
+ }
1756
+ return out;
1757
+ }
1758
+ function toDatasetMainTable(dataDetails, fields) {
1759
+ const rev = {};
1760
+ for (const [dk, fid] of Object.entries(fields || {})) rev[String(fid)] = dk;
1761
+ const out = {};
1762
+ for (const d of dataDetails || []) {
1763
+ const dk = rev[String(d?.formField?.id || "")];
1764
+ if (!dk) continue;
1765
+ let v;
1766
+ if (Array.isArray(d.dataOptions)) {
1767
+ const o = d.dataOptions[0] || {};
1768
+ v = o.optionId !== void 0 && o.optionId !== "" ? String(o.optionId) : String(o.content || "");
1769
+ } else if (d.dataText) {
1770
+ v = String(d.dataText.content ?? "");
1771
+ } else if (d.content !== void 0) {
1772
+ v = String(d.content);
1773
+ } else continue;
1774
+ if (v === "") continue;
1775
+ out[dk] = v;
1776
+ }
1777
+ return out;
1778
+ }
1779
+ async function resolveSubTableMeta(ctx) {
1780
+ const formId = formIdOf(ctx.cache, "talent_pool");
1781
+ const meta = await getFormLayoutMeta(ctx.host, formId);
1782
+ const comps = meta?.formLayout?.pageLayouts?.[0]?.comps || [];
1783
+ const tables = [];
1784
+ for (const c of comps) {
1785
+ const cfg = c.config || {};
1786
+ if (c.type === "DetailTable" && cfg.dataKey && cfg.subFormId) {
1787
+ tables.push({ dataKey: String(cfg.dataKey), subFormId: String(cfg.subFormId), title: String(cfg.title || "") });
1788
+ }
1789
+ }
1790
+ const out = {};
1791
+ for (const t of tables) {
1792
+ const cols = {};
1793
+ for (const c of comps) {
1794
+ const cfg = c.config || {};
1795
+ if (String(cfg.subFormId) !== t.subFormId) continue;
1796
+ if (!cfg.dataKey || !cfg.fieldId) continue;
1797
+ const col = { id: String(cfg.fieldId), type: String(c.type || ""), title: String(cfg.title || "") };
1798
+ if (cfg.start && cfg.end && cfg.start.dataKey && cfg.end.dataKey) {
1799
+ col.startKey = String(cfg.start.dataKey);
1800
+ col.endKey = String(cfg.end.dataKey);
1801
+ }
1802
+ cols[String(cfg.dataKey)] = col;
1803
+ }
1804
+ out[t.dataKey] = { ...t, columns: cols };
1805
+ }
1806
+ return out;
1807
+ }
1808
+ function makeDictLookup(cache) {
1809
+ const byId = /* @__PURE__ */ new Map();
1810
+ for (const x of cache.dict?.raw || []) {
1811
+ if (x.option_id && x.selection_name) byId.set(String(x.option_id), x.selection_name);
1812
+ }
1813
+ return (id) => byId.get(String(id)) || "";
1814
+ }
1815
+ function buildSubDetails(structured, subs, cache) {
1816
+ const byTable = {};
1817
+ const stats = {};
1818
+ const dictName = makeDictLookup(cache);
1819
+ const addRows = (dataKey, rows, fieldMap) => {
1820
+ const sub = subs[dataKey];
1821
+ if (!sub || !Array.isArray(rows) || !rows.length) return 0;
1822
+ const list = [];
1823
+ for (const r of rows) {
1824
+ if (!r || typeof r !== "object") continue;
1825
+ const row = {};
1826
+ for (const [dk, conv] of Object.entries(fieldMap)) {
1827
+ const col = sub.columns[dk];
1828
+ if (!col) continue;
1829
+ const v = conv(r);
1830
+ if (v === void 0 || v === null || v === "") continue;
1831
+ if (col.startKey && col.endKey) {
1832
+ const parts = Array.isArray(v) ? v : String(v).split(/[,~]/);
1833
+ const s = String(parts[0] || "").trim();
1834
+ const e = String(parts[1] || "").trim();
1835
+ if (!s || !e) continue;
1836
+ row[col.startKey] = s;
1837
+ row[col.endKey] = e;
1838
+ continue;
1839
+ }
1840
+ const scalar = typeof v === "object" ? String(v.optionId ?? v.content ?? "") : String(v);
1841
+ if (scalar === "") continue;
1842
+ row[dk] = scalar;
1843
+ }
1844
+ if (Object.keys(row).length) list.push(row);
1845
+ }
1846
+ if (list.length) byTable[dataKey] = list;
1847
+ return list.length;
1848
+ };
1849
+ const dictOpt = (id) => {
1850
+ if (id === void 0 || id === null || id === "") return "";
1851
+ const s = String(id).trim();
1852
+ return dictName(s) ? s : "";
1853
+ };
1854
+ const selOpt = (v) => {
1855
+ if (!v) return "";
1856
+ const s = String(v).trim();
1857
+ return /^\d{6,}$/.test(s) ? dictOpt(s) : "";
1858
+ };
1859
+ stats.rcrt_education_exp = addRows("rcrt_education_exp", structured.academicInformations, {
1860
+ school: (r) => r.school,
1861
+ edu_major: (r) => r.edu_major,
1862
+ edu_education: (r) => dictOpt(r.edu_education),
1863
+ degree: (r) => selOpt(r.degree),
1864
+ study_date: (r) => r.study_date,
1865
+ faculty: (r) => r.faculty,
1866
+ research_direction: (r) => r.research_direction,
1867
+ major_courses: (r) => r.major_courses
1868
+ });
1869
+ stats.rcrt_work_exp = addRows("rcrt_work_exp", structured.jobInformations || structured.workExperiences, {
1870
+ co_name: (r) => r.co_name || r.company,
1871
+ work_position: (r) => r.work_position || r.position,
1872
+ work_dept: (r) => r.work_dept || r.department,
1873
+ work_desc: (r) => r.job_description || r.work_desc,
1874
+ resign_cause: (r) => r.resign_cause,
1875
+ start_end_date: (r) => [r.start_date, r.end_date].filter(Boolean).join(",") || r.work_date,
1876
+ work_type: (r) => selOpt(r.work_type)
1877
+ });
1878
+ stats.rcrt_sxjl = addRows("rcrt_sxjl", structured.internshipExperiences, {
1879
+ sx_co_name: (r) => r.sx_co_name,
1880
+ position_job: (r) => r.position_job,
1881
+ job_description: (r) => r.job_description,
1882
+ entry_time: (r) => r.entry_time,
1883
+ sx_work_type: (r) => selOpt(r.sx_work_type)
1884
+ });
1885
+ stats.rcrt_project_exp = addRows("rcrt_project_exp", structured.projectExperiences, {
1886
+ pj_name: (r) => r.pj_name || r.project_name,
1887
+ pj_role: (r) => r.pj_role,
1888
+ pj_duty: (r) => r.pj_duty,
1889
+ pj_introduction: (r) => r.pj_introduction || r.project_description,
1890
+ pj_date: (r) => [r.start_date, r.end_date].filter(Boolean).join(",")
1891
+ });
1892
+ stats.rcrt_award_exp = addRows("rcrt_award_exp", structured.awardExperiences, {
1893
+ award_name: (r) => r.award_name,
1894
+ awarded: (r) => r.awarded,
1895
+ credentials: (r) => r.credentials,
1896
+ award_date: (r) => r.award_date
1897
+ });
1898
+ stats.rcrt_language_skill = addRows("rcrt_language_skill", structured.languageAbilities, {
1899
+ language_type: (r) => r.language_type,
1900
+ mastery: (r) => selOpt(r.mastery),
1901
+ ls_ability: (r) => selOpt(r.ls_ability),
1902
+ literacy: (r) => selOpt(r.literacy)
1903
+ });
1904
+ return { byTable, stats };
1905
+ }
1906
+ function makeBriefReader(ctx, formId, objId, fields) {
1907
+ const rev = {};
1908
+ for (const [dk, fid] of Object.entries(fields || {})) rev[String(fid)] = dk;
1909
+ return async (dataId) => {
1910
+ try {
1911
+ const r = await ctx.host.request({
1912
+ path: "/api/ebuilder/form/form/core/getFormDataAndLayout",
1913
+ method: "POST",
1914
+ body: {
1915
+ terminalType: "PC",
1916
+ dataId,
1917
+ formId,
1918
+ module: "ebuilderform",
1919
+ newLayout: true,
1920
+ dataCrossLayout: false,
1921
+ permissionId: "",
1922
+ allReadOnly: true,
1923
+ initialDatas: [],
1924
+ dynamicData: false,
1925
+ cost: false,
1926
+ customParam: { objId, dataId }
1927
+ }
1928
+ });
1929
+ const fd = r?.data?.formData;
1930
+ if (!fd) return null;
1931
+ const dets = fd.dataDetails || [];
1932
+ const get = (dk) => {
1933
+ const d = dets.find((x) => rev[String(x.fieldId)] === dk);
1934
+ if (!d) return "";
1935
+ return String(d.content ?? (d.dataText || {}).content ?? (d.dataOptions || []).map((o) => o.content).join(",") ?? "");
1936
+ };
1937
+ return { name: get("candidate_name"), mobile: get("telephone"), email: get("email"), college: get("college") };
1938
+ } catch {
1939
+ return null;
1940
+ }
1941
+ };
1942
+ }
1943
+ function resolveTalentCardUrl(ctx, talentId) {
1944
+ const pid = ctx.cache.ebdDetailPageId;
1945
+ if (!pid) return null;
1946
+ return `${ctx.baseUrl}/sp/ebdpage/view/${pid}?talentId=${talentId}${talentLinkExtra(ctx.cache)}`;
1947
+ }
1948
+ function resolveTalentListLocateUrl(ctx, talentId) {
1949
+ const listId = ctx.cache.lists?.talent?.listId;
1950
+ if (!listId || !talentId) return null;
1951
+ return `${ctx.baseUrl}/sp/ebdfpage/list/${listId}?talentId=${talentId}${talentLinkExtra(ctx.cache)}`;
1952
+ }
1953
+ async function readTalentSnapshot(ctx, opts) {
1954
+ const { host, formId, objId, fields, subMeta, dataId } = opts;
1955
+ const L = await getFormLayoutMeta(ctx.host, formId);
1956
+ const r = await ctx.host.request({
1957
+ path: "/api/ebuilder/form/form/core/getFormDataAndLayout",
1958
+ method: "POST",
1959
+ body: {
1960
+ terminalType: "PC",
1961
+ dataId,
1962
+ formId,
1963
+ module: "ebuilderform",
1964
+ newLayout: true,
1965
+ layoutId: L?.formLayout?.id,
1966
+ layoutMultiId: L?.formLayoutMultiple?.id,
1967
+ dataCrossLayout: false,
1968
+ permissionId: "",
1969
+ allReadOnly: true,
1970
+ initialDatas: [],
1971
+ dynamicData: false,
1972
+ cost: false,
1973
+ customParam: { objId, dataId }
1974
+ }
1975
+ });
1976
+ const dets = r?.data?.formData?.dataDetails || [];
1977
+ const rev = {};
1978
+ for (const [dk, fid] of Object.entries(fields || {})) rev[String(fid)] = dk;
1979
+ const valOf = (d) => {
1980
+ const opt = (d.dataOptions || []).map((o) => o.content).filter(Boolean).join(",");
1981
+ const txt = (d.dataText || {}).content || "";
1982
+ return opt || txt || (d.content ?? "");
1983
+ };
1984
+ const main = {};
1985
+ const subRows = {};
1986
+ const subCells = {};
1987
+ const subIdToKey = {};
1988
+ for (const [dk, sub] of Object.entries(subMeta || {})) subIdToKey[String(sub.subFormId)] = dk;
1989
+ const seen = {};
1990
+ for (const d of dets) {
1991
+ const subId = d.subFormId ? String(d.subFormId) : "";
1992
+ if (subId) {
1993
+ const dk2 = subIdToKey[subId] || subId;
1994
+ (seen[dk2] = seen[dk2] || /* @__PURE__ */ new Set()).add(String(d.dataIndex));
1995
+ subCells[dk2] = (subCells[dk2] || 0) + 1;
1996
+ continue;
1997
+ }
1998
+ const dk = rev[String(d.fieldId)];
1999
+ if (dk) main[dk] = String(valOf(d));
2000
+ }
2001
+ for (const [dk, s] of Object.entries(seen)) subRows[dk] = s.size;
2002
+ return { main, subRows, subCells };
2003
+ }
2004
+ async function matchTalentLabel(host, talentId) {
2005
+ const r = await host.request({ path: "/api/recruit/label/matchTalentLabel?talentId=" + encodeURIComponent(talentId), method: "POST", body: {} });
2006
+ return r?.data || null;
2007
+ }
2008
+ async function queryTagByName(host, name) {
2009
+ const pageSize = 100;
2010
+ const bodyOf = (type) => ({ type, name, order: 8, pageSize, pageNo: 1, search: 1, tagGroup: 0, tagModules: ["ebuilderform"] });
2011
+ const rs = await Promise.all([0, 1].map((type) => host.request({ path: "/api/my/tag/query", method: "POST", body: bodyOf(type) }).catch(() => null)));
2012
+ const items = [];
2013
+ let filtered = true;
2014
+ for (const r of rs) {
2015
+ const d = r?.data || {};
2016
+ const list = Array.isArray(d.data) ? d.data : [];
2017
+ const total = Number(d.total || 0);
2018
+ if (list.length >= pageSize && total >= pageSize) filtered = false;
2019
+ items.push(...list);
2020
+ }
2021
+ return { items, filtered };
2022
+ }
2023
+ async function fetchTagsOfType(host, type) {
2024
+ const pageSize = 100;
2025
+ const bodyOf = (pageNo) => ({ type, name: "", order: 8, pageSize, pageNo, search: 1, tagGroup: 0, tagModules: ["ebuilderform"] });
2026
+ const first = await host.request({ path: "/api/my/tag/query", method: "POST", body: bodyOf(1) }).catch(() => null);
2027
+ const d = first?.data || {};
2028
+ const rows = Array.isArray(d.data) ? [...d.data] : [];
2029
+ const total = Number(d.total || 0);
2030
+ const pages = Math.min(40, Math.max(1, Math.ceil(total / pageSize)));
2031
+ if (pages > 1 && rows.length) {
2032
+ const rest = Array.from({ length: pages - 1 }, (_, i) => i + 2);
2033
+ for (let i = 0; i < rest.length; i += 5) {
2034
+ const batch = rest.slice(i, i + 5);
2035
+ const rs = await Promise.all(batch.map((p) => host.request({ path: "/api/my/tag/query", method: "POST", body: bodyOf(p) }).catch(() => null)));
2036
+ for (const r of rs) {
2037
+ const list = r?.data?.data;
2038
+ if (Array.isArray(list)) rows.push(...list);
2039
+ }
2040
+ }
2041
+ }
2042
+ return rows;
2043
+ }
2044
+ async function listPlatformTags(host) {
2045
+ const all = [];
2046
+ for (const type of [0, 1]) {
2047
+ try {
2048
+ all.push(...await fetchTagsOfType(host, type));
2049
+ } catch {
2050
+ }
2051
+ }
2052
+ const seen = /* @__PURE__ */ new Set();
2053
+ return all.filter((t) => t && t.tagId && !seen.has(String(t.tagId)) && seen.add(String(t.tagId)));
2054
+ }
2055
+ async function resolveTags(host, names) {
2056
+ const norm = (s) => String(s == null ? "" : s).trim().toLowerCase();
2057
+ const byName = /* @__PURE__ */ new Map();
2058
+ const uniqNames = [...new Set((names || []).map((n) => String(n)).filter(Boolean))];
2059
+ let availableCount;
2060
+ if (!uniqNames.length) return { resolved: [], found: [], missing: [], available: [], availableCount };
2061
+ const probes = await Promise.all(uniqNames.map((n) => queryTagByName(host, n).catch(() => ({ items: [], filtered: true }))));
2062
+ const nameQueryUsable = probes.every((p) => p.filtered);
2063
+ if (nameQueryUsable) {
2064
+ for (const p of probes) {
2065
+ for (const t of p.items) {
2066
+ if (!t || !t.tagId) continue;
2067
+ const k = norm(t.name);
2068
+ const prev = byName.get(k);
2069
+ if (!prev) byName.set(k, t);
2070
+ else if ((prev.type ?? 0) !== 0 && t.type === 0) byName.set(k, t);
2071
+ }
2072
+ }
2073
+ } else {
2074
+ const dict = await listPlatformTags(host);
2075
+ availableCount = dict.length;
2076
+ for (const t of dict) {
2077
+ const k = norm(t.name);
2078
+ const prev = byName.get(k);
2079
+ if (!prev) byName.set(k, t);
2080
+ else if ((prev.type ?? 0) !== 0 && t.type === 0) byName.set(k, t);
2081
+ }
2082
+ }
2083
+ const resolved = [];
2084
+ const found = [];
2085
+ const missing = [];
2086
+ for (const n of names || []) {
2087
+ const t = byName.get(norm(n));
2088
+ if (!t) {
2089
+ missing.push(n);
2090
+ continue;
2091
+ }
2092
+ resolved.push({ id: String(t.tagId), name: t.name, type: t.type ?? 0 });
2093
+ found.push(t.name);
2094
+ }
2095
+ return {
2096
+ resolved,
2097
+ found,
2098
+ missing,
2099
+ availableCount,
2100
+ available: [...byName.values()].map((t) => ({ tagId: String(t.tagId), name: t.name, type: t.type ?? 0 }))
2101
+ };
2102
+ }
2103
+ async function addEntityTags(host, talentId, names) {
2104
+ if (!names || !names.length) return { applied: [], skipped: [] };
2105
+ const { resolved, found, missing } = await resolveTags(host, names);
2106
+ if (!resolved.length) return { applied: [], skipped: missing };
2107
+ const r = await host.request({
2108
+ path: "/api/my/tag/common/batchAddEntityTag",
2109
+ method: "POST",
2110
+ body: { tags: resolved, batchDatas: [{ entityId: String(talentId), module: "ebuilderform" }] }
2111
+ });
2112
+ if (r?.status === false) return { applied: [], skipped: [...missing, ...found], error: r?.msg };
2113
+ return { applied: found, skipped: missing };
2114
+ }
2115
+ async function queryEntityTags(host, talentId) {
2116
+ const out = [];
2117
+ for (const type of [0, 1]) {
2118
+ try {
2119
+ const r = await host.request({ path: "/api/my/tag/common/queryEntityTag", method: "POST", body: { entityId: String(talentId), module: "ebuilderform", type } });
2120
+ const list = r?.data;
2121
+ if (Array.isArray(list)) for (const t of list) out.push(t.content || t.name || t.labelName || String(t.id || ""));
2122
+ } catch {
2123
+ }
2124
+ }
2125
+ return [...new Set(out.filter(Boolean))];
2126
+ }
2127
+ function deriveSchoolTags(structured) {
2128
+ const cfg = school_tiers_default;
2129
+ const norm = (x) => String(x || "").replace(/[\s ]/g, "").replace(/[((][^))]*[))]/g, "").replace(/^(.*?(?:大学|学院|学校)).*(?:分校|校区)$/, "$1");
2130
+ const canon = (x) => String(cfg.aliases?.[norm(x)] || norm(x));
2131
+ const tiers = {
2132
+ "985": new Set(cfg["985"].map(canon)),
2133
+ "211": new Set(cfg["211"].map(canon)),
2134
+ "\u53CC\u4E00\u6D41": new Set(cfg["\u53CC\u4E00\u6D41"].map(canon))
2135
+ };
2136
+ const schools = [...new Set(((structured || {}).academicInformations || []).map((r) => String(r.school || "").trim()).filter(Boolean))];
2137
+ const candidates = [];
2138
+ const detail = [];
2139
+ const tierNames = ["985", "211", "\u53CC\u4E00\u6D41"];
2140
+ for (const sch of schools) {
2141
+ const c = canon(sch);
2142
+ const hit = tierNames.filter((t) => tiers[t].has(c));
2143
+ detail.push({ school: sch, tiers: hit });
2144
+ for (const t of hit) if (!candidates.includes(t)) candidates.push(t);
2145
+ if (sch && !candidates.includes(sch)) candidates.push(sch);
2146
+ }
2147
+ return { candidates, detail, schools };
2148
+ }
2149
+ function normalizeTextStructured(cache, s, warnings) {
2150
+ const raw = cache.dict?.raw || [];
2151
+ const byName = cache.dict?.by_name || {};
2152
+ const dictIdByName2 = (name) => {
2153
+ const k = String(name || "").trim();
2154
+ if (!k) return "";
2155
+ let ids = byName[k];
2156
+ if (!ids || !ids.length) ids = raw.filter((x) => x.selection_name === k).map((x) => x.option_id);
2157
+ const uniq = [...new Set((ids || []).map(String))];
2158
+ if (!uniq.length) {
2159
+ warnings.push("\u5B57\u5178\u672A\u627E\u5230\u300C" + k + "\u300D\u2192 \u8BE5\u5B57\u6BB5\u8DF3\u8FC7");
2160
+ return "";
2161
+ }
2162
+ if (uniq.length > 1) {
2163
+ warnings.push("\u5B57\u5178\u300C" + k + "\u300D\u547D\u4E2D " + uniq.length + " \u4E2A\u540C\u540D\u9009\u9879\uFF08\u6B67\u4E49\uFF09\u2192 \u8BE5\u5B57\u6BB5\u8DF3\u8FC7");
2164
+ return "";
2165
+ }
2166
+ return uniq[0];
2167
+ };
2168
+ const fixRows = (rows, keys) => {
2169
+ for (const r of rows || []) {
2170
+ if (!r || typeof r !== "object") continue;
2171
+ for (const k of keys) {
2172
+ const v = String(r[k] || "").trim();
2173
+ if (!v || /^\d{6,}$/.test(v)) continue;
2174
+ const id = dictIdByName2(v);
2175
+ if (id) r[k] = id;
2176
+ else delete r[k];
2177
+ }
2178
+ }
2179
+ };
2180
+ if (!s.gender && s.genderName) {
2181
+ const id = dictIdByName2(s.genderName);
2182
+ if (id) s.gender = id;
2183
+ }
2184
+ if (!s.highest_degree && s.highest_degree_name) {
2185
+ const id = dictIdByName2(s.highest_degree_name);
2186
+ if (id) s.highest_degree = id;
2187
+ }
2188
+ fixRows(s.academicInformations, ["edu_education", "degree"]);
2189
+ fixRows(s.jobInformations || s.workExperiences, ["work_type"]);
2190
+ fixRows(s.internshipExperiences, ["sx_work_type"]);
2191
+ fixRows(s.languageAbilities, ["mastery", "ls_ability", "literacy"]);
2192
+ return s;
2193
+ }
2194
+ async function verifySubRows(ctx, formId, objId, dataId) {
2195
+ const L = await getFormLayoutMeta(ctx.host, formId);
2196
+ const r = await ctx.host.request({
2197
+ path: "/api/ebuilder/form/form/core/getFormDataAndLayout",
2198
+ method: "POST",
2199
+ body: {
2200
+ terminalType: "PC",
2201
+ dataId,
2202
+ formId,
2203
+ module: "ebuilderform",
2204
+ newLayout: true,
2205
+ layoutId: L?.formLayout?.id,
2206
+ layoutMultiId: L?.formLayoutMultiple?.id,
2207
+ dataCrossLayout: false,
2208
+ permissionId: "",
2209
+ allReadOnly: true,
2210
+ initialDatas: [],
2211
+ dynamicData: false,
2212
+ cost: false,
2213
+ customParam: { objId, dataId }
2214
+ }
2215
+ });
2216
+ const dets = r?.data?.formData?.dataDetails || [];
2217
+ const seen = {};
2218
+ for (const d of dets) {
2219
+ if (!d.subFormId) continue;
2220
+ const k = String(d.subFormId);
2221
+ (seen[k] = seen[k] || /* @__PURE__ */ new Set()).add(String(d.dataIndex));
2222
+ }
2223
+ const rowsBySubFormId = {};
2224
+ for (const [k, s] of Object.entries(seen)) rowsBySubFormId[k] = s.size;
2225
+ return {
2226
+ cells: dets.filter((d) => d.subFormId).length,
2227
+ rowsBySubFormId,
2228
+ rowTotal: Object.values(rowsBySubFormId).reduce((a, b) => a + b, 0)
2229
+ };
2230
+ }
2231
+ async function loadStructured(ctx, args) {
2232
+ const warnings = [];
2233
+ if (args.structured !== void 0) {
2234
+ const s = typeof args.structured === "string" ? JSON.parse(args.structured) : args.structured;
2235
+ if (!s || typeof s !== "object") throw rsValidationError("structured_invalid", "structured \u5FC5\u987B\u662F JSON \u5BF9\u8C61");
2236
+ if (!String(s.telephone || "").trim() && !String(s.candidateName || "").trim()) {
2237
+ throw rsValidationError("structured_invalid", "\u7ED3\u6784\u5316\u7B80\u5386\u7F3A\u5C11 telephone \u4E0E candidateName\uFF08\u81F3\u5C11\u8981\u6709\u624B\u673A\u53F7\u6216\u59D3\u540D\uFF0C\u5426\u5219\u65E0\u6CD5\u67E5\u91CD\u5EFA\u6863\uFF09");
2238
+ }
2239
+ return { structured: normalizeTextStructured(ctx.cache, s, warnings), textMode: true, warnings };
2240
+ }
2241
+ if (args.structuredFile) {
2242
+ const p = path.resolve(String(args.structuredFile));
2243
+ const s = JSON.parse(fs.readFileSync(p, "utf8"));
2244
+ if (!s || typeof s !== "object") throw rsValidationError("structured_invalid", "structuredFile \u5185\u5BB9\u4E0D\u662F\u5408\u6CD5 JSON \u5BF9\u8C61");
2245
+ if (!String(s.telephone || "").trim() && !String(s.candidateName || "").trim()) {
2246
+ throw rsValidationError("structured_invalid", "\u7ED3\u6784\u5316\u7B80\u5386\u7F3A\u5C11 telephone \u4E0E candidateName");
2247
+ }
2248
+ return { structured: normalizeTextStructured(ctx.cache, s, warnings), textMode: true, warnings };
2249
+ }
2250
+ return { structured: null, textMode: false, warnings };
2251
+ }
2252
+ async function buildImport(ctx, args) {
2253
+ const { host, baseUrl, appId, cache } = ctx;
2254
+ const objId = objIdOf(cache, "talent_pool");
2255
+ const formId = formIdOf(cache, "talent_pool");
2256
+ const fields = cache.fields[objId] || {};
2257
+ if (!fields.candidate_name || !fields.telephone) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u4EBA\u624D\u8868\u5355\u5B57\u6BB5 candidate_name/telephone");
2258
+ const resumeFieldId = fields.original_resume || "937153853968539861";
2259
+ const { structured: loaded, textMode, warnings: structuredTextWarnings } = await loadStructured(ctx, args);
2260
+ let structured = loaded;
2261
+ let fileid = args.fileId ? String(args.fileId) : "";
2262
+ let fileName = null;
2263
+ if (!fileid && args.resumeFile) {
2264
+ const up = await uploadResume(host, baseUrl, path.resolve(String(args.resumeFile)), resumeFieldId, formId);
2265
+ fileid = up.fileid;
2266
+ fileName = up.name;
2267
+ }
2268
+ if (!textMode) {
2269
+ if (!fileid) throw rsFail("\u9700\u8981 resumeFile \u6216 fileId\uFF08\u9644\u4EF6\u5FC5\u987B\u4E0A\u4F20\uFF09\uFF1B\u89E3\u6790\u8BF7\u7531\u5927\u6A21\u578B\u6309 schema \u6574\u7406\u540E\u8D70 structured \u8F93\u5165");
2270
+ if (!args.useOcr) {
2271
+ throw rsFail("\u89E3\u6790\u9ED8\u8BA4\u7531\u5927\u6A21\u578B\u5B8C\u6210\uFF08structured \u8F93\u5165\uFF09\uFF0C\u4E0D\u81EA\u52A8\u8C03 OCR\u3002\u786E\u9700 OCR \u515C\u5E95\uFF08\u5982\u626B\u63CF\u4EF6\uFF09\u8BF7\u663E\u5F0F useOcr:true \u91CD\u8BD5\uFF08\u95F8\u95E8=checkResumeExtraction\uFF09\u3002");
2272
+ }
2273
+ structured = await extractResume(host, fileid);
2274
+ }
2275
+ const schoolTagInfo = args.noSchoolTags ? { candidates: [], detail: [], schools: [] } : deriveSchoolTags(structured);
2276
+ const tagNames = [...args.tags || []];
2277
+ for (const t of schoolTagInfo.candidates) if (!tagNames.includes(t)) tagNames.push(t);
2278
+ const dataDetails = mapToTalent(fields, structured);
2279
+ if (fileid) {
2280
+ dataDetails.push({ formField: { id: resumeFieldId }, dataOptions: [{ optionId: String(fileid), content: fileName || "\u7B80\u5386\u9644\u4EF6", type: "file" }] });
2281
+ }
2282
+ let channelLabel = null;
2283
+ if (args.channel) {
2284
+ let channelOption;
2285
+ for (const x of cache.dict?.raw || []) if (x.selection_name === args.channel) channelOption = x.option_id;
2286
+ if (!channelOption) throw rsFail("\u6E20\u9053\u300C" + args.channel + "\u300D\u672A\u5728\u5B57\u5178\u4E2D\u627E\u5230\uFF08\u4E0D\u5F97\u81C6\u9020 optionId\uFF09");
2287
+ dataDetails.push({ formField: { id: fields.channel }, dataOptions: [{ optionId: channelOption, content: args.channel }] });
2288
+ channelLabel = args.channel;
2289
+ } else if (fields.channel) {
2290
+ const byName = cache.dict?.by_name || {};
2291
+ const manualId = (byName["\u624B\u52A8\u5F55\u5165"] || byName["\u624B\u5DE5\u5F55\u5165"] || [])[0];
2292
+ if (!manualId) throw rsFail("\u6570\u636E\u5B57\u5178\u4E2D\u672A\u627E\u5230\u300C\u624B\u52A8\u5F55\u5165\u300D\uFF0C\u65E0\u6CD5\u56FA\u5B9A\u7B80\u5386\u6765\u6E90\uFF08\u4E0D\u5F97\u81C6\u9020 optionId\uFF09");
2293
+ dataDetails.push({ formField: { id: fields.channel }, dataOptions: [{ optionId: String(manualId), content: "\u624B\u52A8\u5F55\u5165" }] });
2294
+ channelLabel = "\u624B\u52A8\u5F55\u5165\uFF08\u56FA\u5B9A\uFF09";
2295
+ }
2296
+ if (fields.is_blacklist) {
2297
+ const opt = (cache.field_options?.[objId] || {})[fields.is_blacklist] || {};
2298
+ const noId = Object.keys(opt).find((k) => opt[k] === "\u5426");
2299
+ if (noId) dataDetails.push({ formField: { id: fields.is_blacklist }, dataOptions: [{ optionId: String(noId), content: "\u5426" }] });
2300
+ }
2301
+ let subBuild = { byTable: {}, stats: {} };
2302
+ let subMeta = {};
2303
+ let subError = null;
2304
+ const detailsPayload = {};
2305
+ const detailNodes = {};
2306
+ const unmappedTables = [];
2307
+ try {
2308
+ subMeta = await resolveSubTableMeta(ctx);
2309
+ subBuild = buildSubDetails(structured, subMeta, cache);
2310
+ const mapping = await getTableMapping(host, objId);
2311
+ const nodeOf = invertTableMapping(mapping);
2312
+ for (const [dk, rows] of Object.entries(subBuild.byTable)) {
2313
+ const sub = subMeta[dk];
2314
+ const node = sub ? nodeOf[String(sub.subFormId)] : null;
2315
+ if (!node) {
2316
+ unmappedTables.push(dk);
2317
+ continue;
2318
+ }
2319
+ detailsPayload[node] = rows;
2320
+ detailNodes[dk] = node;
2321
+ }
2322
+ } catch (e) {
2323
+ subError = e.message;
2324
+ }
2325
+ const subRowTotal = Object.values(subBuild.byTable).reduce((n, rows) => n + rows.length, 0);
2326
+ return {
2327
+ objId,
2328
+ formId,
2329
+ fields,
2330
+ fileid,
2331
+ fileName,
2332
+ structured,
2333
+ textMode,
2334
+ structuredTextWarnings,
2335
+ dataDetails,
2336
+ detailsPayload,
2337
+ detailNodes,
2338
+ unmappedTables,
2339
+ subError,
2340
+ subBuild,
2341
+ subRowTotal,
2342
+ subMeta,
2343
+ tagNames,
2344
+ schoolTagInfo,
2345
+ channelLabel,
2346
+ resumeFieldId
2347
+ };
2348
+ }
2349
+ async function findExistingTalents(ctx, opts) {
2350
+ const { objId, formId, fields, mobile } = opts;
2351
+ const blank = { checked: false, unique: true, byPhone: null, byPhoneError: void 0, formConflicts: { existing: [], unreadable: [] }, ids: [] };
2352
+ if (!mobile) return blank;
2353
+ const phoneTask = getCandidateByPhone(ctx.host, mobile).then(
2354
+ (b) => ({ ok: true, b }),
2355
+ (e) => ({ ok: false, e })
2356
+ );
2357
+ const uniqueTask = fields.telephone ? checkUnique(ctx.host, { objId, fieldId: fields.telephone, value: mobile }).then(
2358
+ (r) => ({ ok: true, r }),
2359
+ () => ({ ok: false, r: null })
2360
+ ) : Promise.resolve({ ok: false, r: null });
2361
+ const [phoneRes, uniqueRes] = await Promise.all([phoneTask, uniqueTask]);
2362
+ let byPhone = null;
2363
+ if (phoneRes.ok) {
2364
+ const b = phoneRes.b;
2365
+ if (b.candidateId || b.talentId) {
2366
+ byPhone = {
2367
+ talentId: b.talentId,
2368
+ candidateId: b.candidateId,
2369
+ talentViewUrl: b.candidateId ? buildTalentViewUrl(ctx.baseUrl, b.candidateId) : null,
2370
+ talentCardUrl: b.talentId ? resolveTalentCardUrl(ctx, b.talentId) : null,
2371
+ talentListLocateUrl: b.talentId ? resolveTalentListLocateUrl(ctx, b.talentId) : null
2372
+ };
2373
+ }
2374
+ } else {
2375
+ byPhone = { error: phoneRes.e?.message };
2376
+ }
2377
+ const formConflicts = { existing: [], unreadable: [] };
2378
+ let ids = [];
2379
+ if (uniqueRes.ok && uniqueRes.r) {
2380
+ const r = uniqueRes.r;
2381
+ ids = r.formDataIds;
2382
+ const readBrief = makeBriefReader(ctx, formId, objId, fields);
2383
+ for (const id of ids) {
2384
+ if (byPhone?.talentId === id) continue;
2385
+ const rec = await readBrief(id);
2386
+ if (rec) {
2387
+ formConflicts.existing.push({
2388
+ talentId: id,
2389
+ ...rec,
2390
+ talentCardUrl: resolveTalentCardUrl(ctx, id),
2391
+ talentListLocateUrl: resolveTalentListLocateUrl(ctx, id),
2392
+ recordCardUrl: buildCardUrl(ctx.baseUrl, objId, id)
2393
+ });
2394
+ } else formConflicts.unreadable.push(id);
2395
+ }
2396
+ }
2397
+ const hit = Boolean(byPhone?.candidateId || byPhone?.talentId) || formConflicts.existing.length > 0;
2398
+ return { checked: true, unique: !hit, byPhone: byPhone && !byPhone.error ? byPhone : null, byPhoneError: byPhone?.error, formConflicts, ids };
2399
+ }
2400
+ function fingerprint2(value) {
2401
+ return createHash2("sha256").update(JSON.stringify(value)).digest("hex");
2402
+ }
2403
+ function fingerprintMainTable(mainTable) {
2404
+ const copy = { ...mainTable };
2405
+ delete copy.original_resume;
2406
+ return copy;
2407
+ }
2408
+ var baseInput2 = {
2409
+ structured: { type: "object", description: "\u5927\u6A21\u578B\u6309 schema \u6574\u7406\u7684\u7ED3\u6784\u5316\u7B80\u5386\u5BF9\u8C61\uFF08\u89E3\u6790\u4E3B\u901A\u9053\uFF1B\u4E0E structuredFile \u4E8C\u9009\u4E00\uFF09" },
2410
+ structuredFile: { type: "string", description: "\u7ED3\u6784\u5316\u7B80\u5386 JSON \u6587\u4EF6\u8DEF\u5F84\uFF08\u517C\u5BB9\u811A\u672C\u7248\u7528\u6CD5\uFF09" },
2411
+ fileId: { type: "string", description: "\u5DF2\u4E0A\u4F20\u5230 E10 \u7684\u7B80\u5386\u9644\u4EF6 id\uFF08\u539F\u4EF6\u5199\u8FDB\u9644\u4EF6\u5B57\u6BB5\uFF09" },
2412
+ resumeFile: { type: "string", description: "\u672C\u5730\u7B80\u5386\u6587\u4EF6\u8DEF\u5F84\uFF08\u4E24\u6B65 multipart \u4E0A\u4F20\uFF09" },
2413
+ channel: { type: "string", description: "\u7B80\u5386\u6E20\u9053\uFF08\u987B\u5728\u6570\u636E\u5B57\u5178\u4E2D\u5B58\u5728\uFF1B\u7F3A\u7701\u56FA\u5B9A\u300C\u624B\u52A8\u5F55\u5165\u300D\uFF09" },
2414
+ tags: { type: "array", items: { type: "string" }, maxItems: 20, description: "\u5E73\u53F0\u6807\u7B7E\u540D\uFF08\u6309\u540D\u547D\u4E2D\u73AF\u5883\u6E05\u5355\uFF0C\u4E0D\u5B58\u5728\u8DF3\u8FC7\uFF1B\u5B66\u6821\u5F52\u5C5E\u81EA\u52A8\u63A8\u5BFC\u53E6\u7B97\uFF09" },
2415
+ useOcr: { type: "boolean", description: "\u663E\u5F0F\u5F00\u542F OCR \u515C\u5E95\u89E3\u6790\uFF08\u95F8\u95E8=checkResumeExtraction\uFF1B\u9ED8\u8BA4\u6C38\u4E0D\u4E0A\u8C03\uFF09" },
2416
+ noSchoolTags: { type: "boolean", description: "\u5173\u95ED\u5B66\u6821\u5F52\u5C5E\u6807\u7B7E\u81EA\u52A8\u63A8\u5BFC" },
2417
+ updateTalentId: { type: "string", description: "\u6309\u7B80\u5386\u66F4\u65B0\u5DF2\u6709\u4EBA\u624D\uFF08\u4E3B\u8868\u5168\u91CF\u8986\u76D6+\u660E\u7EC6\u6574\u8868\u8986\u76D6\uFF1B\u5148 prepare \u770B\u5DEE\u5F02\uFF09" }
2418
+ };
2419
+ async function prepare2(args, env) {
2420
+ const host = env.host;
2421
+ const session = await host.getSession();
2422
+ const ctx = { host, baseUrl: session.baseUrl.replace(/\/+$/, ""), appId: "", cache: await env.recruitContext() };
2423
+ ctx.appId = ctx.cache.appId;
2424
+ const argsT = args;
2425
+ if (!argsT.structured && !argsT.structuredFile && !argsT.fileId && !argsT.resumeFile) {
2426
+ throw rsValidationError("input_required", "\u9700\u8981 structured/structuredFile/fileId/resumeFile \u4E4B\u4E00");
2427
+ }
2428
+ const built = await buildImport(ctx, argsT);
2429
+ if (argsT.updateTalentId) {
2430
+ const updateTalentId = String(argsT.updateTalentId);
2431
+ const snapshot = await readTalentSnapshot(ctx, { formId: built.formId, objId: built.objId, fields: built.fields, subMeta: built.subMeta, dataId: updateTalentId });
2432
+ const upMain = toDatasetMainTable(built.dataDetails, built.fields);
2433
+ const protectedSkipped = [];
2434
+ for (const k of PROTECTED_UPDATE_KEYS) {
2435
+ if (k in upMain) {
2436
+ delete upMain[k];
2437
+ protectedSkipped.push(k);
2438
+ }
2439
+ }
2440
+ const dictName = makeDictLookup(ctx.cache);
2441
+ const fo = (ctx.cache.field_options || {})[built.objId] || {};
2442
+ const displayOf = (key, v) => {
2443
+ const s = String(v ?? "");
2444
+ if (key === "original_resume") return s ? "\uFF08\u9644\u4EF6\uFF09" : "";
2445
+ if (!/^\d{6,}$/.test(s)) return s;
2446
+ const dn = dictName(s);
2447
+ if (dn) return dn;
2448
+ const map = built.fields[key] ? fo[built.fields[key]] : null;
2449
+ if (map && map[s]) return map[s];
2450
+ return s;
2451
+ };
2452
+ const fieldDiff = Object.entries(upMain).map(([k, v]) => {
2453
+ const old = snapshot.main[k] ?? "";
2454
+ const newDisplay = displayOf(k, v);
2455
+ const same = String(old) === newDisplay;
2456
+ return {
2457
+ field: k,
2458
+ old,
2459
+ new: newDisplay,
2460
+ rawValue: String(v),
2461
+ change: same ? "same" : k === "original_resume" ? "replace-attachment" : old === "" ? "fill" : "overwrite"
2462
+ };
2463
+ });
2464
+ const changed = fieldDiff.filter((x) => x.change !== "same");
2465
+ const detailDiff = Object.entries(built.detailsPayload).map(([node, rows]) => {
2466
+ const dk = Object.keys(built.detailNodes).find((k) => built.detailNodes[k] === node) || node;
2467
+ return { table: dk, node, oldRows: snapshot.subRows[dk] || 0, newRows: rows.length, action: "cover" };
2468
+ });
2469
+ const coveredKeys = new Set(Object.keys(built.detailNodes));
2470
+ const keptTables = Object.keys(built.subMeta).filter((k) => !coveredKeys.has(k) || !(built.detailNodes[k] in built.detailsPayload));
2471
+ let viewUrl = null;
2472
+ try {
2473
+ const b = await getCandidateByPhone(ctx.host, built.structured.telephone || "");
2474
+ if (b.candidateId) viewUrl = buildTalentViewUrl(ctx.baseUrl, b.candidateId);
2475
+ } catch {
2476
+ }
2477
+ const willSubmit2 = { updateTalentId, mainTable: fingerprintMainTable(upMain), details: built.detailsPayload };
2478
+ const fp2 = fingerprint2(willSubmit2);
2479
+ const continuation2 = await env.continuation.issue({ operation: "recruit-stage.resume.import.apply", fingerprint: fp2 });
2480
+ return rsSuccess("recruit-stage.resume.import.prepare", {
2481
+ mode: "prepare-update",
2482
+ action: "\u6309\u7B80\u5386\u66F4\u65B0\u5DF2\u6709\u4EBA\u624D",
2483
+ talentId: updateTalentId,
2484
+ strategy: { main: "B \u5168\u91CF\u8986\u76D6\uFF08\u7B80\u5386\u89E3\u6790\u51FA\u7684\u5B57\u6BB5\u5168\u90E8\u5199\u5165\uFF09", details: "\u6574\u8868\u8986\u76D6\uFF08\u4EC5\u8986\u76D6\u7B80\u5386\u89E3\u6790\u51FA\u6570\u636E\u7684\u8868\uFF09" },
2485
+ changes: changed,
2486
+ unchanged: fieldDiff.filter((x) => x.change === "same").map((x) => x.field),
2487
+ details: detailDiff,
2488
+ detailPreview: Object.fromEntries(Object.entries(built.detailsPayload).map(([n, rows]) => [n, rows[0]])),
2489
+ keptTables,
2490
+ protectedSkipped,
2491
+ talentViewUrl: viewUrl,
2492
+ note: "\u4EE5\u4E0A\u4E3A\u5C06\u5199\u5165\u7684\u5DEE\u5F02\u3002\u786E\u8BA4\u540E apply \u6267\u884C\uFF1B\u5168\u91CF\u8986\u76D6\u4F1A\u6539\u6389\u4EBA\u5DE5\u4FEE\u6B63\u8FC7\u7684\u503C"
2493
+ }, { continuation: continuation2, fingerprint: fp2, confirmHint: "\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u643A\u5E26 confirm:true \u4E0E continuation \u8C03\u7528 apply" });
2494
+ }
2495
+ const tagCheckTask = (async () => {
2496
+ if (!built.tagNames.length) return null;
2497
+ try {
2498
+ const rt = await resolveTags(ctx.host, built.tagNames);
2499
+ return { requested: built.tagNames, found: rt.found, missing: rt.missing, ...typeof rt.availableCount === "number" ? { availableCount: rt.availableCount } : {} };
2500
+ } catch (e) {
2501
+ return { requested: built.tagNames, error: e.message };
2502
+ }
2503
+ })();
2504
+ const [dupInfo, tagCheck] = await Promise.all([
2505
+ findExistingTalents(ctx, { objId: built.objId, formId: built.formId, fields: built.fields, mobile: built.structured.telephone }),
2506
+ tagCheckTask
2507
+ ]);
2508
+ const summary = {
2509
+ action: "\u7B80\u5386\u5BFC\u5165\u5EFA\u4EBA\u624D",
2510
+ resumeFile: built.fileName || "fileid=" + built.fileid,
2511
+ // 供 apply 复用:传 fileId 即可跳过二次上传(附件已在本环境,省一次 multipart)
2512
+ resumeFileId: built.fileid || void 0,
2513
+ parsed: {
2514
+ name: built.structured.candidateName,
2515
+ mobile: built.structured.telephone,
2516
+ email: built.structured.email || "(\u7A7A)",
2517
+ gender: built.structured.genderName,
2518
+ birthday: built.structured.birthday,
2519
+ age: built.structured.age,
2520
+ degree: built.structured.highest_degree_name,
2521
+ college: built.structured.college,
2522
+ joinworkdate: built.structured.joinworkdate,
2523
+ intendPosition: built.structured.intend_position,
2524
+ jobCount: (built.structured.jobInformations || []).length,
2525
+ projectCount: (built.structured.projectExperiences || []).length
2526
+ },
2527
+ mappedFieldCount: built.dataDetails.length,
2528
+ channel: built.channelLabel || "(\u672A\u6307\u5B9A)",
2529
+ subTables: built.subBuild.stats,
2530
+ subTableRows: built.subRowTotal,
2531
+ subTableNodes: built.detailNodes,
2532
+ subTableUnmapped: built.unmappedTables.length ? built.unmappedTables : void 0,
2533
+ subTableError: built.subError || void 0,
2534
+ structuredWarnings: built.structuredTextWarnings.length ? built.structuredTextWarnings : void 0,
2535
+ duplicate: {
2536
+ checked: dupInfo.checked,
2537
+ unique: dupInfo.unique,
2538
+ bound: dupInfo.byPhone,
2539
+ formConflicts: dupInfo.formConflicts.existing,
2540
+ unreadableIds: dupInfo.formConflicts.unreadable,
2541
+ byPhoneError: dupInfo.byPhoneError,
2542
+ note: dupInfo.byPhone ? "\u624B\u673A\u53F7\u5DF2\u7ED1\u5B9A\u4EBA\u624D\uFF08\u9875\u9762\u540C\u6B3E getCandidateByPhone \u547D\u4E2D\uFF09\u2192 \u4E0D\u65B0\u5EFA\uFF0C\u7ED9\u51FA\u4EBA\u624D\u67E5\u770B\u9875\u5730\u5740" : dupInfo.formConflicts.existing.length ? "\u624B\u673A\u53F7\u5728\u4EBA\u624D\u5E93\u5DF2\u5B58\u5728\uFF08\u8868\u5355\u552F\u4E00\u6027\u547D\u4E2D\uFF0C\u4F46\u65E0\u5E94\u8058\u6279\u6B21\uFF09\u2192 \u4E0D\u65B0\u5EFA" : dupInfo.formConflicts.unreadable.length ? "\u624B\u673A\u53F7\u88AB\u5DF2\u5220\u9664\u8BB0\u5F55\u5360\u7528\uFF08\u5E73\u53F0\u4ECD\u4F1A\u62E6\u65B0\u5EFA\uFF09\uFF1A\u9700\u5148\u5728\u56DE\u6536\u7AD9\u6E05\u7406\uFF0C\u6216\u6539\u4E3A\u66F4\u65B0\u5DF2\u6709\u4EBA\u624D" : "\u65E0\u91CD\u590D"
2543
+ }
2544
+ };
2545
+ const willSubmit = { formId: built.formId, objId: built.objId, mainTable: fingerprintMainTable(toDatasetMainTable(built.dataDetails, built.fields)), details: built.detailsPayload };
2546
+ const fp = fingerprint2(willSubmit);
2547
+ const continuation = await env.continuation.issue({ operation: "recruit-stage.resume.import.apply", fingerprint: fp });
2548
+ return rsSuccess("recruit-stage.resume.import.prepare", {
2549
+ mode: "prepare",
2550
+ ...summary,
2551
+ willSubmit,
2552
+ tagCheck,
2553
+ schoolTags: built.schoolTagInfo,
2554
+ onCommit: "\u63D0\u4EA4\u540E\u5C06\uFF1A\u2460 dataset/v2/saveFormData \u5EFA\u4EBA\u624D\uFF08\u4E3B\u8868+\u660E\u7EC6\u4E00\u6B21\u5199\u5165\uFF09\u2461 \u521D\u59CB\u5316\u4EBA\u624D\u6279\u6B21 \u2462 \u89E6\u53D1\u4EBA\u624D\u81EA\u52A8\u6807\u7B7E\u89C4\u5219\u5339\u914D \u2463 \u6309\u6807\u7B7E\uFF08\u542B\u5B66\u6821\u5F52\u5C5E\u81EA\u52A8\u63A8\u5BFC\uFF09\u5199\u5165\u5E73\u53F0\u6807\u7B7E \u2464 \u8F93\u51FA\u4EBA\u624D\u5361\u7247/\u4EBA\u624D\u5E93\u5B9A\u4F4D\u94FE\u63A5"
2555
+ }, { continuation, fingerprint: fp, confirmHint: "\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u643A\u5E26 confirm:true \u4E0E continuation \u8C03\u7528 apply" });
2556
+ }
2557
+ async function apply2(args, env) {
2558
+ if (args.confirm !== true) throw rsConfirmationError("recruit-stage.resume.import.apply", { hint: "\u5199\u64CD\u4F5C\u5FC5\u987B\u663E\u5F0F confirm:true" });
2559
+ if (!args.continuation) throw rsValidationError("continuation_required", "\u7F3A\u5C11 continuation\uFF08\u6765\u81EA prepare \u7684 meta\uFF09");
2560
+ const payload = await env.continuation.verify(String(args.continuation), "recruit-stage.resume.import.apply");
2561
+ const host = env.host;
2562
+ const session = await host.getSession();
2563
+ const ctx = { host, baseUrl: session.baseUrl.replace(/\/+$/, ""), appId: "", cache: await env.recruitContext() };
2564
+ ctx.appId = ctx.cache.appId;
2565
+ const argsT = args;
2566
+ const built = await buildImport(ctx, argsT);
2567
+ if (argsT.updateTalentId) {
2568
+ const updateTalentId = String(argsT.updateTalentId);
2569
+ const upMain = toDatasetMainTable(built.dataDetails, built.fields);
2570
+ for (const k of PROTECTED_UPDATE_KEYS) delete upMain[k];
2571
+ const willSubmit = { updateTalentId, mainTable: fingerprintMainTable(upMain), details: built.detailsPayload };
2572
+ if (payload.fingerprint !== fingerprint2(willSubmit)) {
2573
+ throw rsValidationError("fingerprint_mismatch", "\u5F85\u63D0\u4EA4\u5185\u5BB9\u4E0E prepare \u786E\u8BA4\u6458\u8981\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 prepare \u786E\u8BA4\u540E\u518D apply");
2574
+ }
2575
+ await datasetUpdateFormData(host, {
2576
+ objId: built.objId,
2577
+ dataId: updateTalentId,
2578
+ mainTable: upMain,
2579
+ details: built.detailsPayload,
2580
+ coverDetails: Object.keys(built.detailsPayload)
2581
+ });
2582
+ const checkAfter = (snap) => {
2583
+ const out = [];
2584
+ for (const [node, rows] of Object.entries(built.detailsPayload)) {
2585
+ const dk = Object.keys(built.detailNodes).find((k) => built.detailNodes[k] === node) || node;
2586
+ const wantCells = rows.reduce((n, r) => n + Object.keys(r).length, 0);
2587
+ const gotRows = snap.subRows[dk] || 0;
2588
+ const gotCells = snap.subCells[dk] || 0;
2589
+ if (gotRows !== rows.length || gotCells !== wantCells) {
2590
+ out.push(`${dk}\uFF1A\u671F\u671B ${rows.length} \u884C/${wantCells} \u683C\uFF0C\u8BFB\u56DE ${gotRows} \u884C/${gotCells} \u683C`);
2591
+ }
2592
+ }
2593
+ return out;
2594
+ };
2595
+ let after = await readTalentSnapshot(ctx, { formId: built.formId, objId: built.objId, fields: built.fields, subMeta: built.subMeta, dataId: updateTalentId });
2596
+ let verifyWarnings = checkAfter(after);
2597
+ if (verifyWarnings.length) {
2598
+ await new Promise((r) => setTimeout(r, 600));
2599
+ after = await readTalentSnapshot(ctx, { formId: built.formId, objId: built.objId, fields: built.fields, subMeta: built.subMeta, dataId: updateTalentId });
2600
+ verifyWarnings = checkAfter(after);
2601
+ }
2602
+ return rsSuccess("recruit-stage.resume.import.apply", {
2603
+ mode: "update",
2604
+ result: "success",
2605
+ talentId: updateTalentId,
2606
+ applied: verifyWarnings,
2607
+ // placeholder replaced below
2608
+ afterSummary: { mainFieldCount: Object.keys(after.main).length, subRows: after.subRows, subCells: after.subCells },
2609
+ verifyWarnings: verifyWarnings.length ? verifyWarnings : void 0,
2610
+ talentCardUrl: resolveTalentCardUrl(ctx, updateTalentId),
2611
+ talentListLocateUrl: resolveTalentListLocateUrl(ctx, updateTalentId),
2612
+ parsed: { name: built.structured.candidateName, mobile: built.structured.telephone, email: built.structured.email || "(\u7A7A)" }
2613
+ });
2614
+ }
2615
+ const dupInfo = await findExistingTalents(ctx, { objId: built.objId, formId: built.formId, fields: built.fields, mobile: built.structured.telephone });
2616
+ if (dupInfo.byPhone?.candidateId || dupInfo.byPhone?.talentId) {
2617
+ const b = dupInfo.byPhone;
2618
+ return rsSuccess("recruit-stage.resume.import.apply", {
2619
+ mode: "apply",
2620
+ result: "duplicate",
2621
+ message: "\u5F53\u524D\u624B\u673A\u53F7\u5DF2\u7ED1\u5B9A\u4EBA\u624D\u4FE1\u606F\uFF0C\u6309\u9875\u9762\u89C4\u5219\u4E0D\u65B0\u5EFA\uFF08\u672A\u5199\u5165\u4EFB\u4F55\u6570\u636E\uFF09",
2622
+ existingTalent: { talentId: b.talentId, candidateId: b.candidateId, talentViewUrl: b.talentViewUrl, talentCardUrl: b.talentCardUrl },
2623
+ talentViewUrl: b.talentViewUrl,
2624
+ otherRecords: dupInfo.formConflicts.existing.length ? dupInfo.formConflicts.existing : void 0,
2625
+ parsed: { name: built.structured.candidateName, mobile: built.structured.telephone },
2626
+ nextStep: "\u5982\u9700\u6309\u8FD9\u4EFD\u7B80\u5386\u66F4\u65B0\u8BE5\u4EBA\u624D\uFF0C\u660E\u786E\u8BF4\u660E\u5373\u53EF\uFF08updateTalentId \u8D70\u66F4\u65B0\u901A\u9053\uFF09"
2627
+ });
2628
+ }
2629
+ if (dupInfo.formConflicts.existing.length) {
2630
+ return rsSuccess("recruit-stage.resume.import.apply", {
2631
+ mode: "apply",
2632
+ result: "duplicate",
2633
+ message: "\u624B\u673A\u53F7\u5728\u4EBA\u624D\u5E93\u5DF2\u5B58\u5728\uFF08\u8868\u5355\u552F\u4E00\u6027\u547D\u4E2D\uFF0C\u4F46\u65E0\u5E94\u8058\u6279\u6B21\uFF09\uFF0C\u4E0D\u65B0\u5EFA\uFF08\u672A\u5199\u5165\u4EFB\u4F55\u6570\u636E\uFF09",
2634
+ formConflicts: dupInfo.formConflicts.existing,
2635
+ parsed: { name: built.structured.candidateName, mobile: built.structured.telephone },
2636
+ nextStep: "\u5982\u9700\u6309\u8FD9\u4EFD\u7B80\u5386\u66F4\u65B0\u8BE5\u4EBA\u624D\uFF0C\u660E\u786E\u8BF4\u660E\u5373\u53EF\uFF08updateTalentId \u8D70\u66F4\u65B0\u901A\u9053\uFF09"
2637
+ });
2638
+ }
2639
+ if (!dupInfo.unique && dupInfo.formConflicts.unreadable.length) {
2640
+ throw rsFail("\u624B\u673A\u53F7\u88AB " + dupInfo.formConflicts.unreadable.length + " \u6761\u5DF2\u5220\u9664\u8BB0\u5F55\u5360\u7528\uFF08\u5E73\u53F0\u552F\u4E00\u6027\u6821\u9A8C\u4ECD\u62E6\u65B0\u5EFA\uFF0Cid=" + dupInfo.formConflicts.unreadable.join(",") + "\uFF09\uFF1A\u8BF7\u5148\u5728\u56DE\u6536\u7AD9\u6E05\u7406\uFF0C\u6216\u6539\u4E3A\u66F4\u65B0\u5DF2\u6709\u4EBA\u624D");
2641
+ }
2642
+ const datasetMain = toDatasetMainTable(built.dataDetails, built.fields);
2643
+ let newId = "";
2644
+ let writeChannel = "";
2645
+ const writeWarnings = [];
2646
+ try {
2647
+ const created = await datasetSaveFormData(host, { objId: built.objId, mainTable: datasetMain, details: built.detailsPayload });
2648
+ newId = created.dataId;
2649
+ writeChannel = "dataset/v2/saveFormData\uFF08\u4E3B\u8868 + \u660E\u7EC6\u4E00\u6B21\u5199\u5165\uFF09";
2650
+ } catch (e) {
2651
+ writeWarnings.push("\u6570\u636E\u96C6\u65B0\u589E\u672A\u901A\u8FC7\uFF08" + e.message + "\uFF09\uFF0C\u6539\u4E3A\u5EFA\u4E3B\u8868\u540E\u7528\u6570\u636E\u96C6 update \u8865\u660E\u7EC6");
2652
+ newId = await saveFormData(host, { appId: ctx.appId, formId: built.formId, objId: built.objId, name: built.structured.candidateName || "\u7B80\u5386\u5BFC\u5165", dataDetails: built.dataDetails });
2653
+ writeChannel = "form/core/saveFormData\uFF08\u4E3B\u8868\uFF09+ dataset/v2/updateFormData\uFF08\u660E\u7EC6\uFF09";
2654
+ if (Object.keys(built.detailsPayload).length) {
2655
+ try {
2656
+ await datasetUpdateFormData(host, { objId: built.objId, dataId: newId, mainTable: {}, details: built.detailsPayload });
2657
+ } catch (e2) {
2658
+ writeWarnings.push("\u56DE\u9000\u540E\u8865\u660E\u7EC6\u5931\u8D25\uFF1A" + e2.message);
2659
+ }
2660
+ }
2661
+ }
2662
+ const r2 = await host.request({ path: "/api/recruit/candidate/initTalentBatch?formDataId=" + newId, method: "POST" });
2663
+ const talentId = String(newId);
2664
+ const [verifyRes, esbRes, labelRes, tagRes] = await Promise.all([
2665
+ verifySubRows(ctx, built.formId, built.objId, newId).then(
2666
+ (v) => ({ ok: true, v }),
2667
+ (e) => ({ ok: false, e })
2668
+ ),
2669
+ runSaveButtonEsbActions(host, { objId: built.objId, formId: built.formId, dataId: newId }).then(
2670
+ (v) => ({ ok: true, v }),
2671
+ (e) => ({ ok: false, e })
2672
+ ),
2673
+ matchTalentLabel(host, talentId).then(
2674
+ (v) => ({ ok: true, v }),
2675
+ (e) => ({ ok: false, e })
2676
+ ),
2677
+ addEntityTags(host, talentId, built.tagNames).then(
2678
+ (v) => ({ ok: true, v }),
2679
+ (e) => ({ ok: false, e })
2680
+ )
2681
+ ]);
2682
+ const subTableVerify = verifyRes.ok ? verifyRes.v : { error: verifyRes.e?.message };
2683
+ if (subTableVerify && subTableVerify.rowTotal === 0 && built.subRowTotal > 0) {
2684
+ writeWarnings.push("\u660E\u7EC6\u884C\u672A\u843D\u5E93\uFF08\u671F\u671B " + built.subRowTotal + " \u884C\uFF0C\u8BFB\u56DE 0 \u884C\uFF09");
2685
+ }
2686
+ const saveEsbActions = esbRes.ok ? esbRes.v : { error: esbRes.e?.message };
2687
+ const labelMatch = labelRes.ok ? labelRes.v : "ERR:" + (labelRes.e?.message || "");
2688
+ const tagResult = tagRes.ok ? tagRes.v : { applied: [], skipped: built.tagNames, error: tagRes.e?.message };
2689
+ const tags = await queryEntityTags(host, talentId).catch(() => []);
2690
+ const talentCardUrl = resolveTalentCardUrl(ctx, talentId);
2691
+ const talentListLocateUrl = resolveTalentListLocateUrl(ctx, talentId);
2692
+ return rsSuccess("recruit-stage.resume.import.apply", {
2693
+ mode: "apply",
2694
+ result: "success",
2695
+ newCandidateFormDataId: newId,
2696
+ talentId,
2697
+ writeChannel,
2698
+ writeWarnings: writeWarnings.length ? writeWarnings : void 0,
2699
+ subTablesExpected: built.subBuild.stats,
2700
+ subTableVerify,
2701
+ initTalentBatch: r2?.data?.msg || "",
2702
+ saveEsbActions,
2703
+ schoolTags: built.schoolTagInfo,
2704
+ labelMatch,
2705
+ tagsApplied: tagResult.applied,
2706
+ tagsSkipped: tagResult.skipped,
2707
+ tags,
2708
+ talentCardUrl,
2709
+ talentListLocateUrl,
2710
+ // 产物链接(Agent 原样搬运:① 人才卡片 ② 人才库定位;缺失哪个只给另一个)
2711
+ viewLink: talentCardUrl ? `[\u67E5\u770B ${built.structured.candidateName || "\u65B0\u4EBA\u624D"} \u7684\u4EBA\u624D\u5361\u7247](${talentCardUrl})` : null,
2712
+ listLocateLink: talentListLocateUrl ? `[\u5728\u4EBA\u624D\u5E93\u4E2D\u6253\u5F00](${talentListLocateUrl})` : null,
2713
+ agentNote: "\u6B63\u6587\u7ED9\u4E24\u4E2A Markdown \u94FE\u63A5\u2014\u2014\u2460 [\u67E5\u770B <\u59D3\u540D> \u7684\u4EBA\u624D\u5361\u7247](talentCardUrl) \u2461 [\u5728\u4EBA\u624D\u5E93\u4E2D\u6253\u5F00](talentListLocateUrl)\uFF1B\u7F3A\u5931\u54EA\u4E2A\u5C31\u53EA\u7ED9\u53E6\u4E00\u4E2A\uFF0C\u4E0D\u8F93\u51FA\u534A\u622A\u3002\u4E0D\u8981\u7528 artifact / browsertool\u3002",
2714
+ parsed: {
2715
+ name: built.structured.candidateName,
2716
+ mobile: built.structured.telephone,
2717
+ email: built.structured.email || "(\u7A7A)",
2718
+ gender: built.structured.genderName,
2719
+ degree: built.structured.highest_degree_name,
2720
+ college: built.structured.college
2721
+ }
2722
+ });
2723
+ }
2724
+ var resumeImportOperations = [
2725
+ {
2726
+ name: "recruit-stage.resume.import.prepare",
2727
+ resource: "recruit.resume",
2728
+ method: "prepare",
2729
+ action: "resume.import.prepare",
2730
+ summary: "\u7B80\u5386\u5BFC\u5165\u786E\u8BA4\u6458\u8981\uFF1A\u89E3\u6790\u7ED3\u679C+\u5B57\u6BB5\u6620\u5C04+\u4E24\u7EA7\u67E5\u91CD\uFF08\u9875\u9762\u540C\u6B3E\uFF09+\u6807\u7B7E\u9884\u6838\uFF08\u542B\u5B66\u6821\u5F52\u5C5E\u81EA\u52A8\u63A8\u5BFC\uFF09\uFF1B\u66F4\u65B0\u6A21\u5F0F\u51FA\u5B57\u6BB5/\u660E\u7EC6\u5DEE\u5F02\uFF1B\u4E0D\u5199\u5165",
2731
+ capability: "recruit.resume.import",
2732
+ risk: "write",
2733
+ requiresConfirmation: true,
2734
+ inputSchema: schemaOf(baseInput2, {}),
2735
+ handler: prepare2
2736
+ },
2737
+ {
2738
+ name: "recruit-stage.resume.import.apply",
2739
+ resource: "recruit.resume",
2740
+ method: "apply",
2741
+ action: "resume.import.apply",
2742
+ summary: "\u7528\u6237\u786E\u8BA4\u540E\u5EFA\u6863\uFF1A\u6570\u636E\u96C6\u901A\u9053\u4E3B\u8868+\u660E\u7EC6\u4E00\u6B21\u5199\u5165\uFF08\u88AB\u62E6\u56DE\u9000\uFF09\u3001\u521D\u59CB\u5316\u6279\u6B21\u3001\u89E6\u53D1\u89C4\u5219\u6807\u7B7E\u3001\u5199\u5E73\u53F0\u6807\u7B7E\u3001\u8BFB\u56DE\u6821\u9A8C\uFF1B\u8FD4\u56DE\u4EBA\u624D\u5361\u7247/\u4EBA\u624D\u5E93\u53CC\u94FE\u63A5",
2743
+ capability: "recruit.resume.import",
2744
+ risk: "write",
2745
+ requiresConfirmation: true,
2746
+ inputSchema: schemaOf({ ...baseInput2, confirm: { type: "boolean" }, continuation: { type: "string" } }, {}),
2747
+ handler: apply2
2748
+ }
2749
+ ];
2750
+
2751
+ // src/shortcuts/recruit-stage/operations/interview-arrange.ts
2752
+ var DATAKEYS = [
2753
+ "subject",
2754
+ "start_time",
2755
+ "end_time",
2756
+ "interview_type",
2757
+ "interview_session",
2758
+ "interviewer",
2759
+ "position_manage_id",
2760
+ "talent_id",
2761
+ "candidate_manage_id",
2762
+ "mobile",
2763
+ "email",
2764
+ "interview_location",
2765
+ "notice_mould"
2766
+ ];
2767
+ var FIELD_LABELS = {
2768
+ subject: "\u9762\u8BD5\u4E3B\u9898",
2769
+ start_time: "\u5F00\u59CB\u65F6\u95F4",
2770
+ end_time: "\u7ED3\u675F\u65F6\u95F4",
2771
+ interview_type: "\u9762\u8BD5\u65B9\u5F0F",
2772
+ interview_session: "\u9762\u8BD5\u8F6E\u6B21",
2773
+ interviewer: "\u9762\u8BD5\u5B98",
2774
+ position_manage_id: "\u5E94\u8058\u804C\u4F4D",
2775
+ talent_id: "\u4EBA\u624D",
2776
+ candidate_manage_id: "\u5E94\u8058\u7BA1\u7406",
2777
+ mobile: "\u624B\u673A",
2778
+ email: "\u90AE\u7BB1",
2779
+ digital_id: "AI\u9762\u8BD5\u65B9\u6848",
2780
+ interview_paper_id: "\u9762\u8BD5\u8BD5\u5377",
2781
+ send_channel: "\u53D1\u9001\u6E20\u9053",
2782
+ notice_mould: "\u901A\u77E5\u6A21\u677F",
2783
+ affiliate_schedule: "\u5173\u8054\u65E5\u7A0B",
2784
+ union_status: "\u72B6\u6001",
2785
+ interview_location: "\u9762\u8BD5\u5730\u70B9"
2786
+ };
2787
+ var labelOf = (k) => FIELD_LABELS[k] ? FIELD_LABELS[k] + "(" + k + ")" : k;
2788
+ var ASSIGNED_API_RE = /\/api\/recruit\/candidate\/assignedInterview/;
2789
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2790
+ async function resolveCandidate(ctx, talentId) {
2791
+ const { host, cache } = ctx;
2792
+ const extra = { ebBusinessId: cache.lists?.talent?.listId || "" };
2793
+ const hr = await host.request({ path: "/api/recruit/talent/hasRights?formDataId=" + talentId, method: "POST", headers: extra });
2794
+ if (!(hr?.data || []).includes("READ")) throw rsFail("\u65E0 READ \u6743\u9650");
2795
+ const r1 = randHex32().slice(0, 6);
2796
+ const pq = await host.request({ path: "/api/recruit/candidate/getCandidateCardPrepareQuery?talentId=" + talentId + "&randomkey_" + r1 + "=" + r1, method: "GET", headers: extra });
2797
+ const batchId = pq?.data?.batchId;
2798
+ const v = randHex32().slice(0, 6);
2799
+ const [o, b2] = await Promise.all([
2800
+ host.request({ path: "/api/recruit/candidate/getCandidateCardOtherInfo?talentId=" + talentId + "&batchId=" + batchId + "&v=" + v + "&randomkey_ab=ab", method: "GET", headers: extra }),
2801
+ host.request({ path: "/api/recruit/candidate/getCandidateCardBaseInfo?talentId=" + talentId + "&batchId=" + batchId + "&v=" + v + "&randomkey_cd=cd", method: "GET", headers: extra })
2802
+ ]);
2803
+ const b = o?.data?.currentCandidateBatch || {};
2804
+ let name = b?.talent?.name;
2805
+ let mobile = "";
2806
+ let email = "";
2807
+ const tb = b2?.data?.talent || {};
2808
+ if (!name) name = tb.hrm?.name || tb.name;
2809
+ mobile = String(tb.telephone || tb.mobile || "").trim();
2810
+ email = String(tb.email || "").trim();
2811
+ return {
2812
+ talentId,
2813
+ name,
2814
+ mobile,
2815
+ email,
2816
+ candidateManageId: b?.id || b?.candidateManageId || null,
2817
+ positionId: b?.position?.id || null,
2818
+ positionName: b?.position?.name
2819
+ };
2820
+ }
2821
+ async function locateTalent(ctx, opts) {
2822
+ const { host, cache } = ctx;
2823
+ const listId = cache.lists?.talent?.listId;
2824
+ const objId = cache.forms?.talent_pool?.objId;
2825
+ if (!listId || !objId) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u4EBA\u624D\u5217\u8868\uFF08lists.talent / forms.talent_pool\uFF09");
2826
+ const fields = cache.fields[objId] || {};
2827
+ const nameKey = fields.candidate_name || fields.name;
2828
+ if (opts.mobile && !fields.telephone) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u4EBA\u624D\u624B\u673A\u5B57\u6BB5\uFF08telephone\uFF09");
2829
+ if (!opts.mobile && opts.name && !nameKey) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u4EBA\u624D\u59D3\u540D\u5B57\u6BB5\uFF08candidate_name/name\uFF09");
2830
+ const conds = {};
2831
+ if (opts.mobile) {
2832
+ const mf = "field_" + fields.telephone;
2833
+ conds[mf] = [{ [mf]: opts.mobile, [mf + "_condition_type"]: "like" }];
2834
+ }
2835
+ if (opts.name && !opts.mobile) {
2836
+ const nf = "field_" + nameKey;
2837
+ conds[nf] = [{ [nf]: opts.name, [nf + "_condition_type"]: "like" }];
2838
+ }
2839
+ const body = {
2840
+ current: 1,
2841
+ pageSize: 20,
2842
+ tableuid: randHex32(),
2843
+ listId,
2844
+ searchParamData: { searchType: "3", commonSearchFields: conds, type: "and" },
2845
+ terminal: "PC",
2846
+ browser: "Chrome",
2847
+ requiredMatch: 1,
2848
+ mapResult: 1,
2849
+ recheckQuery: 1,
2850
+ isFollow: 0,
2851
+ isShareToMe: 0,
2852
+ filterCondition: {},
2853
+ cusMenuId: cache.lists.talent.cusMenuId,
2854
+ urlPageTitle: "\u4EBA\u624D\u5E93",
2855
+ menuName: cache.lists.talent.menuName || "\u4EBA\u624D\u7BA1\u7406"
2856
+ };
2857
+ const r = await host.request({
2858
+ path: "/api/ebuilder/form/list/getListDatas?passid=" + objId + "&cid=" + objId,
2859
+ body,
2860
+ headers: { ebBusinessId: listId }
2861
+ });
2862
+ const disp = r?.data?.displayData;
2863
+ const raw = r?.data?.data || [];
2864
+ const rows = Array.isArray(disp) && disp.length ? disp : raw;
2865
+ return rows.map((x) => ({
2866
+ talentId: x.form_data_id || x.id || null,
2867
+ name: x.candidate_name || x.name || null,
2868
+ mobile: x.telephone || null,
2869
+ position: x.position_manage_id || x.position || null,
2870
+ recruitStatus: x.recruit_status || null
2871
+ }));
2872
+ }
2873
+ var PLAN_NAME_KEYS = ["msfa_name", "digital_name", "plan_name", "scheme_name"];
2874
+ function digitalMenuOf(cache) {
2875
+ const hit = cache.digitalHumanMenu;
2876
+ if (hit?.listId) return hit;
2877
+ const menus = cache.lists?.all_search || cache.menus || [];
2878
+ return menus.find((m) => (m.menuName || "").includes("\u9762\u8BD5\u6570\u5B57\u4EBA")) || menus.find((m) => (m.menuName || "").includes("\u6570\u5B57\u4EBA\u914D\u7F6E")) || null;
2879
+ }
2880
+ async function digitalObjIdOf(ctx) {
2881
+ const cached = ctx.cache.forms?.digital_human?.objId;
2882
+ if (cached) return String(cached);
2883
+ const r = await ctx.host.request({ path: "/api/ebuilder/form/obj/getObjsByTag?appId=" + ctx.appId + "&tag=uf_rcrt_digital_human", method: "GET" });
2884
+ const d = r?.data;
2885
+ return Array.isArray(d) && d.length ? String(d[0].id || "") : "";
2886
+ }
2887
+ async function listDigitalPlans(opts) {
2888
+ const { ctx, formId, layoutId, fieldId } = opts;
2889
+ const { host, cache } = ctx;
2890
+ try {
2891
+ const body = {
2892
+ browserMultiple: false,
2893
+ newly: 1,
2894
+ searchValue: "",
2895
+ formParam: JSON.stringify({ formId, layoutId, fieldId, filterItems: [], module: "ebuilderform", dataDetails: [] }),
2896
+ openScroll: true,
2897
+ controlId: fieldId,
2898
+ businessId: formId,
2899
+ variableMap: {}
2900
+ };
2901
+ const r = await host.request({ path: "/api/ebuilder/form/common/browser/complete/ebuilder", body });
2902
+ if (r?.status !== false && r?.data) {
2903
+ const list = r.data.data || [];
2904
+ const items2 = list.filter((x) => x && x.id).map((x) => ({
2905
+ id: String(x.id),
2906
+ name: String(x.name || "").trim(),
2907
+ position: "",
2908
+ desc: ""
2909
+ }));
2910
+ return { available: true, items: items2, source: "associate-complete" };
2911
+ }
2912
+ } catch {
2913
+ }
2914
+ const menu = digitalMenuOf(cache);
2915
+ if (!menu?.listId) {
2916
+ return { available: false, items: [], reason: "\u5019\u9009\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u4E14\u5F53\u524D\u73AF\u5883\u672A\u914D\u7F6E\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D\u5217\u8868\u83DC\u5355" };
2917
+ }
2918
+ let objId = "";
2919
+ try {
2920
+ const liBody = { cusMenuId: menu.cusMenuId, listId: menu.listId, urlPageTitle: menu.menuName };
2921
+ liBody["appId_" + menu.listId] = ctx.appId;
2922
+ const li = await host.request({ path: "/api/ebuilder/form/list/listInit", body: liBody });
2923
+ objId = String(li?.data?.objid || "");
2924
+ } catch {
2925
+ }
2926
+ if (!objId) objId = await digitalObjIdOf(ctx).catch(() => "");
2927
+ if (!objId) {
2928
+ return { available: false, items: [], reason: "\u672A\u80FD\u89E3\u6790\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D\u5BF9\u8C61 id\uFF08listInit / tag \u5747\u5931\u8D25\uFF09" };
2929
+ }
2930
+ const rows = [];
2931
+ let cur = 1;
2932
+ const size = 100;
2933
+ for (; ; ) {
2934
+ const body = {
2935
+ current: cur,
2936
+ pageSize: size,
2937
+ tableuid: randHex32(),
2938
+ listId: menu.listId,
2939
+ searchParamData: { searchType: "3", type: "and" },
2940
+ terminal: "PC",
2941
+ browser: "Chrome",
2942
+ requiredMatch: 1,
2943
+ mapResult: 1,
2944
+ recheckQuery: 1,
2945
+ isFollow: 0,
2946
+ isShareToMe: 0,
2947
+ filterCondition: {},
2948
+ cusMenuId: menu.cusMenuId,
2949
+ urlPageTitle: menu.menuName,
2950
+ menuName: menu.menuName
2951
+ };
2952
+ const r = await host.request({
2953
+ path: `/api/ebuilder/form/list/getListDatas?passid=${objId}&cid=${objId}`,
2954
+ body,
2955
+ headers: { ebBusinessId: menu.listId }
2956
+ });
2957
+ const page = r?.data?.data || [];
2958
+ rows.push(...page);
2959
+ if (page.length < size) break;
2960
+ cur++;
2961
+ if (cur > 20) break;
2962
+ }
2963
+ const items = rows.map((x) => {
2964
+ const nk = PLAN_NAME_KEYS.find((k) => String(x[k] || "").trim());
2965
+ return {
2966
+ id: String(x.form_data_id || x.id || ""),
2967
+ name: String(nk ? x[nk] : "").trim(),
2968
+ position: String(x.zwmc || "").trim(),
2969
+ desc: String(x.zwjs || "").trim()
2970
+ };
2971
+ }).filter((x) => x.id);
2972
+ return { available: true, items, listId: menu.listId, objId, menuName: menu.menuName, source: "list" };
2973
+ }
2974
+ function pickDigitalPlan(items, key) {
2975
+ const k = String(key || "").trim();
2976
+ if (!k) return { plan: null, ambiguous: [] };
2977
+ const byId = items.filter((x) => x.id === k);
2978
+ if (byId.length === 1) return { plan: byId[0], ambiguous: [] };
2979
+ const byExact = items.filter((x) => x.name === k);
2980
+ if (byExact.length === 1) return { plan: byExact[0], ambiguous: [] };
2981
+ const byLike = items.filter((x) => x.name.includes(k));
2982
+ if (byLike.length === 1) return { plan: byLike[0], ambiguous: [] };
2983
+ return { plan: null, ambiguous: byLike };
2984
+ }
2985
+ function planBrief(items) {
2986
+ return (items || []).map((x) => x.name + (x.position ? "\uFF08\u5C97\u4F4D\uFF1A" + x.position + "\uFF09" : "") + " [id=" + x.id + "]").join("\uFF1B");
2987
+ }
2988
+ async function buildPlan(opts) {
2989
+ const { ctx, talentId, positionId, interviewer, type, session, start, end, email, mobile, aiPlan } = opts;
2990
+ const { host, appId, cache } = ctx;
2991
+ const objId = objIdOf(cache, "interview_plan");
2992
+ const formId = formIdOf(cache, "interview_plan");
2993
+ const fields = cache.fields[objId] || {};
2994
+ const fid = (k) => fields[k];
2995
+ for (const k of DATAKEYS) if (!fid(k)) throw rsFail("\u9762\u8BD5\u8BA1\u5212\u7F3A\u5C11\u5B57\u6BB5 " + k + "\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF09");
2996
+ const cand = await resolveCandidate(ctx, talentId);
2997
+ const positionIdFinal = positionId || (cand.positionId ? String(cand.positionId) : "");
2998
+ if (!positionIdFinal) {
2999
+ throw rsFail("\u8BE5\u4EBA\u624D\u5C1A\u672A\u5173\u8054\u804C\u4F4D\uFF08\u7B80\u5386\u6C60\u4EBA\u624D\uFF09\uFF1A\u5B89\u6392\u9762\u8BD5\u5FC5\u987B\u5148\u9009\u5B9A\u4E00\u4E2A\u804C\u4F4D\u3002\u8BF7\u4ECE\u5728\u62DB\u804C\u4F4D\u4E2D\u63A8\u8350\u5E76\u7531\u7528\u6237\u9009\u5B9A\u540E\uFF0C\u5E26 positionId \u91CD\u8BD5\uFF08prepare \u53EF\u5148\u51FA\u786E\u8BA4\u6458\u8981\uFF09\u3002");
3000
+ }
3001
+ const candManageId = cand.candidateManageId ? String(cand.candidateManageId) : "";
3002
+ if (!candManageId) throw rsFail("\u672A\u5B9A\u4F4D\u5230\u5E94\u8058\u7BA1\u7406\u8BB0\u5F55\uFF08candidateManageId\uFF09\uFF0C\u8BF7\u786E\u8BA4\u5019\u9009\u4EBA\u5DF2\u6709\u5E94\u8058\u6279\u6B21");
3003
+ const typeMap = cachedDsMap(cache, objId, "interview_type") || await getDsOptions(host, objId, fid("interview_type"), appId);
3004
+ const sessionMap = cachedDsMap(cache, objId, "interview_session") || await getDsOptions(host, objId, fid("interview_session"), appId);
3005
+ const typeId = typeMap[type];
3006
+ if (!typeId) throw rsFail("\u9762\u8BD5\u65B9\u5F0F\u300C" + type + "\u300D\u4E0D\u5728\u679A\u4E3E\u4E2D\uFF08" + Object.keys(typeMap).join("/") + "\uFF09");
3007
+ const sessionId = sessionMap[session];
3008
+ if (!sessionId) throw rsFail("\u9762\u8BD5\u8F6E\u6B21\u300C" + session + "\u300D\u4E0D\u5728\u679A\u4E3E\u4E2D\uFF08" + Object.keys(sessionMap).join("/") + "\uFF09");
3009
+ const emailFromTalent = String(cand.email || "").trim();
3010
+ const mobileFromTalent = String(cand.mobile || "").trim();
3011
+ const emailInput = String(email || "").trim();
3012
+ const mobileInput = String(mobile || "").trim();
3013
+ if (emailInput) cand.email = emailInput;
3014
+ if (mobileInput) cand.mobile = mobileInput;
3015
+ const emailSource = emailInput ? "user" : emailFromTalent ? "talent" : "none";
3016
+ const mobileSource = mobileInput ? "user" : mobileFromTalent ? "talent" : "none";
3017
+ const isAI = /AI|人工智能/i.test(String(type));
3018
+ if (isAI && !cand.email) {
3019
+ throw rsFail("AI\u9762\u8BD5\u5FC5\u987B\u586B\u5199\u5019\u9009\u4EBA\u90AE\u7BB1\uFF08\u5FC5\u586B\uFF09\uFF1A\u8BE5\u4EBA\u624D\u8D44\u6599\u672A\u7EF4\u62A4\u90AE\u7BB1\uFF08talentId=" + talentId + "\uFF09\u3002\u8BF7\u7528\u6237\u63D0\u4F9B\u90AE\u7BB1\u540E\u91CD\u8BD5\uFF08email \u53C2\u6570\uFF09\uFF1B\u6216\u5148\u5230\u4EBA\u624D\u8D44\u6599\u8865\u5168\u90AE\u7BB1\u518D\u5B89\u6392\u3002");
3020
+ }
3021
+ if (cand.email && !EMAIL_RE.test(cand.email)) {
3022
+ throw rsFail("\u5019\u9009\u4EBA\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E\uFF1A" + cand.email + "\u3002\u8BF7\u63D0\u4F9B\u6709\u6548\u90AE\u7BB1\u540E\u91CD\u8BD5\u3002");
3023
+ }
3024
+ if (!cand.mobile) {
3025
+ throw rsFail("\u672A\u53D6\u5230\u5019\u9009\u4EBA\u624B\u673A\u53F7\uFF08talentId=" + talentId + "\uFF09\uFF0C\u8BF7\u5148\u5728\u4EBA\u624D\u8D44\u6599\u8865\u5168\u624B\u673A\u53F7\uFF0C\u6216\u7528 mobile \u53C2\u6570\u6307\u5B9A");
3026
+ }
3027
+ const interviewerTask = (async () => {
3028
+ if (interviewer) {
3029
+ return { id: await resolvePersonId(host, fid("interviewer"), interviewer), name: interviewer };
3030
+ }
3031
+ const cu = await resolveCurrentUser(host);
3032
+ return cu ? { id: cu.id, name: cu.name } : { id: null, name: interviewer };
3033
+ })().then(
3034
+ (value) => ({ ok: true, value }),
3035
+ (error) => ({ ok: false, error })
3036
+ );
3037
+ let aiPlanPicked = null;
3038
+ let aiPlanOptions = { available: false, items: [] };
3039
+ let layoutId = "";
3040
+ const digitalFid = fid("digital_id");
3041
+ let interviewerId = null;
3042
+ let interviewerName = interviewer;
3043
+ if (isAI) {
3044
+ if (!digitalFid) throw rsFail("\u9762\u8BD5\u8BA1\u5212\u8868\u5355\u7F3A\u5C11\u300CAI\u9762\u8BD5\u65B9\u6848\u300D\u5B57\u6BB5\uFF08dataKey=digital_id\uFF09\uFF0C\u65E0\u6CD5\u5B89\u6392 AI \u9762\u8BD5");
3045
+ layoutId = await getFormLayout(host, formId);
3046
+ const [plans, ivr] = await Promise.all([listDigitalPlans({ ctx, formId, layoutId, fieldId: digitalFid }), interviewerTask]);
3047
+ if (!ivr.ok) throw ivr.error;
3048
+ interviewerId = ivr.value.id;
3049
+ interviewerName = ivr.value.name;
3050
+ aiPlanOptions = plans;
3051
+ if (!aiPlanOptions.available) {
3052
+ throw rsFail("AI\u9762\u8BD5\u5FC5\u987B\u9009\u62E9\u300CAI\u9762\u8BD5\u65B9\u6848\u300D\uFF0C\u4F46" + aiPlanOptions.reason + "\uFF1B\u8BF7\u5148\u5728\u8BE5\u73AF\u5883\u7EF4\u62A4\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E");
3053
+ }
3054
+ if (!aiPlanOptions.items.length) {
3055
+ throw rsFail("\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D\u6682\u65E0\u53EF\u9009\u65B9\u6848\uFF0C\u8BF7\u5148\u5728\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D\u4E2D\u65B0\u589E\u65B9\u6848\u540E\u518D\u5B89\u6392 AI \u9762\u8BD5");
3056
+ }
3057
+ if (aiPlan) {
3058
+ const { plan, ambiguous } = pickDigitalPlan(aiPlanOptions.items, aiPlan);
3059
+ if (plan) aiPlanPicked = plan;
3060
+ else if (ambiguous.length > 1) throw rsFail("AI\u9762\u8BD5\u65B9\u6848\u300C" + aiPlan + "\u300D\u547D\u4E2D\u591A\u4E2A\uFF1A" + planBrief(ambiguous) + "\uFF0C\u8BF7\u6307\u660E\u5177\u4F53\u65B9\u6848");
3061
+ else throw rsFail("\u672A\u627E\u5230 AI\u9762\u8BD5\u65B9\u6848\u300C" + aiPlan + "\u300D\u3002\u53EF\u9009\uFF1A" + planBrief(aiPlanOptions.items));
3062
+ }
3063
+ } else {
3064
+ const ivr = await interviewerTask;
3065
+ if (!ivr.ok) throw ivr.error;
3066
+ interviewerId = ivr.value.id;
3067
+ interviewerName = ivr.value.name;
3068
+ }
3069
+ if (!interviewerId) throw rsFail("\u9762\u8BD5\u5B98\u672A\u6307\u5B9A\u4E14\u65E0\u6CD5\u89E3\u6790\u5F53\u524D\u767B\u5F55\u7528\u6237\uFF0C\u8BF7\u7528 interviewer \u53C2\u6570\u6307\u5B9A");
3070
+ const dataDetails = [
3071
+ { formField: { id: fid("subject") }, content: (cand.positionName ? cand.positionName + "-" : "") + cand.name + "\u7684\u9762\u8BD5" },
3072
+ { formField: { id: fid("start_time") }, content: start },
3073
+ { formField: { id: fid("end_time") }, content: end },
3074
+ { formField: { id: fid("interview_type") }, dataOptions: [{ optionId: typeId, content: type }] },
3075
+ { formField: { id: fid("interview_session") }, dataOptions: [{ optionId: sessionId, content: session }] },
3076
+ { formField: { id: fid("interviewer") }, dataOptions: [{ optionId: interviewerId, content: interviewerName }] },
3077
+ { formField: { id: fid("mobile") }, content: cand.mobile },
3078
+ ...cand.email ? [{ formField: { id: fid("email") }, content: cand.email }] : [],
3079
+ { formField: { id: fid("position_manage_id") }, dataOptions: [{ optionId: positionIdFinal, content: cand.positionName }] },
3080
+ { formField: { id: fid("talent_id") }, dataOptions: [{ optionId: talentId, content: cand.name }] },
3081
+ { formField: { id: fid("candidate_manage_id") }, dataOptions: [{ optionId: candManageId, content: candManageId }] }
3082
+ ];
3083
+ if (aiPlanPicked) {
3084
+ dataDetails.push({ formField: { id: digitalFid }, dataOptions: [{ optionId: aiPlanPicked.id, content: aiPlanPicked.name }] });
3085
+ }
3086
+ return {
3087
+ host: ctx.host,
3088
+ objId,
3089
+ formId,
3090
+ appId,
3091
+ dataDetails,
3092
+ cand,
3093
+ typeId,
3094
+ sessionId,
3095
+ interviewerId,
3096
+ isAI,
3097
+ layoutId,
3098
+ typeOptions: Object.fromEntries(Object.entries(typeMap).map(([n, id]) => [String(id), n])),
3099
+ aiPlanPicked,
3100
+ aiPlanOptions,
3101
+ summary: {
3102
+ action: "\u5B89\u6392\u9762\u8BD5",
3103
+ talent: cand.name + "\uFF08talentId=" + talentId + "\uFF09",
3104
+ position: cand.positionName,
3105
+ type,
3106
+ session,
3107
+ time: start + " ~ " + end,
3108
+ interviewer: interviewerName,
3109
+ mobile: cand.mobile + "\uFF08\u6765\u6E90\uFF1A" + (mobileSource === "user" ? "\u672C\u6B21\u6307\u5B9A" : "\u4EBA\u624D\u8D44\u6599") + "\uFF09",
3110
+ email: (cand.email || "\uFF08\u672A\u63D0\u4F9B\uFF09") + "\uFF08\u6765\u6E90\uFF1A" + (emailSource === "user" ? "\u672C\u6B21\u6307\u5B9A" : emailSource === "talent" ? "\u4EBA\u624D\u8D44\u6599" : "\u7F3A\u5931") + "\uFF09",
3111
+ aiPlan: isAI ? aiPlanPicked ? aiPlanPicked.name + "\uFF08id=" + aiPlanPicked.id + "\uFF09" : "\u672A\u9009\u62E9\uFF08AI\u9762\u8BD5\u5FC5\u586B\uFF0C\u9700\u7528\u6237\u6307\u5B9A\uFF09" : "\uFF08\u975E AI \u9762\u8BD5\uFF0C\u4E0D\u9002\u7528\uFF09",
3112
+ isAIInterview: isAI
3113
+ }
3114
+ };
3115
+ }
3116
+ async function resolveSaveActions(plan) {
3117
+ const layoutId = plan.layoutId || await getFormLayout(plan.host, plan.formId);
3118
+ const acts = pickSaveButtonActions(await getFormButtons(plan.host, plan.objId, layoutId));
3119
+ return acts.filter((a) => a.actionType !== "system");
3120
+ }
3121
+ async function callAssignedInterview(host, formDataId) {
3122
+ const r = await host.request({ path: "/api/recruit/candidate/assignedInterview?formDataId=" + formDataId, method: "POST" });
3123
+ const ok = String(r?.code) === "200" || r?.status === true || r?.success === true;
3124
+ if (!ok) throw rsFail("\u5206\u914D\u9762\u8BD5\uFF08assignedInterview\uFF09\u5931\u8D25\uFF1A" + (r?.msg || JSON.stringify(r).slice(0, 200)));
3125
+ return r?.data || {};
3126
+ }
3127
+ async function execSaveActions(plan, formDataId) {
3128
+ const host = plan.host;
3129
+ const executed = [];
3130
+ const warnings = [];
3131
+ let assigned = null;
3132
+ let actions = [];
3133
+ try {
3134
+ actions = await resolveSaveActions(plan);
3135
+ } catch (e) {
3136
+ warnings.push("\u8BFB\u53D6\u300C\u4FDD\u5B58\u300D\u6309\u94AE\u52A8\u4F5C\u94FE\u5931\u8D25\uFF1A" + e.message + "\uFF1B\u5DF2\u6309\u300C\u4FDD\u5B58 + \u5206\u914D\u9762\u8BD5\u300D\u515C\u5E95\u6267\u884C");
3137
+ }
3138
+ const hasAssigned = actions.some((a) => ASSIGNED_API_RE.test(a.ecode || ""));
3139
+ if (!actions.length) warnings.push("\u672A\u89E3\u6790\u5230\u300C\u4FDD\u5B58\u300D\u6309\u94AE\u7684\u540E\u7EED\u52A8\u4F5C\uFF08\u8BE5\u73AF\u5883\u53EF\u80FD\u672A\u914D\u7F6E\uFF0C\u6216\u6309\u94AE\u914D\u7F6E\u63A5\u53E3\u4E0D\u53EF\u7528\uFF09\uFF1B\u5DF2\u6309\u300C\u4FDD\u5B58 + \u5206\u914D\u9762\u8BD5\u300D\u515C\u5E95");
3140
+ for (const a of actions) {
3141
+ const base = { order: a.showOrder, name: a.name, type: a.actionType };
3142
+ if (a.actionType === "esb") {
3143
+ const r = await runEsb(host, { objId: plan.objId, dataIds: formDataId, esbActionIds: a.id });
3144
+ const ok = r?.status !== false;
3145
+ executed.push({ ...base, ok, msg: r?.successMsg || r?.failMsg || "" });
3146
+ if (!ok) throw rsFail("\u300C\u4FDD\u5B58\u300D\u540E\u7EED\u52A8\u4F5C\u300C" + (a.name || a.id) + "\u300D\u6267\u884C\u5931\u8D25\uFF1A" + (r?.failMsg || "\u672A\u77E5\u539F\u56E0"));
3147
+ } else if (a.actionType === "jsCode") {
3148
+ if (ASSIGNED_API_RE.test(a.ecode || "")) {
3149
+ assigned = await callAssignedInterview(host, formDataId);
3150
+ executed.push({ ...base, ok: true, msg: "\u7B49\u4EF7\u8C03\u7528 assignedInterview\uFF08\u5206\u914D\u9762\u8BD5\uFF09" });
3151
+ } else {
3152
+ executed.push({ ...base, ok: null, msg: "\u81EA\u5B9A\u4E49\u811A\u672C\u52A8\u4F5C\uFF0C\u670D\u52A1\u7AEF\u65E0\u6CD5\u7B49\u4EF7\u6267\u884C\uFF0C\u5DF2\u8DF3\u8FC7" });
3153
+ warnings.push("\u52A8\u4F5C\u300C" + (a.name || a.id) + "\u300D\u4E3A\u81EA\u5B9A\u4E49 jsCode\uFF0C\u672A\u6267\u884C\uFF1B\u5982\u9700\u8BE5\u6548\u679C\u8BF7\u5728\u9875\u9762\u70B9\u4FDD\u5B58");
3154
+ }
3155
+ } else {
3156
+ executed.push({ ...base, ok: null, msg: "\u975E\u670D\u52A1\u7AEF\u52A8\u4F5C\uFF08" + a.actionType + "\uFF09\uFF0C\u5DF2\u8DF3\u8FC7" });
3157
+ }
3158
+ }
3159
+ if (!hasAssigned) {
3160
+ assigned = await callAssignedInterview(host, formDataId);
3161
+ executed.push({ order: 999, name: "\u5206\u914D\u9762\u8BD5\uFF08\u515C\u5E95\uFF09", type: "api", ok: true, msg: "\u8BE5\u73AF\u5883\u672A\u628A\u5206\u914D\u9762\u8BD5\u914D\u7F6E\u4E3A\u4FDD\u5B58\u52A8\u4F5C" });
3162
+ warnings.push("\u8BE5\u73AF\u5883\u300C\u4FDD\u5B58\u300D\u52A8\u4F5C\u94FE\u672A\u89E3\u6790\u5230 assignedInterview\uFF0C\u5DF2\u6309\u62DB\u8058\u4E1A\u52A1\u5951\u7EA6\u515C\u5E95\u8C03\u7528");
3163
+ }
3164
+ return { executed, warnings, assigned };
3165
+ }
3166
+ async function resolveRequiredHint(plan, ctx) {
3167
+ const fields = ctx.cache.fields[plan.objId] || {};
3168
+ const meta = await getFormLayoutMeta(ctx.host, plan.formId);
3169
+ if (!meta) return { available: false, required: [], missing: [] };
3170
+ return computeRequiredHint({
3171
+ meta,
3172
+ fields,
3173
+ typeName: plan.summary.type,
3174
+ typeOptionId: plan.typeId,
3175
+ typeOptions: plan.typeOptions || {},
3176
+ dataDetails: plan.dataDetails
3177
+ });
3178
+ }
3179
+ function resolveTalentCardUrl2(ctx, talentId) {
3180
+ const pid = ctx.cache.ebdDetailPageId;
3181
+ if (!pid) return null;
3182
+ return `${ctx.baseUrl}/sp/ebdpage/view/${pid}?talentId=${talentId}${talentLinkExtra(ctx.cache)}`;
3183
+ }
3184
+ async function resolveInterviewListUrl(ctx) {
3185
+ const { host, appId, baseUrl, cache } = ctx;
3186
+ const cachedMenu = cache.lists?.interview;
3187
+ if (cachedMenu?.listId && cachedMenu.combinationId) {
3188
+ return `${baseUrl}/ebdapp/view/${appId}/combination/${cachedMenu.combinationId}-${cachedMenu.listId || cachedMenu.cusMenuId}/search/${cachedMenu.pageId || cachedMenu.cusMenuId}?terminal=PC&cusMenuId=${cachedMenu.cusMenuId}`;
3189
+ }
3190
+ const r = await host.request({ path: `/api/cusapp/app/allMenus?isFront=1&appId=${appId}&terminalType=PC`, method: "GET" });
3191
+ const flat = [];
3192
+ (function walk(ns) {
3193
+ for (const n of ns || []) if (n && typeof n === "object") {
3194
+ flat.push(n);
3195
+ walk(n.children || n.childs);
3196
+ }
3197
+ })(r?.data?.cusAppMenus || r?.data);
3198
+ const node = findMenuNode(flat, ["\u9762\u8BD5\u7BA1\u7406", "\u9762\u8BD5\u5B89\u6392\u5217\u8868", "\u9762\u8BD5\u5B89\u6392"], (n) => n.combinationId && n.id && n.pageId);
3199
+ if (!node) return null;
3200
+ return `${baseUrl}/ebdapp/view/${appId}/combination/${node.combinationId}-${node.id}/search/${node.pageId}?terminal=PC&cusMenuId=${node.id}`;
3201
+ }
3202
+ function nowAndEnd(type) {
3203
+ const isAI = /AI|人工智能/i.test(String(type || ""));
3204
+ const d = /* @__PURE__ */ new Date();
3205
+ const f = (x) => String(x).padStart(2, "0");
3206
+ const fmt = (x) => x.getFullYear() + "-" + f(x.getMonth() + 1) + "-" + f(x.getDate()) + " " + f(x.getHours()) + ":" + f(x.getMinutes());
3207
+ const s = fmt(d);
3208
+ const e = fmt(new Date(d.getTime() + (isAI ? 7 * 24 * 3600 * 1e3 : 3600 * 1e3)));
3209
+ return { s, e, isAI };
3210
+ }
3211
+ function shiftTime(str, addMs) {
3212
+ const m = /^(\d{4})-(\d{1,2})-(\d{1,2})[ T](\d{1,2}):(\d{2})$/.exec(String(str || "").trim());
3213
+ if (!m) return null;
3214
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]));
3215
+ if (Number.isNaN(d.getTime())) return null;
3216
+ const t = new Date(d.getTime() + addMs);
3217
+ const f = (x) => String(x).padStart(2, "0");
3218
+ return t.getFullYear() + "-" + f(t.getMonth() + 1) + "-" + f(t.getDate()) + " " + f(t.getHours()) + ":" + f(t.getMinutes());
3219
+ }
3220
+ function makeCtx(env) {
3221
+ return (async () => {
3222
+ const host = env.host;
3223
+ const session = await host.getSession();
3224
+ const cache = await env.recruitContext();
3225
+ return { host, baseUrl: session.baseUrl.replace(/\/+$/, ""), appId: cache.appId, cache };
3226
+ })();
3227
+ }
3228
+ async function locateTalentId(ctx, args) {
3229
+ if (args.talentId) return String(args.talentId);
3230
+ if (!args.mobile && !args.name) {
3231
+ throw rsValidationError("talent_required", "\u8BF7\u63D0\u4F9B talentId\uFF0C\u6216 mobile/name \u4E4B\u4E00\u7528\u4E8E\u5B9A\u4F4D\u5019\u9009\u4EBA");
3232
+ }
3233
+ const cands = await locateTalent(ctx, { mobile: args.mobile, name: args.name });
3234
+ if (!cands.length) {
3235
+ throw rsFail("\u4EBA\u624D\u5E93\u672A\u627E\u5230" + (args.mobile ? "\u624B\u673A\u53F7\u4E3A " + args.mobile : "\u59D3\u540D\u4E3A " + args.name) + "\u7684\u5019\u9009\u4EBA\uFF08\u68C0\u7D22\u6761\u4EF6\uFF1A\u4EBA\u624D\u5E93\u5217\u8868\uFF0C\u5173\u952E\u8BCD like\uFF09", { located: false, candidates: [] });
3236
+ }
3237
+ if (cands.length > 1) {
3238
+ throw rsFail("\u5339\u914D\u5230\u591A\u6761\u540C" + (args.mobile ? "\u624B\u673A\u53F7" : "\u59D3\u540D") + "\u5019\u9009\u4EBA\uFF0C\u8BF7\u7528\u6237\u9009\u62E9\u540E\u518D\u5B89\u6392\u9762\u8BD5\uFF08\u7EDD\u4E0D\u81EA\u52A8\u6311\u4E00\u4E2A\uFF09", { located: false, needChoice: true, candidates: cands });
3239
+ }
3240
+ return String(cands[0].talentId);
3241
+ }
3242
+ var baseInput3 = {
3243
+ talentId: { type: "string", description: "\u4EBA\u624D id\uFF08\u4E0E mobile/name \u4E09\u9009\u4E00\uFF09" },
3244
+ mobile: { type: "string", pattern: "^\\d{6,20}$", description: "\u624B\u673A\u53F7\u5B9A\u4F4D\uFF08\u552F\u4E00\u547D\u4E2D\u81EA\u52A8\u91C7\u7528\uFF09" },
3245
+ name: { type: "string", description: "\u59D3\u540D\u5B9A\u4F4D\uFF08\u591A\u6761\u4F1A\u8981\u6C42\u7528\u6237\u9009\u62E9\uFF09" },
3246
+ positionId: { type: "string", description: "\u5E94\u8058\u804C\u4F4D id\uFF08\u7B80\u5386\u6C60\u4EBA\u624D\u5FC5\u586B\uFF09" },
3247
+ interviewer: { type: "string", description: "\u9762\u8BD5\u5B98\u59D3\u540D\uFF08\u7F3A\u7701=\u5F53\u524D\u767B\u5F55\u7528\u6237\uFF09" },
3248
+ type: { type: "string", description: "\u9762\u8BD5\u65B9\u5F0F\uFF08\u73B0\u573A\u9762\u8BD5/\u89C6\u9891\u9762\u8BD5/\u7535\u8BDD\u9762\u8BD5/AI\u9762\u8BD5\uFF1B\u542B AI \u65F6\u90AE\u7BB1\u4E0E\u65B9\u6848\u5FC5\u586B\uFF09" },
3249
+ session: { type: "string", description: "\u9762\u8BD5\u8F6E\u6B21\uFF08\u521D\u8BD5/\u590D\u8BD5/\u2026\uFF09" },
3250
+ start: { type: "string", pattern: "^\\d{4}-\\d{1,2}-\\d{1,2}[ T]\\d{1,2}:\\d{2}$", description: "\u5F00\u59CB\u65F6\u95F4 YYYY-MM-DD HH:mm\uFF08\u7F3A\u7701=\u5F53\u524D\u65F6\u95F4\uFF09" },
3251
+ end: { type: "string", pattern: "^\\d{4}-\\d{1,2}-\\d{1,2}[ T]\\d{1,2}:\\d{2}$", description: "\u7ED3\u675F\u65F6\u95F4\uFF08\u7F3A\u7701=AI +7\u5929 / \u5176\u5B83 +1\u5C0F\u65F6\uFF09" },
3252
+ email: { type: "string", description: "\u5019\u9009\u4EBA\u90AE\u7BB1\uFF08AI \u9762\u8BD5\u5FC5\u586B\uFF1B\u8865\u5F55\u503C\u4F18\u5148\u4E8E\u4EBA\u624D\u8D44\u6599\uFF09" },
3253
+ aiPlan: { type: "string", description: "AI \u9762\u8BD5\u65B9\u6848\u540D\u6216 id\uFF08AI \u9762\u8BD5\u5FC5\u586B\uFF1B\u4E0D\u4F20\u5219 prepare \u5217\u51FA\u5019\u9009\uFF09" }
3254
+ };
3255
+ async function prepare3(args, env) {
3256
+ const ctx = await makeCtx(env);
3257
+ const talentId = await locateTalentId(ctx, args);
3258
+ const auto = nowAndEnd(args.type || "\u73B0\u573A\u9762\u8BD5");
3259
+ const startTime = args.start || auto.s;
3260
+ const endTime = args.end || (args.start ? shiftTime(String(args.start), auto.isAI ? 7 * 24 * 3600 * 1e3 : 3600 * 1e3) || auto.e : auto.e);
3261
+ const plan = await buildPlan({
3262
+ ctx,
3263
+ talentId,
3264
+ positionId: args.positionId ? String(args.positionId) : void 0,
3265
+ interviewer: args.interviewer ? String(args.interviewer) : void 0,
3266
+ type: String(args.type || "\u73B0\u573A\u9762\u8BD5"),
3267
+ session: String(args.session || "\u521D\u8BD5"),
3268
+ start: String(startTime),
3269
+ end: String(endTime),
3270
+ email: args.email ? String(args.email) : void 0,
3271
+ mobile: args.mobile ? String(args.mobile) : void 0,
3272
+ aiPlan: args.aiPlan ? String(args.aiPlan) : void 0
3273
+ });
3274
+ plan.host = ctx.host;
3275
+ const aiPlanCandidates = plan.isAI ? (plan.aiPlanOptions.items || []).map((x) => ({ id: x.id, name: x.name, position: x.position })) : [];
3276
+ const requiredHint = await resolveRequiredHint(plan, ctx).catch(() => null);
3277
+ if (requiredHint?.missing?.length) requiredHint.missingLabels = requiredHint.missing.map(labelOf);
3278
+ const defPlan = plan.isAI ? requiredHint?.defaultValues?.digital_id : null;
3279
+ if (defPlan?.optionId) {
3280
+ for (const x of aiPlanCandidates) if (String(x.id) === String(defPlan.optionId)) x.isDefault = true;
3281
+ }
3282
+ const defaultAiPlan = defPlan?.optionId ? { id: defPlan.optionId, name: defPlan.content } : void 0;
3283
+ const willSubmit = { formId: plan.formId, objId: plan.objId, dataDetails: plan.dataDetails };
3284
+ const { createHash: createHash3 } = await import("node:crypto");
3285
+ const fp = createHash3("sha256").update(JSON.stringify(willSubmit)).digest("hex");
3286
+ const continuation = await env.continuation.issue({ operation: "recruit-stage.interview.arrange.apply", fingerprint: fp });
3287
+ return rsSuccess("recruit-stage.interview.arrange.prepare", {
3288
+ mode: "prepare",
3289
+ ...plan.summary,
3290
+ needAiPlan: plan.isAI && !plan.aiPlanPicked,
3291
+ aiPlans: plan.isAI ? aiPlanCandidates : void 0,
3292
+ defaultAiPlan,
3293
+ aiPlanHint: plan.isAI && !plan.aiPlanPicked ? "AI\u9762\u8BD5\u65B9\u6848\u4E3A\u5FC5\u586B\uFF1A\u8BF7\u628A aiPlans \u5217\u7ED9\u7528\u6237\u9009\u62E9\uFF08isDefault=true \u4E3A\u9875\u9762\u9ED8\u8BA4\u65B9\u6848\uFF09\uFF0C\u786E\u8BA4\u540E\u5E26 aiPlan \u91CD\u8DD1 apply" : void 0,
3294
+ requiredHint,
3295
+ willSubmit
3296
+ }, {
3297
+ continuation,
3298
+ fingerprint: fp,
3299
+ confirmHint: "\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u643A\u5E26 confirm:true \u4E0E\u672C continuation \u8C03\u7528 recruit-stage.interview.arrange.apply\uFF08AI \u9762\u8BD5\u9700\u5148\u9009\u5B9A aiPlan\uFF09"
3300
+ });
3301
+ }
3302
+ async function apply3(args, env) {
3303
+ if (args.confirm !== true) throw rsConfirmationError("recruit-stage.interview.arrange.apply", { hint: "\u5199\u64CD\u4F5C\u5FC5\u987B\u663E\u5F0F confirm:true" });
3304
+ if (!args.continuation) throw rsValidationError("continuation_required", "\u7F3A\u5C11 continuation\uFF08\u6765\u81EA prepare \u7684 meta\uFF09");
3305
+ const payload = await env.continuation.verify(String(args.continuation), "recruit-stage.interview.arrange.apply");
3306
+ const ctx = await makeCtx(env);
3307
+ const talentId = await locateTalentId(ctx, args);
3308
+ const auto = nowAndEnd(args.type || "\u73B0\u573A\u9762\u8BD5");
3309
+ const startTime = args.start || auto.s;
3310
+ const endTime = args.end || (args.start ? shiftTime(String(args.start), auto.isAI ? 7 * 24 * 3600 * 1e3 : 3600 * 1e3) || auto.e : auto.e);
3311
+ const plan = await buildPlan({
3312
+ ctx,
3313
+ talentId,
3314
+ positionId: args.positionId ? String(args.positionId) : void 0,
3315
+ interviewer: args.interviewer ? String(args.interviewer) : void 0,
3316
+ type: String(args.type || "\u73B0\u573A\u9762\u8BD5"),
3317
+ session: String(args.session || "\u521D\u8BD5"),
3318
+ start: String(startTime),
3319
+ end: String(endTime),
3320
+ email: args.email ? String(args.email) : void 0,
3321
+ mobile: args.mobile ? String(args.mobile) : void 0,
3322
+ aiPlan: args.aiPlan ? String(args.aiPlan) : void 0
3323
+ });
3324
+ plan.host = ctx.host;
3325
+ if (plan.isAI && !plan.aiPlanPicked) {
3326
+ throw rsFail("AI\u9762\u8BD5\u5FC5\u987B\u9009\u62E9\u300CAI\u9762\u8BD5\u65B9\u6848\u300D\uFF08\u5FC5\u586B\uFF09\uFF0C\u5F53\u524D\u672A\u6307\u5B9A\u3002\u53EF\u9009\u65B9\u6848\uFF1A" + (plan.aiPlanOptions.items?.length ? planBrief(plan.aiPlanOptions.items) : "\uFF08\u65E0\uFF09") + "\u3002\u8BF7\u8BA9\u7528\u6237\u9009\u62E9\u540E\u5E26 aiPlan \u91CD\u8BD5\u3002");
3327
+ }
3328
+ const willSubmit = { formId: plan.formId, objId: plan.objId, dataDetails: plan.dataDetails };
3329
+ const { createHash: createHash3 } = await import("node:crypto");
3330
+ if (payload.fingerprint !== createHash3("sha256").update(JSON.stringify(willSubmit)).digest("hex")) {
3331
+ throw rsValidationError("fingerprint_mismatch", "\u5F85\u63D0\u4EA4\u5185\u5BB9\u4E0E prepare \u786E\u8BA4\u6458\u8981\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 prepare \u786E\u8BA4\u540E\u518D apply");
3332
+ }
3333
+ const formDataId = await saveFormData(ctx.host, {
3334
+ appId: plan.appId,
3335
+ formId: plan.formId,
3336
+ objId: plan.objId,
3337
+ name: "\u9762\u8BD5\u7BA1\u7406-" + plan.cand.name + " " + plan.summary.time,
3338
+ dataDetails: plan.dataDetails
3339
+ });
3340
+ const actionsRun = await execSaveActions(plan, formDataId);
3341
+ const detailUrl = buildCardUrl(ctx.baseUrl, plan.objId, formDataId);
3342
+ const listLocateUrl = buildListLocateUrl(ctx.baseUrl, ctx.appId, ctx.cache.lists?.interview?.listId || "", plan.objId, formDataId);
3343
+ return rsSuccess("recruit-stage.interview.arrange.apply", {
3344
+ mode: "apply",
3345
+ result: "success",
3346
+ interviewFormId: formDataId,
3347
+ interviewCardUrl: detailUrl,
3348
+ viewLink: detailUrl ? `[\u67E5\u770B ${plan.cand.name || ""} \u7684\u9762\u8BD5\u5B89\u6392\u8BE6\u60C5](${detailUrl})` : null,
3349
+ listLocateLink: listLocateUrl ? `[\u6253\u5F00\u9762\u8BD5\u7BA1\u7406\u5217\u8868\uFF08\u5DF2\u5B9A\u4F4D\u672C\u6B21\u9762\u8BD5\u5B89\u6392\uFF09](${listLocateUrl})` : null,
3350
+ agentNote: "\u4EA7\u7269 = \u4E24\u4E2A Markdown \u94FE\u63A5\uFF08\u5E73\u53F0\u5B9E\u8BC1\u53EF\u70B9\u51FB\uFF09\uFF1A\u2460 viewLink \u9762\u8BD5\u5B89\u6392\u8BE6\u60C5\u9875 \u2461 listLocateLink \u9762\u8BD5\u7BA1\u7406\u5217\u8868\u5E76\u53F3\u4FA7\u6ED1\u51FA\u672C\u6B21\u9762\u8BD5\u5B89\u6392\uFF1B\u6309\u987A\u5E8F\u539F\u6837\u653E\u56DE\u7B54\u6B63\u6587\uFF0C\u7F3A\u54EA\u4E2A\u53EA\u7ED9\u53E6\u4E00\u4E2A\u3001\u4E0D\u8F93\u51FA\u534A\u622A\u3002\u4E0D\u8981\u5305 browsertool / <artifact> \u6807\u7B7E\u3002",
3351
+ aiPlan: plan.isAI && plan.aiPlanPicked ? { id: plan.aiPlanPicked.id, name: plan.aiPlanPicked.name } : void 0,
3352
+ saveActions: actionsRun.executed,
3353
+ warnings: actionsRun.warnings,
3354
+ assigned: actionsRun.assigned || {}
3355
+ });
3356
+ }
3357
+ async function prefill2(args, env) {
3358
+ const ctx = await makeCtx(env);
3359
+ const talentId = await locateTalentId(ctx, args);
3360
+ const objId = objIdOf(ctx.cache, "interview_plan");
3361
+ const formId = formIdOf(ctx.cache, "interview_plan");
3362
+ const fields = ctx.cache.fields[objId] || {};
3363
+ const typeKey = String(args.type || "AI\u9762\u8BD5");
3364
+ const typeFid = fields.interview_type;
3365
+ if (!typeFid) throw rsFail("\u9762\u8BD5\u8BA1\u5212\u7F3A\u5C11\u5B57\u6BB5 interview_type\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF09");
3366
+ const typeMap = cachedDsMap(ctx.cache, objId, "interview_type") || await getDsOptions(ctx.host, objId, typeFid, ctx.appId);
3367
+ const typeId = typeMap[typeKey];
3368
+ if (!typeId) throw rsFail("\u9762\u8BD5\u65B9\u5F0F\u300C" + typeKey + "\u300D\u4E0D\u5728\u679A\u4E3E\u4E2D\uFF08" + Object.keys(typeMap).join("/") + "\uFF09");
3369
+ const cardUrl = resolveTalentCardUrl2(ctx, talentId);
3370
+ const listUrl = await resolveInterviewListUrl(ctx).catch(() => null);
3371
+ const jsExpr = `(() => { const W = window.WeFormSDK; const i = W && W.getWeFormInstance('ebuilderform', '${formId}'); if (!i) return 'no-form-instance(\u8BF7\u5148\u5728\u4EBA\u624D\u5361\u7247\u70B9\u300C\u5B89\u6392\u9762\u8BD5\u300D\u6253\u5F00\u9762\u8BD5\u5B89\u6392\u8868\u5355)'; try { i.changeFieldValue('field${typeFid}', { value: '${typeId}' }); return 'ok \u9762\u8BD5\u65B9\u5F0F=${typeKey}'; } catch (e) { return 'err:' + e.message; } })()`;
3372
+ return rsSuccess("recruit-stage.interview.arrange.prefill", {
3373
+ mode: "prefill",
3374
+ action: "\u5B89\u6392\u9762\u8BD5\uFF08\u6253\u5F00\u8868\u5355 + \u9009\u9762\u8BD5\u65B9\u5F0F\uFF0C\u4EBA\u5DE5\u6838\u5BF9\u4FDD\u5B58\uFF1B\u4E0D\u843D\u5E93\uFF09",
3375
+ talentId,
3376
+ interviewFormId: formId,
3377
+ interviewType: typeKey,
3378
+ interviewTypeOptionId: typeId,
3379
+ talentCardUrl: cardUrl,
3380
+ interviewListUrl: listUrl,
3381
+ jsExpr,
3382
+ steps: [
3383
+ "1) \u6D4F\u89C8\u5668\u6253\u5F00 talentCardUrl\uFF08\u9700\u5DF2\u767B\u5F55\u8BE5\u73AF\u5883\uFF09\u2192 \u8FDB\u5165\u8BE5\u5019\u9009\u4EBA\u5361\u7247",
3384
+ "2) \u70B9\u5361\u7247\u4E0A\u7684\u300C\u5B89\u6392\u9762\u8BD5\u300D\u2192 \u6253\u5F00\u9762\u8BD5\u5B89\u6392\u8868\u5355\uFF08\u4E3B\u9898/\u5019\u9009\u4EBA/\u804C\u4F4D\u7531\u8868\u5355\u9ED8\u8BA4\u503C\u81EA\u52A8\u5E26\u5165\uFF09",
3385
+ "3) \u5728\u9875\u9762\u4E0A\u4E0B\u6587\u6267\u884C jsExpr \u2192 \u9009\u4E2D\u300C\u9762\u8BD5\u65B9\u5F0F=" + typeKey + "\u300D\u5E76\u89E6\u53D1\u8054\u52A8\uFF08AI\u9762\u8BD5\u65B9\u6848/\u9762\u8BD5\u5B98/\u5F00\u59CB\u7ED3\u675F\u65F6\u95F4\u81EA\u52A8\u5E26\u5165\uFF09",
3386
+ "4) \u7531 HR \u6838\u5BF9\uFF08\u53EF\u6309\u9700\u8C03\u6574\uFF09\u540E\u70B9\u300C\u4FDD\u5B58\u300D\u5B8C\u6210\u9762\u8BD5\u5B89\u6392\uFF1B\u672C\u6A21\u5F0F\u4E0D\u5199\u4EFB\u4F55\u6570\u636E",
3387
+ "5)\uFF08\u53EF\u9009\uFF09\u6253\u5F00 interviewListUrl \u67E5\u770B\u9762\u8BD5\u7BA1\u7406\u5217\u8868"
3388
+ ]
3389
+ });
3390
+ }
3391
+ var interviewArrangeOperations = [
3392
+ {
3393
+ name: "recruit-stage.interview.arrange.prepare",
3394
+ resource: "recruit.interview",
3395
+ method: "prepare",
3396
+ action: "interview.arrange.prepare",
3397
+ summary: "\u5B89\u6392\u9762\u8BD5\u786E\u8BA4\u6458\u8981\uFF1A\u5B9A\u4F4D\u5019\u9009\u4EBA\uFF08talentId/\u624B\u673A\u53F7/\u59D3\u540D\u4E09\u9009\u4E00\uFF09\u3001\u89E3\u6790\u9762\u8BD5\u65B9\u5F0F/\u8F6E\u6B21\u679A\u4E3E\u3001AI \u9762\u8BD5\u65F6\u5217\u5019\u9009\u65B9\u6848\uFF08\u7EDD\u4E0D\u81EA\u52A8\u53D6\u7B2C\u4E00\u6761\uFF09\u4E0E\u8054\u52A8\u5FC5\u586B\u6838\u5BF9\uFF0C\u4E0D\u5199\u5165",
3398
+ capability: "recruit.interview.arrange",
3399
+ risk: "write",
3400
+ requiresConfirmation: true,
3401
+ inputSchema: schemaOf(baseInput3, {}),
3402
+ handler: prepare3
3403
+ },
3404
+ {
3405
+ name: "recruit-stage.interview.arrange.apply",
3406
+ resource: "recruit.interview",
3407
+ method: "apply",
3408
+ action: "interview.arrange.apply",
3409
+ summary: "\u7528\u6237\u786E\u8BA4\u540E\u5B89\u6392\u9762\u8BD5\uFF1AsaveFormData \u2192 \u6309\u300C\u4FDD\u5B58\u300D\u6309\u94AE\u52A8\u4F5C\u94FE\u6267\u884C\uFF08ESB \u65E5\u7A0B/\u5206\u914D\u9762\u8BD5/AI \u9762\u8BD5\u63D0\u9192\uFF09\uFF0C\u4E0E\u9875\u9762\u70B9\u4FDD\u5B58\u7B49\u4EF7\uFF1B\u8FD4\u56DE\u53CC\u94FE\u63A5",
3410
+ capability: "recruit.interview.arrange",
3411
+ risk: "write",
3412
+ requiresConfirmation: true,
3413
+ inputSchema: schemaOf({ ...baseInput3, confirm: { type: "boolean" }, continuation: { type: "string" } }, {}),
3414
+ handler: apply3
3415
+ },
3416
+ {
3417
+ name: "recruit-stage.interview.arrange.prefill",
3418
+ resource: "recruit.interview",
3419
+ method: "prefill",
3420
+ action: "interview.arrange.prefill",
3421
+ summary: "\u9884\u586B\u6A21\u5F0F\uFF08\u4EBA\u5DE5\u4FDD\u5B58\uFF09\uFF1A\u8F93\u51FA\u4EBA\u624D\u5361\u7247 URL + \u9762\u8BD5\u7BA1\u7406\u5217\u8868 URL + \u9009\u9762\u8BD5\u65B9\u5F0F\u7684 jsExpr\uFF0C\u89E6\u53D1\u8868\u5355\u8054\u52A8\u540E\u7531 HR \u6838\u5BF9\u4FDD\u5B58\uFF0C\u4E0D\u843D\u5E93",
3422
+ capability: "recruit.interview.arrange",
3423
+ risk: "read",
3424
+ inputSchema: schemaOf(baseInput3, {}),
3425
+ handler: prefill2
3426
+ }
3427
+ ];
3428
+
3429
+ // src/shortcuts/recruit-stage/operations/interview-feedback.ts
3430
+ var INVALID_STATUS_ID = "841052191228968977";
3431
+ var R = (v) => String(v ?? "").replace(/^<p>|<\/p>$/g, "").trim();
3432
+ async function locateTalentId2(host, cache, args) {
3433
+ if (args.talentId) return String(args.talentId);
3434
+ if (!args.mobile) throw rsValidationError("locate_required", "\u8BF7\u63D0\u4F9B talentId \u6216 mobile");
3435
+ const listId = cache.forms?.talent_pool?.listId;
3436
+ if (!listId) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u4EBA\u624D\u5217\u8868 listId");
3437
+ const poolObjId = cache.forms.talent_pool.objId;
3438
+ const r = await host.request({
3439
+ path: "/api/ebuilder/form/list/getListDatas?passid=" + poolObjId + "&cid=" + poolObjId,
3440
+ body: {
3441
+ listId,
3442
+ cid: poolObjId,
3443
+ passid: poolObjId,
3444
+ searchParamData: JSON.stringify({ searchParamList: [{ key: "field_mobile", value: args.mobile, operator: "=" }] }),
3445
+ current: 1,
3446
+ pageSize: 10,
3447
+ tableuid: randHex32()
3448
+ }
3449
+ });
3450
+ const rows = r?.data?.rows || r?.data || [];
3451
+ if (!rows.length) throw rsFail("\u672A\u627E\u5230\u624B\u673A\u53F7\u4E3A " + args.mobile + " \u7684\u5019\u9009\u4EBA");
3452
+ if (rows.length > 1) throw rsFail("\u8BE5\u624B\u673A\u53F7\u547D\u4E2D\u591A\u6761\u5019\u9009\u4EBA\uFF0C\u8BF7\u63D0\u4F9B talentId \u7CBE\u786E\u5B9A\u4F4D");
3453
+ return String(rows[0].talentId || rows[0].form_data_id);
3454
+ }
3455
+ async function getBatchId(host, talentId) {
3456
+ const rk = "randomkey_" + randHex32().slice(0, 6);
3457
+ const r = await host.request({ path: "/api/recruit/candidate/getCandidateCardPrepareQuery?talentId=" + talentId + "&" + rk + "=" + rk.split("_")[1], method: "GET" });
3458
+ return r?.data?.batchId;
3459
+ }
3460
+ async function listInterviewFeedback(host, talentId, batchId) {
3461
+ const r = await host.request({
3462
+ path: "/api/recruit/candidate/getCandidateCardInterviewInfo",
3463
+ method: "POST",
3464
+ body: { talentId, batchId, current: 1, pageSize: 20 }
3465
+ });
3466
+ const list = r?.data?.list || [];
3467
+ const out = [];
3468
+ for (let i = 0; i < list.length; i++) {
3469
+ const it = list[i];
3470
+ const fbs = it?.interviewFeedBack || [];
3471
+ if (fbs.length) {
3472
+ const fb = fbs[0];
3473
+ out.push({
3474
+ index: i,
3475
+ planId: (fb.interviewPlanId || [])[0]?.id,
3476
+ planName: (fb.interviewPlanId || [])[0]?.name || (it.subject || ""),
3477
+ interviewer: (fb.interviewer || []).map((x) => x.name).join("\u3001") || (it.interviewer || ""),
3478
+ feedbackId: fb.id || fb.formDataId,
3479
+ result: (fb.result || []).map((x) => x.name).join("\u3001") || "",
3480
+ judgement: R(fb.judgement),
3481
+ unionStatusId: (it.unionStatus || [])[0]?.id || "",
3482
+ unionStatusName: (it.unionStatus || [])[0]?.name || ""
3483
+ });
3484
+ }
3485
+ }
3486
+ return out;
3487
+ }
3488
+ async function buildTarget(env, args) {
3489
+ const host = env.host;
3490
+ const session = await host.getSession();
3491
+ const cache = await env.recruitContext();
3492
+ const baseUrl = session.baseUrl.replace(/\/+$/, "");
3493
+ const talentId = await locateTalentId2(host, cache, args);
3494
+ const batchId = await getBatchId(host, talentId);
3495
+ const list = await listInterviewFeedback(host, talentId, batchId);
3496
+ if (!list.length) throw rsFail("\u8BE5\u5019\u9009\u4EBA\u6CA1\u6709\u53EF\u7F16\u8F91\u7684\u9762\u8BD5\u53CD\u9988\u8BB0\u5F55");
3497
+ let target;
3498
+ if (args.feedbackId) {
3499
+ target = list.find((x) => String(x.feedbackId) === String(args.feedbackId));
3500
+ if (!target) throw rsFail("\u672A\u627E\u5230\u53CD\u9988\u8BB0\u5F55 id=" + args.feedbackId + "\uFF0C\u53EF\u9009\uFF1A" + list.map((x) => x.feedbackId).join(","));
3501
+ } else {
3502
+ const idx = args.planIndex !== void 0 ? Number(args.planIndex) : 0;
3503
+ if (args.planIndex !== void 0 && !list[idx]) throw rsFail("planIndex \u8D85\u51FA\u8303\u56F4\uFF0C\u5171 " + list.length + " \u573A\u542B\u53CD\u9988");
3504
+ target = list[idx];
3505
+ }
3506
+ if (target.unionStatusId === INVALID_STATUS_ID) {
3507
+ throw rsFail("\u8BE5\u9762\u8BD5\u72B6\u6001\u4E3A\u300C\u5DF2\u5931\u6548\u300D\uFF0C\u4E0D\u5141\u8BB8\u586B\u5199/\u7F16\u8F91\u9762\u8BD5\u53CD\u9988\uFF08\u4E0E\u7CFB\u7EDF\u6309\u94AE\u6761\u4EF6\u4E00\u81F4\uFF1A\u5DF2\u5931\u6548\u4E0D\u663E\u793A\u53CD\u9988\u6309\u94AE\uFF09\u3002\u9762\u8BD5=" + target.planName + "\uFF0C\u53CD\u9988\u8BB0\u5F55Id=" + target.feedbackId);
3508
+ }
3509
+ const objId = objIdOf(cache, "interview_detail");
3510
+ const formId = formIdOf(cache, "interview_detail");
3511
+ const fields = cache.fields?.[objId] || {};
3512
+ const resultFid = fields.result;
3513
+ const judgementFid = fields.judgement;
3514
+ if (!resultFid || !judgementFid) throw rsFail("\u73AF\u5883\u7F13\u5B58\u7F3A\u5C11\u9762\u8BD5\u53CD\u9988\u7684 result/judgement \u5B57\u6BB5\u6620\u5C04");
3515
+ return { host, baseUrl, appId: cache.appId, cache, talentId, batchId, list, target, objId, formId, fields, resultFid, judgementFid };
3516
+ }
3517
+ async function prepare4(args, env) {
3518
+ if (!args.result && args.comment === void 0) {
3519
+ throw rsValidationError("result_or_comment_required", "\u8BF7\u63D0\u4F9B result\uFF08\u901A\u8FC7/\u4E0D\u901A\u8FC7/\u5F85\u5B9A\uFF09\u548C/\u6216 comment\uFF08\u8BC4\u4EF7\uFF09");
3520
+ }
3521
+ const t = await buildTarget(env, args);
3522
+ const rec = await getFormDataRecord(t.host, t.formId, String(t.target.feedbackId), t.objId);
3523
+ if (!rec.id) throw rsFail("\u65E0\u6CD5\u83B7\u53D6\u53CD\u9988\u8BB0\u5F55\u6570\u636E\uFF08formData \u4E3A\u7A7A\uFF09\uFF0C\u53EF\u80FD\u65E0\u6743\u9650\u6216\u8BB0\u5F55\u4E0D\u5B58\u5728");
3524
+ const btns = await getFormButtonsForData(t.host, t.objId, t.formId, String(t.target.feedbackId));
3525
+ const submitBtn = findSubmitButton(btns);
3526
+ const analysis = submitBtn ? analyzeButtonActions(submitBtn) : null;
3527
+ let newOptionId = null;
3528
+ if (args.result) {
3529
+ const opts = cachedDsMap(t.cache, t.objId, "result") || await getDsOptions(t.host, t.objId, t.resultFid, t.appId);
3530
+ newOptionId = opts[String(args.result)];
3531
+ if (!newOptionId) throw rsFail("\u672A\u8BC6\u522B\u7684\u9762\u8BD5\u7ED3\u679C\u300C" + args.result + "\u300D\uFF0C\u53EF\u9009\uFF1A" + Object.keys(opts).join("/"));
3532
+ }
3533
+ const newComment = args.comment !== void 0 ? String(args.comment) : t.target.judgement;
3534
+ const newResultName = args.result ? String(args.result) : t.target.result;
3535
+ const willSubmit = { feedbackId: String(t.target.feedbackId), result: newResultName, comment: newComment };
3536
+ const { createHash: createHash3 } = await import("node:crypto");
3537
+ const fp = createHash3("sha256").update(JSON.stringify(willSubmit)).digest("hex");
3538
+ const continuation = await env.continuation.issue({ operation: "recruit-stage.interview.feedback.edit.apply", fingerprint: fp });
3539
+ return rsSuccess("recruit-stage.interview.feedback.edit.prepare", {
3540
+ mode: "prepare",
3541
+ talentId: t.talentId,
3542
+ batchId: t.batchId,
3543
+ \u9762\u8BD5: t.target.planName,
3544
+ \u9762\u8BD5\u5B98: t.target.interviewer,
3545
+ \u53CD\u9988\u8BB0\u5F55Id: t.target.feedbackId,
3546
+ \u5F53\u524D\u53CD\u9988: (t.target.result ? "\u7ED3\u679C " + t.target.result : "") + (t.target.judgement ? "\uFF1B\u8BC4\u4EF7 " + t.target.judgement : ""),
3547
+ \u65B0\u53CD\u9988: (args.result ? "\u7ED3\u679C " + newResultName : "") + (args.comment !== void 0 ? "\uFF1B\u8BC4\u4EF7 " + newComment : ""),
3548
+ \u5C06\u6267\u884C\u52A8\u4F5C: analysis ? {
3549
+ \u4FDD\u5B58\u53CD\u9988\u8868\u5355: "saveFormData\uFF08\u5168\u5B57\u6BB5\u96C6 + updateTimeStamp \u4E50\u89C2\u9501\uFF09",
3550
+ \u52A8\u4F5C\u6D41: analysis.esbIds.length ? analysis.esbIds : "\uFF08\u8BE5\u6309\u94AE\u65E0 esb \u52A8\u4F5C\u6D41\uFF09",
3551
+ \u62DB\u8058API: analysis.jsCalls.map((c) => c.url + (Object.keys(c.data).length ? " data=" + Object.keys(c.data).join(",") : ""))
3552
+ } : "\u26A0\uFE0F \u672A\u5B9A\u4F4D\u5230\u300C\u63D0\u4EA4\u8BC4\u4EF7\u300D\u6309\u94AE\uFF08getFormButtons \u672A\u53D6\u5230\u4FDD\u5B58\u6309\u94AE\uFF09\uFF0C\u4EC5\u80FD\u6267\u884C\u4FDD\u5B58+\u5199\u8FC7\u7A0B\u65E5\u5FD7",
3553
+ willSubmit
3554
+ }, { continuation, fingerprint: fp, confirmHint: "\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u643A\u5E26 confirm:true \u4E0E continuation \u8C03\u7528 apply" });
3555
+ }
3556
+ async function apply4(args, env) {
3557
+ if (args.confirm !== true) throw rsConfirmationError("recruit-stage.interview.feedback.edit.apply", { hint: "\u5199\u64CD\u4F5C\u5FC5\u987B\u663E\u5F0F confirm:true" });
3558
+ if (!args.continuation) throw rsValidationError("continuation_required", "\u7F3A\u5C11 continuation\uFF08\u6765\u81EA prepare \u7684 meta\uFF09");
3559
+ const payload = await env.continuation.verify(String(args.continuation), "recruit-stage.interview.feedback.edit.apply");
3560
+ const t = await buildTarget(env, args);
3561
+ const rec2 = await getFormDataRecord(t.host, t.formId, String(t.target.feedbackId), t.objId);
3562
+ const details2 = toSubmitDetails(rec2.dataDetails);
3563
+ let newOptionId = null;
3564
+ if (args.result) {
3565
+ const opts = cachedDsMap(t.cache, t.objId, "result") || await getDsOptions(t.host, t.objId, t.resultFid, t.appId);
3566
+ newOptionId = opts[String(args.result)];
3567
+ if (!newOptionId) throw rsFail("\u672A\u8BC6\u522B\u7684\u9762\u8BD5\u7ED3\u679C\u300C" + args.result + "\u300D\uFF0C\u53EF\u9009\uFF1A" + Object.keys(opts).join("/"));
3568
+ }
3569
+ const newComment = args.comment !== void 0 ? String(args.comment) : t.target.judgement;
3570
+ const newResultName = args.result ? String(args.result) : t.target.result;
3571
+ const willSubmit = { feedbackId: String(t.target.feedbackId), result: newResultName, comment: newComment };
3572
+ const { createHash: createHash3 } = await import("node:crypto");
3573
+ if (payload.fingerprint !== createHash3("sha256").update(JSON.stringify(willSubmit)).digest("hex")) {
3574
+ throw rsValidationError("fingerprint_mismatch", "\u5F85\u63D0\u4EA4\u5185\u5BB9\u4E0E prepare \u786E\u8BA4\u6458\u8981\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 prepare \u786E\u8BA4\u540E\u518D apply");
3575
+ }
3576
+ const newDetails = details2.map((x) => {
3577
+ const fid = x.formField.id;
3578
+ if (fid === t.resultFid && newOptionId) return { formField: { id: fid }, dataOptions: [{ optionId: newOptionId, content: newResultName }] };
3579
+ if (fid === t.judgementFid) return { formField: { id: fid }, dataText: { content: "<p>" + (newComment || "") + "</p>" } };
3580
+ return x;
3581
+ });
3582
+ if (newOptionId && !newDetails.some((x) => x.formField.id === t.resultFid)) {
3583
+ newDetails.push({ formField: { id: t.resultFid }, dataOptions: [{ optionId: newOptionId, content: newResultName }] });
3584
+ }
3585
+ if (args.comment !== void 0 && !newDetails.some((x) => x.formField.id === t.judgementFid)) {
3586
+ newDetails.push({ formField: { id: t.judgementFid }, dataText: { content: "<p>" + (newComment || "") + "</p>" } });
3587
+ }
3588
+ const savedId = await saveFormData(t.host, {
3589
+ appId: t.appId,
3590
+ formId: t.formId,
3591
+ objId: t.objId,
3592
+ name: "\u9762\u8BD5\u53CD\u9988-" + t.target.planName,
3593
+ formDataId: String(t.target.feedbackId),
3594
+ updateTimeStamp: rec2.updateTimeStamp,
3595
+ dataDetails: newDetails,
3596
+ rightType: "2",
3597
+ layoutId: rec2.formLayoutId,
3598
+ extraCustomParam: { secretLevel: "", secretLevelValidity: "" }
3599
+ });
3600
+ const btns = await getFormButtonsForData(t.host, t.objId, t.formId, String(t.target.feedbackId));
3601
+ const submitBtn = findSubmitButton(btns);
3602
+ const analysis = submitBtn ? analyzeButtonActions(submitBtn) : null;
3603
+ let esbRes = null;
3604
+ if (analysis?.esbIds?.length) {
3605
+ esbRes = await runEsb(t.host, { objId: t.objId, dataIds: String(t.target.feedbackId), esbActionIds: analysis.esbIds.join(",") });
3606
+ }
3607
+ let processRes = null;
3608
+ if (analysis?.jsCalls?.length) {
3609
+ const call = analysis.jsCalls.find((c) => c.url.includes("/api/recruit/")) || analysis.jsCalls[0];
3610
+ processRes = await t.host.request({
3611
+ path: call.url,
3612
+ method: (call.method || "post").toUpperCase(),
3613
+ body: {
3614
+ talentId: String(t.talentId),
3615
+ batchId: String(t.batchId),
3616
+ result: newOptionId || t.target.result,
3617
+ judgement: "<p>" + (newComment || "") + "</p>",
3618
+ businessId: String(t.target.feedbackId),
3619
+ processEnum: "20"
3620
+ }
3621
+ });
3622
+ }
3623
+ const back = await listInterviewFeedback(t.host, t.talentId, t.batchId);
3624
+ const backFb = back.find((x) => String(x.feedbackId) === String(t.target.feedbackId));
3625
+ const changed = backFb && String(backFb.result).includes(newResultName) && (args.comment === void 0 || String(backFb.judgement).includes(newComment));
3626
+ const cardPageId = String(t.cache.ebdDetailPageId || "");
3627
+ const talentExtra = String(t.cache.selfUse) === "1" ? "&recruitType=useSelf" : "";
3628
+ const talentCardUrl = cardPageId ? `${t.baseUrl}/sp/ebdpage/view/${cardPageId}?talentId=${t.talentId}${talentExtra}` : null;
3629
+ const interviewListId = String(t.cache.lists?.interview?.listId || "");
3630
+ const interviewListUrl = interviewListId ? `${t.baseUrl}/sp/ebdfpage/list/${interviewListId}` : null;
3631
+ return rsSuccess("recruit-stage.interview.feedback.edit.apply", {
3632
+ mode: "apply",
3633
+ result: "success",
3634
+ saveFormData: { \u6210\u529F: !!savedId, \u8BB0\u5F55Id: savedId },
3635
+ runEsb: esbRes ? { \u52A8\u4F5C\u6D41\u6570: analysis?.esbIds.length ?? 0, \u8FD4\u56DE: esbRes?.successNum ?? esbRes?.status ?? "ok" } : "\u65E0 esb \u52A8\u4F5C\u6D41",
3636
+ addProcessLog: processRes ? { \u8FD4\u56DE: processRes?.data ?? processRes?.status ?? "ok" } : "\u65E0 jsCode \u62DB\u8058 API",
3637
+ \u56DE\u67E5: backFb ? { \u7ED3\u679C: backFb.result, \u8BC4\u4EF7: backFb.judgement, \u5DF2\u751F\u6548: changed } : "\u56DE\u67E5\u672A\u53D6\u5230\u8BE5\u53CD\u9988",
3638
+ talentId: t.talentId,
3639
+ batchId: t.batchId,
3640
+ \u9762\u8BD5: t.target.planName,
3641
+ talentCardUrl,
3642
+ interviewListUrl
3643
+ });
3644
+ }
3645
+ var interviewFeedbackOperations = [
3646
+ {
3647
+ name: "recruit-stage.interview.feedback.edit.prepare",
3648
+ resource: "recruit.interview",
3649
+ method: "prepare",
3650
+ action: "interview.feedback.edit.prepare",
3651
+ summary: "\u9762\u8BD5\u53CD\u9988\u786E\u8BA4\u6458\u8981\uFF1A\u5B9A\u4F4D\u53CD\u9988\u8BB0\u5F55\uFF08feedbackId \u6216 planIndex\uFF09\u3001\u6309\u94AE\u52A8\u4F5C\u667A\u80FD\u89E3\u6790\uFF08\u4FDD\u5B58+esb+\u62DB\u8058API\uFF09\u3001\u5F53\u524D/\u65B0\u53CD\u9988\u5BF9\u7167\uFF0C\u4E0D\u5199\u5165",
3652
+ capability: "recruit.interview.feedback",
3653
+ risk: "write",
3654
+ requiresConfirmation: true,
3655
+ inputSchema: schemaOf({
3656
+ talentId: { type: "string", description: "\u4EBA\u624D id\uFF08\u4E0E mobile \u4E8C\u9009\u4E00\uFF09" },
3657
+ mobile: { type: "string", pattern: "^\\d{6,20}$", description: "\u624B\u673A\u53F7\u5B9A\u4F4D\uFF08\u552F\u4E00\u547D\u4E2D\uFF09" },
3658
+ feedbackId: { type: "string", description: "\u53CD\u9988\u8BB0\u5F55 id\uFF08\u6700\u7A33\uFF1B\u7F3A\u7701\u7528 planIndex \u9009\u7B2C\u51E0\u573A\uFF09" },
3659
+ planIndex: { type: "integer", minimum: 0, description: "\u7B2C\u51E0\u573A\u542B\u53CD\u9988\u7684\u9762\u8BD5\uFF08\u9ED8\u8BA4 0\uFF09" },
3660
+ result: { type: "string", description: "\u9762\u8BD5\u7ED3\u679C\uFF08\u5982 \u901A\u8FC7/\u4E0D\u901A\u8FC7/\u5F85\u5B9A\uFF0C\u6309\u73AF\u5883\u679A\u4E3E\uFF09" },
3661
+ comment: { type: "string", description: "\u8BC4\u4EF7\u6B63\u6587" }
3662
+ }, {}),
3663
+ handler: prepare4
3664
+ },
3665
+ {
3666
+ name: "recruit-stage.interview.feedback.edit.apply",
3667
+ resource: "recruit.interview",
3668
+ method: "apply",
3669
+ action: "interview.feedback.edit.apply",
3670
+ summary: "\u7528\u6237\u786E\u8BA4\u540E\u63D0\u4EA4\u53CD\u9988\uFF1AsaveFormData\uFF08\u4E50\u89C2\u9501\u5168\u5B57\u6BB5\u96C6\uFF09\u2192 runEsb \u52A8\u4F5C\u6D41 \u2192 addProcessLog \u8FC7\u7A0B\u65E5\u5FD7 \u2192 \u56DE\u67E5\u6821\u9A8C",
3671
+ capability: "recruit.interview.feedback",
3672
+ risk: "write",
3673
+ requiresConfirmation: true,
3674
+ inputSchema: schemaOf({
3675
+ talentId: { type: "string" },
3676
+ mobile: { type: "string", pattern: "^\\d{6,20}$" },
3677
+ feedbackId: { type: "string" },
3678
+ planIndex: { type: "integer", minimum: 0 },
3679
+ result: { type: "string" },
3680
+ comment: { type: "string" },
3681
+ confirm: { type: "boolean" },
3682
+ continuation: { type: "string" }
3683
+ }, {}),
3684
+ handler: apply4
3685
+ }
3686
+ ];
3687
+
3688
+ // src/shortcuts/recruit-stage/operations/list.ts
3689
+ var TYPES = {
3690
+ demand: { listKey: "demand", formType: "demand_manage", title: "\u62DB\u8058\u9700\u6C42", menuNames: ["\u62DB\u8058\u9700\u6C42", "\u9700\u6C42"] },
3691
+ position: { listKey: "position", formType: "position_manage", title: "\u62DB\u8058\u804C\u4F4D", menuNames: ["\u62DB\u8058\u804C\u4F4D", "\u804C\u4F4D\u5217\u8868", "\u804C\u4F4D\u7BA1\u7406"] },
3692
+ entry: { listKey: "entry", formType: "entry_manage", title: "\u5165\u804C\u7BA1\u7406", menuNames: ["\u5165\u804C\u7BA1\u7406"] },
3693
+ offer: { listKey: "offer", formType: "offer", title: "\u5F55\u7528\u7BA1\u7406", menuNames: ["\u5F55\u7528\u7BA1\u7406", "offer\u7BA1\u7406", "offer"] },
3694
+ interview: { listKey: "interview", formType: "interview_plan", title: "\u9762\u8BD5\u7BA1\u7406", menuNames: ["\u9762\u8BD5\u7BA1\u7406", "\u9762\u8BD5\u5B89\u6392\u5217\u8868", "\u9762\u8BD5\u5B89\u6392"] },
3695
+ candidate: { listKey: "candidate", formType: "candidate_manage", title: "\u5E94\u8058\u8005", menuNames: ["\u5E94\u8058\u8005", "\u5E94\u8058\u7BA1\u7406", "\u5019\u9009\u4EBA", "\u5E94\u8058\u6279\u6B21"] }
3696
+ };
3697
+ var DISPLAY_ALIASES = {
3698
+ candidate_name: ["talent_id", "name"],
3699
+ position_name: ["position_manage_id", "position"],
3700
+ interview_type_key: ["interview_type"],
3701
+ union_status_key: ["union_status"],
3702
+ stage_name: ["stage_type_key", "current_stage"]
3703
+ };
3704
+ var FILTER_ARGS = {
3705
+ positionId: { dataKey: "position_id", label: "\u804C\u4F4D" },
3706
+ talentId: { dataKey: "talent_id", label: "\u4EBA\u624D" },
3707
+ candidateManageId: { dataKey: "candidate_manage_id", label: "\u5E94\u8058\u6279\u6B21" }
3708
+ };
3709
+ var INTERNAL_KEYS = /* @__PURE__ */ new Set([
3710
+ "id",
3711
+ "form_data_id",
3712
+ "form_table_id",
3713
+ "rowKey",
3714
+ "ebrecord_rowkey",
3715
+ "isFollow",
3716
+ "is_flow",
3717
+ "data_status",
3718
+ "data_status_value",
3719
+ "update_time",
3720
+ "formDataId",
3721
+ "isFollowSearch",
3722
+ "isShareToMe"
3723
+ ]);
3724
+ var DISPLAY_UI_KEYS = /* @__PURE__ */ new Set([...INTERNAL_KEYS, "operate", "isToDo", "todoPropSpan", "flow_status"]);
3725
+ var ENUM_SHORT_FIELDS = /* @__PURE__ */ new Set([
3726
+ "entry_status",
3727
+ "transact_status",
3728
+ "demand_status",
3729
+ "position_status",
3730
+ "offer_status",
3731
+ "talent_status",
3732
+ "channel_status"
3733
+ ]);
3734
+ async function runList(env, args, feedbackTodoMode) {
3735
+ const host = env.host;
3736
+ const session = await host.getSession();
3737
+ const baseUrl = session.baseUrl.replace(/\/+$/, "");
3738
+ const cache = await env.recruitContext();
3739
+ const appId = cache.appId;
3740
+ const type = String(args.type || (feedbackTodoMode ? "interview" : ""));
3741
+ if (!type || !TYPES[type]) {
3742
+ throw rsFail("type \u5FC5\u987B\u662F demand|position|entry|offer|interview|candidate \u4E4B\u4E00");
3743
+ }
3744
+ const t = TYPES[type];
3745
+ const pageSize = Number(args.pageSize || (feedbackTodoMode ? 200 : 10));
3746
+ const current = Number(args.current || 1);
3747
+ if (feedbackTodoMode) {
3748
+ args.all = true;
3749
+ }
3750
+ const listParam = cache.lists[t.listKey] || (cache.lists?.all_search || []).find((m) => m.listId && t.menuNames.some((n) => (m.menuName || "").trim() === n)) || (cache.lists?.all_search || []).find((m) => m.listId && t.menuNames.some((n) => (m.menuName || "").includes(n)));
3751
+ if (!listParam?.listId) {
3752
+ throw rsFail("\u5F53\u524D\u73AF\u5883\u672A\u542F\u7528\u300C" + t.title + "\u300D\u529F\u80FD\uFF08\u672A\u627E\u5230\u5BF9\u5E94\u83DC\u5355/\u5217\u8868\u53C2\u6570\uFF0C\u6309\u80FD\u529B\u63A2\u6D4B\u62E6\u622A\uFF09");
3753
+ }
3754
+ const listId = listParam.listId;
3755
+ const cusMenuId = listParam.cusMenuId;
3756
+ const menuName = listParam.menuName || t.title;
3757
+ const formObjId = cache.forms[t.formType]?.objId;
3758
+ const fieldMap = formObjId && cache.fields[formObjId] || {};
3759
+ const commonSearchFields = {};
3760
+ const appliedFilters = [];
3761
+ for (const [argKey, def] of Object.entries(FILTER_ARGS)) {
3762
+ const val = args[argKey];
3763
+ if (!val) continue;
3764
+ const fieldId = fieldMap[def.dataKey];
3765
+ if (!fieldId) continue;
3766
+ const mark = "field_" + fieldId;
3767
+ commonSearchFields[mark] = [{ [mark]: String(val), [mark + "_condition_type"]: "like" }];
3768
+ appliedFilters.push(def.label);
3769
+ }
3770
+ const baseBody = {
3771
+ current,
3772
+ pageSize,
3773
+ tableuid: randHex32(),
3774
+ listId,
3775
+ searchParamData: { searchType: "3", type: "and", ...appliedFilters.length ? { commonSearchFields } : {} },
3776
+ terminal: "PC",
3777
+ browser: "Chrome",
3778
+ requiredMatch: 1,
3779
+ mapResult: 1,
3780
+ recheckQuery: 1,
3781
+ isFollow: 0,
3782
+ isShareToMe: 0,
3783
+ filterCondition: {},
3784
+ cusMenuId,
3785
+ urlPageTitle: t.title,
3786
+ menuName
3787
+ };
3788
+ const extra = { ebBusinessId: listId };
3789
+ const defaultPages = feedbackTodoMode ? 5 : 20;
3790
+ const wantPages = args.all ? Math.max(1, Number(args.maxPages || defaultPages)) : 1;
3791
+ const rawRows = [];
3792
+ const dispRows = [];
3793
+ let hasDisplay = false;
3794
+ let cntTotal = null;
3795
+ let lastGot = 0;
3796
+ for (let i = 0; i < wantPages; i++) {
3797
+ const body = { ...baseBody, current: current + i };
3798
+ const cntBody = { ...body };
3799
+ delete cntBody.current;
3800
+ delete cntBody.pageSize;
3801
+ const [resp, c] = await Promise.all([
3802
+ host.request({
3803
+ path: "/api/ebuilder/form/list/getListDatas?passid=" + formObjId + "&cid=" + formObjId,
3804
+ body,
3805
+ headers: extra
3806
+ }),
3807
+ i === 0 ? host.request({
3808
+ path: "/api/ebuilder/form/list/getListDataCount?passid=" + formObjId + "&cid=" + formObjId,
3809
+ body: cntBody,
3810
+ headers: extra
3811
+ }).catch(() => null) : Promise.resolve(null)
3812
+ ]);
3813
+ if (c && typeof c?.data?.total === "number") cntTotal = c.data.total;
3814
+ const rRaw = resp?.data?.data || [];
3815
+ const rDisp = resp?.data?.displayData;
3816
+ if (Array.isArray(rDisp) && rDisp.length) {
3817
+ hasDisplay = true;
3818
+ dispRows.push(...rDisp);
3819
+ }
3820
+ rawRows.push(...rRaw);
3821
+ const got = Array.isArray(rDisp) && rDisp.length ? rDisp.length : rRaw.length;
3822
+ lastGot = got;
3823
+ if (got < pageSize) break;
3824
+ if (cntTotal !== null && (current + i) * pageSize >= cntTotal) break;
3825
+ }
3826
+ const rows = hasDisplay ? dispRows : rawRows;
3827
+ let workRows = rows;
3828
+ const clientFilters = [];
3829
+ for (const [argKey, def] of Object.entries(FILTER_ARGS)) {
3830
+ if (args[argKey]) clientFilters.push([def.dataKey, String(args[argKey])]);
3831
+ }
3832
+ if (clientFilters.length) {
3833
+ workRows = workRows.filter((r) => clientFilters.every(([dk, val]) => String(r[dk] ?? "") === val));
3834
+ }
3835
+ if (args.recent) {
3836
+ const days = Number(args.recent);
3837
+ const cutoff = Date.now() - days * 864e5;
3838
+ workRows = workRows.filter((r) => {
3839
+ const raw = String(r.start_time || r.interview_time || "");
3840
+ const ts = Date.parse(raw.replace(/-/g, "/"));
3841
+ return !Number.isNaN(ts) && ts >= cutoff;
3842
+ });
3843
+ }
3844
+ if (args.sort) {
3845
+ const k = String(args.sort);
3846
+ workRows = [...workRows].sort((a, b) => {
3847
+ const av = String(a[k] ?? ""), bv = String(b[k] ?? "");
3848
+ return av < bv ? 1 : av > bv ? -1 : 0;
3849
+ });
3850
+ }
3851
+ const reachedEnd = lastGot < pageSize;
3852
+ let total;
3853
+ let totalExact;
3854
+ let totalNote;
3855
+ if (cntTotal !== null) {
3856
+ total = cntTotal;
3857
+ totalExact = true;
3858
+ } else if (reachedEnd) {
3859
+ total = rows.length;
3860
+ totalExact = true;
3861
+ totalNote = "\u8BA1\u6570\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u5DF2\u5230\u672B\u9875\uFF0C\u603B\u6570\u6309\u5DF2\u53D6\u56DE\u884C\u6570\u5F97\u51FA";
3862
+ } else {
3863
+ total = null;
3864
+ totalExact = false;
3865
+ totalNote = "\u8BA1\u6570\u63A5\u53E3\u4E0D\u53EF\u7528\u4E14\u672C\u9875\u5DF2\u6EE1\uFF0C\u603B\u6570\u4E0D\u53EF\u77E5\uFF08\u53EF\u52A0\u5927 pageSize \u6216\u5F00 all \u53D6\u56DE\u5168\u90E8\u884C\u6570\uFF09";
3866
+ }
3867
+ if (args.all && rows.length >= wantPages * pageSize && (cntTotal === null || cntTotal > rows.length)) {
3868
+ totalNote = `\u5DF2\u53D6\u56DE ${rows.length} \u884C\uFF08\u8FBE maxPages \u4E0A\u9650 ${wantPages} \u9875\uFF09\uFF0C\u53EF\u80FD\u4ECD\u6709\u66F4\u591A\uFF1B\u53EF\u589E\u5927 maxPages \u6216\u52A0\u7CBE\u786E\u7B5B\u9009`;
3869
+ }
3870
+ const dictNameMap = {};
3871
+ const optionsMap = {};
3872
+ const dsOptionsCache = {};
3873
+ for (const item of cache.dict?.raw || []) {
3874
+ if (item.option_id && item.selection_name) dictNameMap[item.option_id] = item.selection_name;
3875
+ }
3876
+ for (const [fieldId, opts] of Object.entries(cache.options || {})) {
3877
+ if (opts && typeof opts === "object") Object.assign(optionsMap, opts);
3878
+ }
3879
+ const dsOptionsFor = async (fieldId) => {
3880
+ if (fieldId && dsOptionsCache[fieldId] === void 0) {
3881
+ try {
3882
+ const o = await host.request({
3883
+ path: "/api/ebuilder/common/ds/options?fieldName=" + fieldId + "&objId=" + formObjId + "&sourceType=FORM&groupId=" + appId,
3884
+ method: "GET"
3885
+ });
3886
+ const map = {};
3887
+ for (const it of o?.data || []) if (it?.optionId || it?.id) map[String(it.optionId || it.id)] = it.name || it.text || it.optionName;
3888
+ dsOptionsCache[fieldId] = map;
3889
+ } catch {
3890
+ dsOptionsCache[fieldId] = {};
3891
+ }
3892
+ }
3893
+ return dsOptionsCache[fieldId] || {};
3894
+ };
3895
+ const mapEnum = async (optionId, fieldKey) => {
3896
+ const s = String(optionId);
3897
+ if (dictNameMap[s]) return dictNameMap[s];
3898
+ if (optionsMap[s]) return optionsMap[s];
3899
+ let cn = (await dsOptionsFor(fieldMap[fieldKey]))[s] || null;
3900
+ if (!cn && String(fieldKey).endsWith("_key")) {
3901
+ cn = (await dsOptionsFor(fieldMap[String(fieldKey).slice(0, -4)]))[s] || null;
3902
+ }
3903
+ return cn;
3904
+ };
3905
+ if (!hasDisplay) {
3906
+ const needFieldIds = /* @__PURE__ */ new Set();
3907
+ for (const r of workRows) {
3908
+ for (const [k, v] of Object.entries(r)) {
3909
+ if (INTERNAL_KEYS.has(k) || DISPLAY_UI_KEYS.has(k)) continue;
3910
+ const s = String(v ?? "");
3911
+ if (!s) continue;
3912
+ if (!ENUM_SHORT_FIELDS.has(k) && !/^\d{16,}$/.test(s)) continue;
3913
+ if (dictNameMap[s] || optionsMap[s]) continue;
3914
+ const fieldId = fieldMap[k] || (k.endsWith("_key") ? fieldMap[k.slice(0, -4)] : null);
3915
+ if (fieldId && dsOptionsCache[fieldId] === void 0) needFieldIds.add(fieldId);
3916
+ }
3917
+ }
3918
+ await Promise.all([...needFieldIds].map((fieldId) => dsOptionsFor(fieldId)));
3919
+ }
3920
+ const out = {
3921
+ env: new URL(baseUrl).hostname,
3922
+ type,
3923
+ total,
3924
+ totalExact,
3925
+ ...totalNote ? { totalNote } : {},
3926
+ ...appliedFilters.length ? { filters: appliedFilters } : {},
3927
+ ...args.recent ? { recentDays: Number(args.recent) } : {},
3928
+ page: current,
3929
+ pageSize,
3930
+ scanned: rows.length,
3931
+ returned: workRows.length,
3932
+ rows: []
3933
+ };
3934
+ const outRows = out.rows;
3935
+ for (const r of workRows) {
3936
+ const row = { form_data_id: r.form_data_id || r.id };
3937
+ for (const [k, v] of Object.entries(r)) {
3938
+ if (hasDisplay) {
3939
+ if (DISPLAY_UI_KEYS.has(k) || k === "form_data_id") continue;
3940
+ if (k.endsWith("Span") || k.endsWith("Action")) continue;
3941
+ const s2 = String(v ?? "");
3942
+ if (!s2) continue;
3943
+ if (/^\d{16,}$/.test(s2)) {
3944
+ const cn = await mapEnum(s2, k);
3945
+ if (cn) {
3946
+ row[k] = cn;
3947
+ continue;
3948
+ }
3949
+ }
3950
+ if (typeof v === "object" || typeof v === "string" && s2.trimStart().startsWith("[")) {
3951
+ let arr;
3952
+ if (typeof v === "object") arr = Array.isArray(v) ? v : [v];
3953
+ else {
3954
+ try {
3955
+ arr = JSON.parse(s2);
3956
+ } catch {
3957
+ arr = null;
3958
+ }
3959
+ }
3960
+ if (Array.isArray(arr)) {
3961
+ const names = arr.map((x) => x?.name || x?.username || x?.text || x?.title || (typeof x === "string" ? x : null)).filter(Boolean);
3962
+ if (names.length) {
3963
+ row[k] = names.join("\u3001");
3964
+ continue;
3965
+ }
3966
+ }
3967
+ row[k] = s2;
3968
+ continue;
3969
+ }
3970
+ row[k] = s2;
3971
+ continue;
3972
+ }
3973
+ if (INTERNAL_KEYS.has(k)) continue;
3974
+ const s = String(v ?? "");
3975
+ if (!s) continue;
3976
+ if (ENUM_SHORT_FIELDS.has(k)) {
3977
+ const cn = await mapEnum(s, k);
3978
+ row[k] = cn || s;
3979
+ continue;
3980
+ }
3981
+ if (/^\d{16,}$/.test(s)) {
3982
+ const cn = await mapEnum(s, k);
3983
+ if (cn) row[k] = cn;
3984
+ continue;
3985
+ }
3986
+ if (typeof v === "object" || typeof v === "string" && s.trimStart().startsWith("[")) {
3987
+ let arr;
3988
+ if (typeof v === "object") arr = Array.isArray(v) ? v : [v];
3989
+ else {
3990
+ try {
3991
+ arr = JSON.parse(s);
3992
+ } catch {
3993
+ arr = null;
3994
+ }
3995
+ }
3996
+ if (Array.isArray(arr)) {
3997
+ const names = arr.map((x) => x?.name || x?.text || x?.title || (typeof x === "string" ? x : null)).filter(Boolean);
3998
+ if (names.length) {
3999
+ row[k] = names.join("\u3001");
4000
+ continue;
4001
+ }
4002
+ }
4003
+ row[k] = JSON.stringify(v);
4004
+ continue;
4005
+ }
4006
+ row[k] = s;
4007
+ }
4008
+ for (const [canonical, aliases] of Object.entries(DISPLAY_ALIASES)) {
4009
+ const cur = row[canonical];
4010
+ if (cur !== void 0 && cur !== "") continue;
4011
+ for (const a of aliases) {
4012
+ const v = row[a];
4013
+ if (v === void 0 || v === "") continue;
4014
+ if (/^\d{16,}$/.test(String(v))) continue;
4015
+ row[canonical] = v;
4016
+ break;
4017
+ }
4018
+ }
4019
+ outRows.push(row);
4020
+ }
4021
+ if (feedbackTodoMode) {
4022
+ const cu = await resolveCurrentUser(host);
4023
+ const me = cu ? cu.name : "";
4024
+ const st = (r) => String(r.union_status_key || r.union_status || "");
4025
+ const tp = (r) => String(r.interview_type_key || r.interview_type || "");
4026
+ const iv = (r) => String(r.interviewer || "");
4027
+ const slim = (r) => ({
4028
+ form_data_id: r.form_data_id,
4029
+ candidate_name: r.candidate_name,
4030
+ position_name: r.position_name,
4031
+ start_time: r.start_time,
4032
+ interview_type_key: tp(r),
4033
+ interview_session: r.interview_session,
4034
+ interviewer: iv(r),
4035
+ union_status_key: st(r),
4036
+ subject: r.subject,
4037
+ mobile: r.mobile,
4038
+ talent_id: r.talent_id,
4039
+ position_manage_id: r.position_manage_id
4040
+ });
4041
+ const pending = outRows.filter((r) => /待反馈/.test(st(r)));
4042
+ const mineAll = me ? pending.filter((r) => iv(r).includes(me)) : [];
4043
+ const myAi = mineAll.filter((r) => /AI\s*面试/i.test(tp(r)));
4044
+ const notes = [];
4045
+ if (!me) notes.push("\u672A\u80FD\u89E3\u6790\u5F53\u524D\u767B\u5F55\u7528\u6237\uFF0C\u65E0\u6CD5\u6309\u9762\u8BD5\u5B98\u8FC7\u6EE4\uFF08\u7ED3\u679C\u4E3A\u7A7A\uFF09");
4046
+ if (out.total !== null && outRows.length < out.total) {
4047
+ notes.push("\u5DF2\u626B\u63CF " + outRows.length + "/" + out.total + " \u6761\uFF08\u9762\u8BD5\u5217\u8868\u9ED8\u8BA4\u987A\u5E8F\uFF09\uFF0C\u672A\u8986\u76D6\u5168\u90E8\uFF1B\u5982\u6000\u7591\u9057\u6F0F\u53EF\u52A0\u5927 maxPages");
4048
+ }
4049
+ out.feedbackTodo = {
4050
+ currentUser: me || null,
4051
+ scanned: outRows.length,
4052
+ myAiPendingFeedback: { count: myAi.length, rows: myAi.map(slim) },
4053
+ myOtherPendingFeedback: {
4054
+ count: mineAll.length - myAi.length,
4055
+ rows: mineAll.filter((r) => !/AI\s*面试/i.test(tp(r))).map(slim)
4056
+ },
4057
+ ...notes.length ? { note: notes.join("\uFF1B") } : {}
4058
+ };
4059
+ }
4060
+ return out;
4061
+ }
4062
+ var listInput = {
4063
+ type: { type: "string", enum: ["demand", "position", "entry", "offer", "interview", "candidate"], description: "\u5217\u8868\u7C7B\u578B" },
4064
+ pageSize: { type: "integer", minimum: 1, maximum: 200, description: "\u6BCF\u9875\u884C\u6570\uFF08\u9ED8\u8BA4 10\uFF1BfeedbackTodo \u9ED8\u8BA4 200\uFF09" },
4065
+ current: { type: "integer", minimum: 1, description: "\u9875\u7801\uFF08\u9ED8\u8BA4 1\uFF09" },
4066
+ positionId: { type: "string", description: "\u6309\u804C\u4F4D id \u5BA2\u6237\u7AEF\u7CBE\u786E\u7B5B\u9009\uFF08\u670D\u52A1\u7AEF\u7B5B\u9009\u90E8\u5206\u5217\u8868\u4E0D\u53EF\u9760\uFF09" },
4067
+ talentId: { type: "string", description: "\u6309\u4EBA\u624D id \u5BA2\u6237\u7AEF\u7CBE\u786E\u7B5B\u9009" },
4068
+ candidateManageId: { type: "string", description: "\u6309\u5E94\u8058\u6279\u6B21 id \u5BA2\u6237\u7AEF\u7CBE\u786E\u7B5B\u9009" },
4069
+ recent: { type: "integer", minimum: 1, description: "\u8FD1 N \u5929\uFF08\u6309 start_time \u672C\u5730\u8FC7\u6EE4\uFF09" },
4070
+ sort: { type: "string", description: "\u6309 dataKey \u5012\u5E8F\u6392\u5E8F\uFF08\u5982 start_time\uFF09" },
4071
+ all: { type: "boolean", description: "\u7FFB\u9875\u53D6\u5168\uFF08\u672B\u9875\u5224\u636E=\u9875\u5185\u884C\u6570 < pageSize\uFF09" },
4072
+ maxPages: { type: "integer", minimum: 1, maximum: 50, description: "all \u6A21\u5F0F\u6700\u5927\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF1BfeedbackTodo \u9ED8\u8BA4 5\uFF09" }
4073
+ };
4074
+ var listOperations = [
4075
+ {
4076
+ name: "recruit-stage.list",
4077
+ resource: "recruit.list",
4078
+ method: "query",
4079
+ action: "list",
4080
+ summary: "\u62DB\u8058 6 \u7C7B\u5217\u8868\u67E5\u8BE2\uFF08\u9700\u6C42/\u804C\u4F4D/\u5165\u804C/\u5F55\u7528/\u9762\u8BD5/\u5E94\u8058\u8005\uFF09\uFF1AdisplayData \u663E\u793A\u5C42\u4F18\u5148\u3001\u5BA2\u6237\u7AEF\u7B5B\u9009/\u65F6\u95F4\u8FC7\u6EE4/\u6392\u5E8F\u3001\u603B\u6570\u4E09\u6001\u4E0D\u9020\u5047",
4081
+ capability: "recruit.list",
4082
+ risk: "read",
4083
+ inputSchema: schemaOf(listInput, { required: ["type"] }),
4084
+ handler: async (args, env) => rsSuccess("recruit-stage.list", await runList(env, args, false))
4085
+ },
4086
+ {
4087
+ name: "recruit-stage.interview.feedbackTodo",
4088
+ resource: "recruit.interview",
4089
+ method: "query",
4090
+ action: "interview.feedbackTodo",
4091
+ summary: "\u9762\u8BD5\u5F85\u529E\uFF1A\u53EA\u8FD4\u56DE\u6211\u4F5C\u4E3A\u9762\u8BD5\u5B98\u7684\u5F85\u53CD\u9988\u6E05\u5355\uFF08\u540E\u7AEF TO_FEEDBACK \u5DF2\u542B\u300C\u5DF2\u5B8C\u6210+\u6709\u5F55\u50CF+\u672A\u586B\u610F\u89C1\u300D\u8BED\u4E49\uFF09\uFF1BmyAiPendingFeedback \u4E3B\u6E05\u5355 + myOtherPendingFeedback \u9644\u5E26",
4092
+ capability: "recruit.interview.feedback",
4093
+ risk: "read",
4094
+ inputSchema: schemaOf({
4095
+ pageSize: listInput.pageSize,
4096
+ maxPages: listInput.maxPages
4097
+ }, {}),
4098
+ handler: async (args, env) => rsSuccess("recruit-stage.interview.feedbackTodo", await runList(env, { ...args, type: "interview" }, true))
4099
+ }
4100
+ ];
4101
+
4102
+ // src/shortcuts/recruit-stage/operations/search.ts
4103
+ var ID_RE = /^\d+$/;
4104
+ function parsePortalName(field) {
4105
+ if (!field) return null;
4106
+ if (Array.isArray(field)) {
4107
+ const names = field.map((x) => x && (x.name || x.username || x.text)).filter(Boolean);
4108
+ return names.length ? names.join("\u3001") : null;
4109
+ }
4110
+ if (typeof field === "object") {
4111
+ const f = field;
4112
+ return f.name || f.username || f.text || f.title || null;
4113
+ }
4114
+ if (typeof field === "string") {
4115
+ const s = field.trim();
4116
+ if (s.startsWith("[")) {
4117
+ try {
4118
+ return parsePortalName(JSON.parse(s));
4119
+ } catch {
4120
+ }
4121
+ }
4122
+ return null;
4123
+ }
4124
+ return null;
4125
+ }
4126
+ async function resolvePureIds(host, ids) {
4127
+ const uniq = [...new Set(ids.filter((x) => x != null && ID_RE.test(String(x).trim())).map((x) => String(x).trim()))];
4128
+ const map = {};
4129
+ if (!uniq.length) return map;
4130
+ for (let i = 0; i < uniq.length; i += 100) {
4131
+ const batch = uniq.slice(i, i + 100);
4132
+ try {
4133
+ const r = await host.request({
4134
+ path: "/api/hrm/common/getEmployeeByIds",
4135
+ body: { userIdList: batch, externalIdList: [] },
4136
+ headers: { devtype: "5" }
4137
+ });
4138
+ for (const u of r?.data?.userList || []) {
4139
+ if (u?.id != null) map[String(u.id)] = u.name || null;
4140
+ }
4141
+ } catch {
4142
+ }
4143
+ }
4144
+ return map;
4145
+ }
4146
+ async function handler(args, env) {
4147
+ const host = env.host;
4148
+ const session = await host.getSession();
4149
+ const baseUrl = session.baseUrl.replace(/\/+$/, "");
4150
+ const cache = await env.recruitContext();
4151
+ const talent = cache.lists?.talent;
4152
+ const listId = talent?.listId;
4153
+ const objId = cache.forms?.talent_pool?.objId;
4154
+ if (!listId || !objId) {
4155
+ throw rsFail("\u5F53\u524D\u73AF\u5883\u672A\u89E3\u6790\u5230\u300C\u4EBA\u624D\u5E93\u300D\u83DC\u5355\u6216\u4EBA\u624D\u5E93\u8868\u5355\uFF0C\u65E0\u6CD5\u641C\u7D22\u4EBA\u624D\u3002\u8BF7\u786E\u8BA4 CLI \u5DF2\u767B\u5F55\u76EE\u6807\u73AF\u5883\u4E14\u8BE5\u73AF\u5883\u5DF2\u90E8\u7F72\u62DB\u8058\u5E94\u7528\uFF08\u53EF\u8FD0\u884C ehr.recruit.context.resolve \u5BF9\u7167\u68C0\u67E5\uFF09\u3002");
4156
+ }
4157
+ const cusMenuId = talent.cusMenuId;
4158
+ const menuName = talent.menuName || "\u4EBA\u624D\u7BA1\u7406";
4159
+ const fields = cache.fields[objId] || {};
4160
+ const nameField = "field_" + (fields.candidate_name || fields.name);
4161
+ const mobileField = "field_" + fields.telephone;
4162
+ const name = args.name ? String(args.name) : "";
4163
+ const mobile = args.mobile ? String(args.mobile) : "";
4164
+ const conds = {};
4165
+ if (name) conds[nameField] = [{ [nameField]: name, [nameField + "_condition_type"]: "like" }];
4166
+ if (mobile) conds[mobileField] = [{ [mobileField]: mobile, [mobileField + "_condition_type"]: "like" }];
4167
+ const baseBody = {
4168
+ current: Number(args.current || 1),
4169
+ pageSize: Number(args.pageSize || 10),
4170
+ tableuid: randHex32(),
4171
+ listId,
4172
+ searchParamData: { searchType: "3", commonSearchFields: conds, type: "and" },
4173
+ terminal: "PC",
4174
+ browser: "Chrome",
4175
+ requiredMatch: 1,
4176
+ mapResult: 1,
4177
+ recheckQuery: 1,
4178
+ isFollow: 0,
4179
+ isShareToMe: 0,
4180
+ filterCondition: {},
4181
+ cusMenuId,
4182
+ urlPageTitle: "\u4EBA\u624D\u5E93",
4183
+ menuName
4184
+ };
4185
+ const extra = { ebBusinessId: listId };
4186
+ const cntBody = { ...baseBody };
4187
+ delete cntBody.current;
4188
+ delete cntBody.pageSize;
4189
+ const [resp, c] = await Promise.all([
4190
+ host.request({
4191
+ path: "/api/ebuilder/form/list/getListDatas?passid=" + objId + "&cid=" + objId,
4192
+ body: baseBody,
4193
+ headers: extra
4194
+ }),
4195
+ host.request({
4196
+ path: "/api/ebuilder/form/list/getListDataCount?passid=" + objId + "&cid=" + objId,
4197
+ body: cntBody,
4198
+ headers: extra
4199
+ }).catch(() => null)
4200
+ ]);
4201
+ const rawRows = resp?.data?.data || [];
4202
+ const dispRows = resp?.data?.displayData;
4203
+ const rows = Array.isArray(dispRows) && dispRows.length ? dispRows : rawRows;
4204
+ let total = rows.length;
4205
+ if (c?.data?.total !== void 0) total = c.data.total;
4206
+ const statusMap = cache.options?.recruit_status || {};
4207
+ const dictNameMap = {};
4208
+ for (const item of cache.dict?.raw || []) {
4209
+ if (item.option_id && item.selection_name) dictNameMap[item.option_id] = item.selection_name;
4210
+ }
4211
+ const out = { env: new URL(baseUrl).hostname, total, rows: [] };
4212
+ const outRows = out.rows;
4213
+ const inlineNames = rows.map((r) => parsePortalName(r.creator));
4214
+ const pureIds = [];
4215
+ rows.forEach((r, i) => {
4216
+ if (inlineNames[i] == null && r.creator != null && ID_RE.test(String(r.creator).trim())) {
4217
+ pureIds.push(String(r.creator).trim());
4218
+ }
4219
+ });
4220
+ const idNameMap = await resolvePureIds(host, pureIds);
4221
+ rows.forEach((r, i) => {
4222
+ let creator = inlineNames[i];
4223
+ if (creator == null) {
4224
+ const raw = r.creator != null ? String(r.creator).trim() : null;
4225
+ creator = (raw != null && ID_RE.test(raw) ? idNameMap[raw] || raw : raw) || null;
4226
+ }
4227
+ outRows.push({
4228
+ talentId: r.form_data_id || r.id || null,
4229
+ name: r.candidate_name || r.name || null,
4230
+ mobile: r.telephone || null,
4231
+ channel: dictNameMap[r.channel] || r.channel || null,
4232
+ status: statusMap[r.recruit_status] || r.recruit_status || null,
4233
+ creator,
4234
+ createTime: r.create_time || null
4235
+ });
4236
+ });
4237
+ return rsSuccess("recruit-stage.candidate.search", out);
4238
+ }
4239
+ var searchOperations = [
4240
+ {
4241
+ name: "recruit-stage.candidate.search",
4242
+ resource: "recruit.candidate",
4243
+ method: "query",
4244
+ action: "candidate.search",
4245
+ summary: "\u6309\u59D3\u540D/\u624B\u673A\u53F7\u6A21\u7CCA\u641C\u7D22\u4EBA\u624D\u5E93\u4EBA\u624D\uFF1B\u65E0\u7B5B\u9009\u6761\u4EF6\u65F6\u5217\u51FA\u5168\u90E8\u4EBA\u624D\u7528\u4E8E\u6D4F\u89C8\u3002\u8FD4\u56DE\u767D\u540D\u5355\u5C55\u793A\u5B57\u6BB5\uFF08talentId/\u59D3\u540D/\u624B\u673A/\u6E20\u9053/\u72B6\u6001/\u521B\u5EFA\u4EBA\uFF09",
4246
+ capability: "recruit.candidate.search",
4247
+ risk: "read",
4248
+ inputSchema: schemaOf({
4249
+ name: { type: "string", description: "\u59D3\u540D\u5173\u952E\u8BCD\uFF08like\uFF09" },
4250
+ mobile: { type: "string", pattern: "^\\d{6,20}$", description: "\u624B\u673A\u53F7\uFF08like\uFF09" },
4251
+ pageSize: { type: "integer", minimum: 1, maximum: 100 },
4252
+ current: { type: "integer", minimum: 1 }
4253
+ }, {}),
4254
+ handler
4255
+ }
4256
+ ];
4257
+
4258
+ // src/shortcuts/recruit-stage/operations/registry.ts
4259
+ var definitions = [
4260
+ ...searchOperations,
4261
+ ...listOperations,
4262
+ ...jdCreateOperations,
4263
+ ...resumeImportOperations,
4264
+ ...interviewArrangeOperations,
4265
+ ...interviewFeedbackOperations
4266
+ ];
4267
+ var registry = /* @__PURE__ */ new Map();
4268
+ for (const definition of definitions) {
4269
+ if (registry.has(definition.name)) {
4270
+ throw new Error(`duplicate recruit-stage operation: ${definition.name}`);
4271
+ }
4272
+ registry.set(definition.name, definition);
4273
+ }
4274
+ function recruitStageOperationDefinitions() {
4275
+ return [...registry.values()];
4276
+ }
4277
+ function getRecruitStageOperation(operation) {
4278
+ const definition = registry.get(operation);
4279
+ if (!definition) {
4280
+ throw new RecruitStageCliError("validation", "operation_unknown", `\u62DB\u8058\u52A9\u624B\u4E0D\u652F\u6301\u64CD\u4F5C\uFF1A${operation}`, {
4281
+ exitCode: 1
4282
+ });
4283
+ }
4284
+ return definition;
4285
+ }
4286
+ async function executeRecruitStageOperation(operation, input, env) {
4287
+ const definition = getRecruitStageOperation(operation);
4288
+ return definition.handler(input, env);
4289
+ }
4290
+
4291
+ // src/shortcuts/recruit-stage/manifest.ts
4292
+ function buildOperations() {
4293
+ return recruitStageOperationDefinitions().map((definition) => ({
4294
+ name: definition.name,
4295
+ resource: definition.resource,
4296
+ summary: definition.summary,
4297
+ ...definition.capability ? { capability: definition.capability } : {},
4298
+ risk: definition.risk,
4299
+ ...definition.requiresConfirmation ? { requiresConfirmation: true } : {},
4300
+ inputSchema: definition.inputSchema
4301
+ }));
4302
+ }
4303
+ var recruitStageManifest = {
4304
+ schemaVersion: 1,
4305
+ name: "recruit-stage",
4306
+ title: "\u6CDB\u5FAEE10\u62DB\u8058\u52A9\u624B\uFF08JD\u751F\u6210/\u7B80\u5386\u5EFA\u6863/AI\u9762\u8BD5\u9080\u7EA6\uFF09",
4307
+ version: "0.1.0",
4308
+ host: "e10-recruit-stage",
4309
+ status: "builtin",
4310
+ sourceSkill: "weaver-e10-recruit-stage",
4311
+ capabilities: ["recruit.jd.create", "recruit.resume.import", "recruit.interview.arrange", "recruit.interview.feedback", "recruit.list", "recruit.candidate.search"],
4312
+ operations: buildOperations(),
4313
+ withheldOperations: []
4314
+ };
4315
+
4316
+ // src/shortcuts/recruit-stage/context.ts
4317
+ import { join, resolve as resolve2 } from "node:path";
4318
+ import { existsSync as existsSync2 } from "node:fs";
4319
+ import { readFile as readFile2 } from "node:fs/promises";
4320
+ import { homedir as homedir2 } from "node:os";
4321
+ var flowMemo = /* @__PURE__ */ new Map();
4322
+ var digitalMenuMemo = /* @__PURE__ */ new Map();
4323
+ function cacheDirFor(baseUrl) {
4324
+ const url = new URL(baseUrl);
4325
+ const host = url.hostname + (url.port ? `_${url.port}` : "");
4326
+ const envRoot = process.env.E10_SKILL_CACHE_DIR;
4327
+ if (envRoot) return join(envRoot, host);
4328
+ const home = process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve2(homedir2(), ".weaver-work-cli");
4329
+ return join(home, "env-cache", "recruit-ops", host);
4330
+ }
4331
+ async function readLegacyCacheExtra(baseUrl) {
4332
+ try {
4333
+ const dir = cacheDirFor(baseUrl);
4334
+ if (!existsSync2(dir)) return { defaultRecruitFlow: null, digitalHumanMenu: null };
4335
+ const files = (await import("node:fs/promises")).readdir(dir);
4336
+ const jsonFiles = (await files).filter((f) => f.endsWith(".json"));
4337
+ if (jsonFiles.length !== 1) return { defaultRecruitFlow: null, digitalHumanMenu: null };
4338
+ const raw = JSON.parse(await readFile2(join(dir, jsonFiles[0]), "utf8"));
4339
+ const flow = raw?.defaultRecruitFlow && raw.defaultRecruitFlow.id ? { id: String(raw.defaultRecruitFlow.id), name: String(raw.defaultRecruitFlow.name || "") } : null;
4340
+ const dh = raw?.lists?.digital_human?.listId ? {
4341
+ menuName: String(raw.lists.digital_human.menuName || "\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E"),
4342
+ listId: String(raw.lists.digital_human.listId),
4343
+ cusMenuId: String(raw.lists.digital_human.cusMenuId || "")
4344
+ } : null;
4345
+ return { defaultRecruitFlow: flow, digitalHumanMenu: dh };
4346
+ } catch {
4347
+ return { defaultRecruitFlow: null, digitalHumanMenu: null };
4348
+ }
4349
+ }
4350
+ async function resolveDefaultRecruitFlow(host, baseUrl, appId) {
4351
+ const memoKey = baseUrl;
4352
+ if (flowMemo.has(memoKey)) return flowMemo.get(memoKey) ?? null;
4353
+ const legacy = await readLegacyCacheExtra(baseUrl);
4354
+ if (legacy.defaultRecruitFlow) {
4355
+ flowMemo.set(memoKey, legacy.defaultRecruitFlow);
4356
+ return legacy.defaultRecruitFlow;
4357
+ }
4358
+ let result = null;
4359
+ try {
4360
+ const r = await host.request({
4361
+ path: "/api/recruit/position/recruitFlow/list",
4362
+ method: "POST",
4363
+ body: { appId, current: 1, pageSize: 50 },
4364
+ operation: "context.defaultRecruitFlow"
4365
+ });
4366
+ const rows = r?.data?.list || r?.data?.rows || r?.data || [];
4367
+ const list = Array.isArray(rows) ? rows : [];
4368
+ const enabled = list.find((x) => x && (x.isDefault === true || Number(x.isDefault) === 1 || x.default_flow === "1")) || list.find((x) => x && x.id);
4369
+ if (enabled?.id) {
4370
+ result = { id: String(enabled.id), name: String(enabled.name || enabled.flow_name || "") };
4371
+ }
4372
+ } catch {
4373
+ }
4374
+ flowMemo.set(memoKey, result);
4375
+ return result;
4376
+ }
4377
+ async function resolveDigitalHumanMenu(host, baseUrl, cache) {
4378
+ const memoKey = baseUrl;
4379
+ if (digitalMenuMemo.has(memoKey)) return digitalMenuMemo.get(memoKey) ?? null;
4380
+ const legacy = await readLegacyCacheExtra(baseUrl);
4381
+ if (legacy.digitalHumanMenu) {
4382
+ digitalMenuMemo.set(memoKey, legacy.digitalHumanMenu);
4383
+ return legacy.digitalHumanMenu;
4384
+ }
4385
+ const allSearch = cache.lists?.all_search || [];
4386
+ const candidates = allSearch.filter((m) => m.listId);
4387
+ let hit = (candidates.find((m) => (m.menuName || "").includes("\u9762\u8BD5\u6570\u5B57\u4EBA")) || candidates.find((m) => (m.menuName || "").includes("\u6570\u5B57\u4EBA\u914D\u7F6E"))) ?? null;
4388
+ digitalMenuMemo.set(memoKey, hit);
4389
+ return hit;
4390
+ }
4391
+ var loaders = /* @__PURE__ */ new Map();
4392
+ function createRecruitStageContext() {
4393
+ return async (host) => {
4394
+ const loaderKey = "shared";
4395
+ if (!loaders.has(loaderKey)) loaders.set(loaderKey, createRecruitContextLoader());
4396
+ const cache = await loaders.get(loaderKey).load(host);
4397
+ const baseUrl = (await host.getSession()).baseUrl.replace(/\/+$/, "");
4398
+ const [defaultRecruitFlow, digitalHumanMenu] = await Promise.all([
4399
+ resolveDefaultRecruitFlow(host, baseUrl, cache.appId),
4400
+ resolveDigitalHumanMenu(host, baseUrl, cache)
4401
+ ]);
4402
+ return { ...cache, defaultRecruitFlow, digitalHumanMenu };
4403
+ };
4404
+ }
4405
+
4406
+ // src/shortcuts/recruit-stage/index.ts
4407
+ function asOptions(value) {
4408
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4409
+ }
4410
+ async function readStdin() {
4411
+ const chunks = [];
4412
+ for await (const chunk of process.stdin) {
4413
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
4414
+ }
4415
+ return Buffer.concat(chunks).toString("utf8");
4416
+ }
4417
+ function stripBom(value) {
4418
+ return value.replace(/^\uFEFF+/, "");
4419
+ }
4420
+ async function inputFrom(options, extra = {}) {
4421
+ const opts = asOptions(options);
4422
+ let input = {};
4423
+ if (opts.input && opts.inputJson) {
4424
+ throw rsValidationError("input_conflict", "\u4E0D\u80FD\u540C\u65F6\u4F20 --input \u548C --input-json");
4425
+ }
4426
+ if (opts.inputJson) {
4427
+ input = parseJsonInput(String(opts.inputJson), "--input-json");
4428
+ }
4429
+ if (opts.input) {
4430
+ let raw;
4431
+ try {
4432
+ raw = stripBom(opts.input === "-" ? await readStdin() : await readFile3(String(opts.input), "utf8"));
4433
+ } catch {
4434
+ throw rsValidationError("input_unreadable", `\u65E0\u6CD5\u8BFB\u53D6 --input\uFF1A${String(opts.input)}`);
4435
+ }
4436
+ input = parseJsonInput(raw, "--input");
4437
+ }
4438
+ return {
4439
+ ...input,
4440
+ ...Object.fromEntries(Object.entries(extra).filter(([, value]) => value !== void 0 && value !== ""))
4441
+ };
4442
+ }
4443
+ function parseJsonInput(raw, label) {
4444
+ try {
4445
+ const parsed = JSON.parse(stripBom(raw));
4446
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4447
+ throw new Error("input must be an object");
4448
+ }
4449
+ return parsed;
4450
+ } catch {
4451
+ throw rsValidationError("input_invalid", `${label} \u4E0D\u662F\u5408\u6CD5 JSON \u5BF9\u8C61`);
4452
+ }
4453
+ }
4454
+ function successEnvelope(operation, result) {
4455
+ return {
4456
+ schemaVersion: 1,
4457
+ ok: true,
4458
+ operation,
4459
+ data: result.data === void 0 ? null : result.data,
4460
+ ...result.meta ? { meta: result.meta } : {},
4461
+ warnings: []
4462
+ };
4463
+ }
4464
+ function failureEnvelope(error) {
4465
+ const structured = error;
4466
+ return {
4467
+ schemaVersion: 1,
4468
+ ok: false,
4469
+ ...structured?.partialData === void 0 ? {} : { data: structured.partialData },
4470
+ error: toErrorEnvelope(error)
4471
+ };
4472
+ }
4473
+ async function executeAndPrint(ctx, operation, input) {
4474
+ try {
4475
+ const host = createRecruitStageHost(ctx);
4476
+ const contextLoader = createRecruitStageContext();
4477
+ const result = await executeRecruitStageOperation(operation, input, {
4478
+ ctx,
4479
+ host,
4480
+ continuation: createRecruitStageContinuationSigner(ctx),
4481
+ recruitContext: () => contextLoader(host)
4482
+ });
4483
+ ctx.output.write(successEnvelope(operation, result), { json: true });
4484
+ } catch (error) {
4485
+ console.error(JSON.stringify(failureEnvelope(error), null, 2));
4486
+ process.exitCode = isCliError(error) ? error.exitCode : 1 /* GeneralError */;
4487
+ }
4488
+ }
4489
+ var recruitStageShortcut = {
4490
+ name: "recruit-stage",
4491
+ description: "\u6CDB\u5FAEE10\u62DB\u8058\u52A9\u624B\uFF08JD\u751F\u6210/\u7B80\u5386\u5EFA\u6863/AI\u9762\u8BD5\u9080\u7EA6\uFF09CLI",
4492
+ category: "shortcut",
4493
+ register({ program, action }) {
4494
+ const rs = program.command("recruit-stage").description("\u6CDB\u5FAEE10\u62DB\u8058\u52A9\u624B\uFF08\u62DB\u8058\u542F\u4E8B\u751F\u6210/\u7B80\u5386\u89E3\u6790\u5EFA\u6863/AI\u9762\u8BD5\u9080\u7EA6\u4E0E\u53CD\u9988\uFF09");
4495
+ rs.command("schema").description("Print recruit-stage operation manifest for agents").action(action((ctx) => {
4496
+ ctx.output.write(recruitStageManifest, { json: true });
4497
+ }));
4498
+ rs.command("run").description("Run a recruit-stage operation with JSON input").argument("<operation>", "operation name, e.g. recruit-stage.jd.create.prepare").option("--input <path>", "JSON input file, or - for stdin").option("--input-json <json>", "inline JSON input object").action(action(async (ctx, operation, options) => {
4499
+ await executeAndPrint(ctx, String(operation), await inputFrom(options));
4500
+ }));
4501
+ }
4502
+ };
4503
+ export {
4504
+ inputFrom,
4505
+ recruitStageShortcut,
4506
+ successEnvelope
4507
+ };