weaver-work-cli 0.1.39 → 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 (197) hide show
  1. package/README.md +4 -2
  2. package/dist/chunks/{archive-ENZYBBME.js → archive-J4I6MCKF.js} +2 -2
  3. package/dist/chunks/{archive-oa-YWLEC5V4.js → archive-oa-BRZKKTO4.js} +2 -2
  4. package/dist/chunks/{asset-OOV6DWPA.js → asset-L7D4FAOZ.js} +2 -2
  5. package/dist/chunks/{asset-idle-RE632MY5.js → asset-idle-DADLLV75.js} +2 -2
  6. package/dist/chunks/{attachment-content-RWN2FCLL.js → attachment-content-K4C7VLWQ.js} +3 -3
  7. package/dist/chunks/{blog-PEPOJAL7.js → blog-BVGQ7NZL.js} +2 -2
  8. package/dist/chunks/{caizhilian-WYIQDVPJ.js → caizhilian-SAADNFD6.js} +2 -2
  9. package/dist/chunks/{calendar-CIARBAOX.js → calendar-Q7NQY5Z2.js} +61 -23
  10. package/dist/chunks/{chunk-F2EQXUEE.js → chunk-3KTFS4IA.js} +397 -159
  11. package/dist/chunks/{chunk-G2JSIUWF.js → chunk-4PD5NWAX.js} +89 -0
  12. package/dist/chunks/{chunk-HJYVHXUQ.js → chunk-4RAE6JXP.js} +1 -1
  13. package/dist/chunks/{chunk-CQHGNCK2.js → chunk-72CKAUAG.js} +41 -8
  14. package/dist/chunks/{chunk-K3RUEEOL.js → chunk-APVFJUQH.js} +2 -2
  15. package/dist/chunks/{chunk-BJGXMFGD.js → chunk-BBKDXQEC.js} +2 -2
  16. package/dist/chunks/chunk-BIPYLLVS.js +501 -0
  17. package/dist/chunks/{chunk-GVPJZXJ2.js → chunk-EKWAQSLU.js} +92 -50
  18. package/dist/chunks/{chunk-XXJOSZTQ.js → chunk-FOAA27U5.js} +381 -57
  19. package/dist/chunks/{chunk-I2HB737U.js → chunk-G4UR6NXN.js} +2 -2
  20. package/dist/chunks/{chunk-YS5DPN4T.js → chunk-GEWYMQME.js} +1 -1
  21. package/dist/chunks/{chunk-P2DAISYW.js → chunk-GHXCDPTM.js} +1 -1
  22. package/dist/chunks/{chunk-L4N7B7ZD.js → chunk-IZ7S7QUS.js} +4 -4
  23. package/dist/chunks/{chunk-NLK2RZKG.js → chunk-JPRO6ER2.js} +122 -2214
  24. package/dist/chunks/{chunk-36DIFZAH.js → chunk-LADPYOWC.js} +2 -2
  25. package/dist/chunks/chunk-LDZQPUIA.js +1238 -0
  26. package/dist/chunks/chunk-NKZRQXST.js +2546 -0
  27. package/dist/chunks/{chunk-EINRJHQ3.js → chunk-QKNKR4RH.js} +3 -3
  28. package/dist/chunks/{chunk-T7JDJJ7H.js → chunk-R4CHUSKC.js} +126 -19
  29. package/dist/chunks/{chunk-EZGFHJFW.js → chunk-SA6VBIHY.js} +88 -15
  30. package/dist/chunks/{chunk-ZXRNHZTE.js → chunk-TTPJJJC4.js} +2 -2
  31. package/dist/chunks/{chunk-KTCB5R5H.js → chunk-W7BEHOSR.js} +2 -2
  32. package/dist/chunks/{chunk-MUQUHKPG.js → chunk-YRSZQUD2.js} +2 -2
  33. package/dist/chunks/{conflict-DRMDOL56.js → conflict-F2OMZRTO.js} +2 -2
  34. package/dist/chunks/{contract-73MERZWN.js → contract-5BETHRSG.js} +2 -2
  35. package/dist/chunks/{cowork-HNGBPD7A.js → cowork-3MN6EQQO.js} +27 -10
  36. package/dist/chunks/create-flow-5FQNNMVO.js +55 -0
  37. package/dist/chunks/{ebuilder-form-OY6DVNRK.js → ebuilder-form-SBHOYBUD.js} +3 -3
  38. package/dist/chunks/{ehr-RAQNEQO7.js → ehr-KMOJULKG.js} +432 -1234
  39. package/dist/chunks/env-LB7NBL2O.js +19 -0
  40. package/dist/chunks/{esb-6XV7C7YV.js → esb-W2PJCFCX.js} +4 -4
  41. package/dist/chunks/factory-6ZFQXQ46.js +30 -0
  42. package/dist/chunks/{fna-XTSRB7G7.js → fna-ZQPCUV4R.js} +2 -2
  43. package/dist/chunks/group-chat-2HHAJN46.js +20 -0
  44. package/dist/chunks/{host-YMBXWFWX.js → host-4CBHGOHT.js} +3 -3
  45. package/dist/chunks/{host-A7J6GDUP.js → host-ATKVNZAT.js} +3 -3
  46. package/dist/chunks/{host-3F5UM5GN.js → host-BQH22NTQ.js} +3 -3
  47. package/dist/chunks/{host-YAQAPYNP.js → host-X2CSLFM6.js} +3 -3
  48. package/dist/chunks/{host-VGPVP7YF.js → host-YTODKMSL.js} +3 -3
  49. package/dist/chunks/{hrm-TLHKC545.js → hrm-FFM4UJFS.js} +4 -4
  50. package/dist/chunks/{im-YOUCFMUK.js → im-VEHKDQPB.js} +8 -8
  51. package/dist/chunks/{invoice-WNBS2B4C.js → invoice-LVIKSIIF.js} +3 -3
  52. package/dist/chunks/{jiuchuanhui-XABWS5RN.js → jiuchuanhui-X6DINO5P.js} +2 -2
  53. package/dist/chunks/{lazy-dispatch-NLZ5UQQI.js → lazy-dispatch-KSWTN5L6.js} +4 -4
  54. package/dist/chunks/{mail-LRYMBFNL.js → mail-VDKRGUD3.js} +9 -4
  55. package/dist/chunks/{meeting-ESXL6MFI.js → meeting-CLC6HP3Z.js} +12 -10
  56. package/dist/chunks/{okr-EOV65SSF.js → okr-CHG4OO3U.js} +2 -2
  57. package/dist/chunks/{plan-YFPVKW6Y.js → plan-XWR75667.js} +2 -2
  58. package/dist/chunks/{project-MDVZUYQW.js → project-5BXOOKMG.js} +40 -23
  59. package/dist/chunks/recruit-stage-DGMFNNDE.js +4507 -0
  60. package/dist/chunks/{registry-VT5Y2HLA.js → registry-7FFOXIAW.js} +4 -4
  61. package/dist/chunks/{registry-QCLZEDAP.js → registry-BKBZPBAJ.js} +10 -8
  62. package/dist/chunks/{registry-YX4CSEMD.js → registry-RHMW6ANW.js} +2 -2
  63. package/dist/chunks/{registry-NYPYMZXG.js → registry-Z43434VG.js} +1 -1
  64. package/dist/chunks/{room-recommend-L3MWH4JW.js → room-recommend-62H7ZQQ6.js} +4 -4
  65. package/dist/chunks/{schedule-UASAVDVD.js → schedule-FNXKH3DQ.js} +4 -4
  66. package/dist/chunks/{workflow-KDGIUCNW.js → workflow-ADHKCAJK.js} +4 -4
  67. package/dist/index.js +33 -28
  68. package/dist/internal/e10/auth/commands.js +110 -56
  69. package/dist/internal/e10/auth/messages.js +6 -0
  70. package/dist/internal/e10/auth/session.js +28 -1
  71. package/dist/internal/e10/auth/xiaoe.js +315 -48
  72. package/dist/internal/e10/auth-recovery.js +36 -5
  73. package/dist/internal/e10/client.js +13 -0
  74. package/dist/internal/e10/context.js +61 -9
  75. package/dist/internal/e10/index.js +1 -1
  76. package/dist/internal/e10/network-error.js +93 -0
  77. package/dist/internal/e10/request-runtime.js +15 -0
  78. package/dist/shortcuts/calendar/operations/read.js +31 -22
  79. package/dist/shortcuts/calendar/operations/write.js +45 -1
  80. package/dist/shortcuts/cowork/operations/discuss-write.js +5 -1
  81. package/dist/shortcuts/cowork/operations/item-write.js +5 -3
  82. package/dist/shortcuts/cowork/operations/member-write.js +3 -1
  83. package/dist/shortcuts/cowork/operations/permission-write.js +5 -1
  84. package/dist/shortcuts/cowork/operations/shared.js +7 -0
  85. package/dist/shortcuts/ehr/operations/attend-write.js +43 -1
  86. package/dist/shortcuts/ehr/operations/hr-contact.js +7 -4
  87. package/dist/shortcuts/ehr/operations/hr-report.js +13 -7
  88. package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +134 -59
  89. package/dist/shortcuts/ehr/operations/recruit-write.js +262 -17
  90. package/dist/shortcuts/ehr/operations/recruit.js +10 -4
  91. package/dist/shortcuts/ehr/operations/shared.js +47 -0
  92. package/dist/shortcuts/hrm/operations/org.js +118 -0
  93. package/dist/shortcuts/index.js +5 -0
  94. package/dist/shortcuts/mail/operations/mail-send.js +8 -2
  95. package/dist/shortcuts/meeting/operations/eb/catalog.js +9 -2
  96. package/dist/shortcuts/meeting/operations/eb/create-flow.js +461 -127
  97. package/dist/shortcuts/meeting/operations/eb/crm-search.js +87 -5
  98. package/dist/shortcuts/meeting/operations/eb/factory.js +33 -0
  99. package/dist/shortcuts/meeting/operations/eb/flow-memory.js +49 -0
  100. package/dist/shortcuts/meeting/operations/eb/group-chat.js +605 -0
  101. package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
  102. package/dist/shortcuts/meeting/operations/eb/person-cache.js +96 -0
  103. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +44 -6
  104. package/dist/shortcuts/meeting/operations/eb/room-summary.js +2 -0
  105. package/dist/shortcuts/meeting/operations/eb/usage-cache.js +99 -0
  106. package/dist/shortcuts/meeting/operations/from-group-chat.js +35 -12
  107. package/dist/shortcuts/project/operations/read.js +6 -25
  108. package/dist/shortcuts/project/operations/shared.js +22 -0
  109. package/dist/shortcuts/project/operations/write.js +28 -2
  110. package/dist/shortcuts/recruit-stage/context.js +119 -0
  111. package/dist/shortcuts/recruit-stage/continuation.js +73 -0
  112. package/dist/shortcuts/recruit-stage/errors.js +65 -0
  113. package/dist/shortcuts/recruit-stage/host.js +109 -0
  114. package/dist/shortcuts/recruit-stage/index.js +115 -0
  115. package/dist/shortcuts/recruit-stage/manifest.js +29 -0
  116. package/dist/shortcuts/recruit-stage/operations/interview-arrange.js +722 -0
  117. package/dist/shortcuts/recruit-stage/operations/interview-feedback.js +269 -0
  118. package/dist/shortcuts/recruit-stage/operations/jd-create.js +630 -0
  119. package/dist/shortcuts/recruit-stage/operations/list.js +431 -0
  120. package/dist/shortcuts/recruit-stage/operations/registry.js +38 -0
  121. package/dist/shortcuts/recruit-stage/operations/resume-import.js +1198 -0
  122. package/dist/shortcuts/recruit-stage/operations/search.js +158 -0
  123. package/dist/shortcuts/recruit-stage/operations/types.js +25 -0
  124. package/dist/shortcuts/recruit-stage/school-tiers.json +318 -0
  125. package/dist/shortcuts/recruit-stage/shared.js +608 -0
  126. package/dist/shortcuts/workflow/operations/form.js +21 -2
  127. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +52 -1
  128. package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +4 -2
  129. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +19 -11
  130. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +17 -24
  131. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +14 -9
  132. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +78 -10
  133. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +3 -1
  134. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +17 -11
  135. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +16 -40
  136. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +3 -2
  137. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +52 -15
  138. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.pipeline.js +16 -1
  139. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +151 -63
  140. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.subGroup.js +56 -0
  141. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +53 -16
  142. package/docs/e10-auth.md +22 -1
  143. package/docs/operation-manual.md +8 -0
  144. package/package.json +1 -1
  145. package/scripts/install-skill-to-xiaoe.mjs +105 -0
  146. package/scripts/mcp-meeting.mjs +234 -0
  147. package/scripts/package-skill.mjs +4 -2
  148. package/skill-template/business-info.json +24 -0
  149. package/skill-template/domains/recruit-stage.md +29 -0
  150. package/skills/weaver-e10-calendar/SKILL.md +2 -1
  151. package/skills/weaver-e10-calendar/references/calendar-write.md +54 -0
  152. package/skills/weaver-e10-cowork/SKILL.md +1 -1
  153. package/skills/weaver-e10-cowork/references/cowork-write.md +3 -1
  154. package/skills/weaver-e10-jucailin/SKILL.md +1 -1
  155. package/skills/weaver-e10-jucailin/references/attend-write.md +8 -0
  156. package/skills/weaver-e10-jucailin/references/hr-contact.md +8 -0
  157. package/skills/weaver-e10-jucailin/references/hr-report.md +8 -0
  158. package/skills/weaver-e10-jucailin/references/recruit-create.md +2 -2
  159. package/skills/weaver-e10-jucailin/references/recruit-read.md +1 -1
  160. package/skills/weaver-e10-jucailin/references/recruit-write.md +4 -3
  161. package/skills/weaver-e10-mail/SKILL.md +1 -1
  162. package/skills/weaver-e10-mail/references/mail-send.md +2 -0
  163. package/skills/weaver-e10-meeting/SKILL.md +119 -26
  164. package/skills/weaver-e10-meeting/references/meeting-minute-file.md +25 -11
  165. package/skills/weaver-e10-meeting.zip +0 -0
  166. package/skills/weaver-e10-recruit-stage/SKILL.md +226 -0
  167. package/skills/weaver-e10-recruit-stage/agents/openai.yaml +119 -0
  168. package/skills/weaver-e10-recruit-stage/lib/recruit-xiaoe-file.mjs +188 -0
  169. package/skills/weaver-e10-recruit-stage/references/recruit-arrange-interview.md +322 -0
  170. package/skills/weaver-e10-recruit-stage/references/recruit-capability-check.md +127 -0
  171. package/skills/weaver-e10-recruit-stage/references/recruit-check-install.md +65 -0
  172. package/skills/weaver-e10-recruit-stage/references/recruit-detail-process.md +63 -0
  173. package/skills/weaver-e10-recruit-stage/references/recruit-edit-interview-feedback.md +142 -0
  174. package/skills/weaver-e10-recruit-stage/references/recruit-jd-create.md +73 -0
  175. package/skills/weaver-e10-recruit-stage/references/recruit-jd-template.md +56 -0
  176. package/skills/weaver-e10-recruit-stage/references/recruit-list-candidate.md +101 -0
  177. package/skills/weaver-e10-recruit-stage/references/recruit-list-interview.md +148 -0
  178. package/skills/weaver-e10-recruit-stage/references/recruit-match-candidates.md +77 -0
  179. package/skills/weaver-e10-recruit-stage/references/recruit-output-links.md +59 -0
  180. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-creator.md +69 -0
  181. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-metadata.md +241 -0
  182. package/skills/weaver-e10-recruit-stage/references/recruit-resume-import.md +438 -0
  183. package/skills/weaver-e10-recruit-stage/references/recruit-resume-parse.md +128 -0
  184. package/skills/weaver-e10-recruit-stage/references/recruit-search-candidate.md +146 -0
  185. package/skills/weaver-e10-recruit-stage/references/recruit-urge-feedback.md +69 -0
  186. package/skills/weaver-e10-shared/SKILL.md +2 -2
  187. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -2
  188. package/skills/weaver-e10-shijingran/SKILL.md +3 -2
  189. package/skills/weaver-e10-shijingran/references/operations.md +3 -1
  190. package/skills/weaver-e10-shijingran/references/source-manifest.json +41 -40
  191. package/skills/weaver-e10-shijingran/references/write-operations.md +2 -0
  192. package/skills/weaver-e10-workflow/SKILL.md +7 -1
  193. package/skills/weaver-e10-workflow/aigroup/default.json +23 -9
  194. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +37 -21
  195. package/skills/weaver-e10-workflow/tools/todo-summary.cjs +135 -43
  196. package/skills/weaver-e10-yepiaotong/SKILL.md +9 -10
  197. package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +6 -3
@@ -1,17 +1,54 @@
1
1
  import { createRequire as __esbuildCreateRequire } from 'node:module';
2
2
  const require = __esbuildCreateRequire(import.meta.url);
3
+ import {
4
+ EHR_PAGES,
5
+ EhrCliError,
6
+ READ,
7
+ SCHEMA_DATE,
8
+ SCHEMA_ID,
9
+ SCHEMA_ID_LIST,
10
+ SCHEMA_MONTH,
11
+ SCHEMA_STRING_LIST,
12
+ WRITE,
13
+ addDays,
14
+ asObject,
15
+ assertDateRange,
16
+ assertEnum,
17
+ assertMonth,
18
+ assertNoForbiddenKeys,
19
+ buildListBody,
20
+ compact,
21
+ createRecruitContextLoader,
22
+ ehrAuthError,
23
+ ehrConfirmationError,
24
+ ehrNetworkError,
25
+ ehrPageUrl,
26
+ ehrPartialError,
27
+ ehrSuccess,
28
+ ehrValidationError,
29
+ fetchListRows,
30
+ normalizePaged,
31
+ pickDefined,
32
+ recruitOperations,
33
+ requireDateRange,
34
+ resolveSecondAuthToken,
35
+ schemaOf,
36
+ toIdList,
37
+ toNumber,
38
+ toPageQuery,
39
+ toPositiveInt
40
+ } from "./chunk-LDZQPUIA.js";
3
41
  import {
4
42
  E10ApiError,
5
43
  createE10RequestRuntime,
6
44
  isE10SessionExpiredResponse,
7
45
  requireE10Session
8
- } from "./chunk-EZGFHJFW.js";
46
+ } from "./chunk-SA6VBIHY.js";
9
47
  import {
10
48
  E10_SESSION_EXPIRED_MESSAGE,
11
49
  getActiveProfile
12
- } from "./chunk-XXJOSZTQ.js";
50
+ } from "./chunk-FOAA27U5.js";
13
51
  import {
14
- CliError,
15
52
  isCliError,
16
53
  toErrorEnvelope
17
54
  } from "./chunk-LUSPKZKY.js";
@@ -26,62 +63,6 @@ import { existsSync } from "node:fs";
26
63
  import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
27
64
  import { homedir } from "node:os";
28
65
  import { resolve } from "node:path";
29
-
30
- // src/shortcuts/ehr/errors.ts
31
- var EhrCliError = class extends CliError {
32
- type;
33
- subtype;
34
- code;
35
- retryable;
36
- stage;
37
- details;
38
- partialData;
39
- constructor(type, subtype, message, options = {}) {
40
- super(message, options.exitCode ?? 5 /* InternalError */);
41
- this.name = "EhrCliError";
42
- this.type = type;
43
- this.subtype = subtype;
44
- this.code = options.code;
45
- this.retryable = options.retryable ?? false;
46
- this.stage = options.stage;
47
- this.details = options.details;
48
- this.partialData = options.partialData;
49
- }
50
- };
51
- function ehrValidationError(subtype, message, details) {
52
- return new EhrCliError("validation", subtype, message, {
53
- details,
54
- exitCode: 2 /* UsageError */
55
- });
56
- }
57
- function ehrAuthError(subtype, message, details) {
58
- return new EhrCliError("authentication", subtype, message, {
59
- details,
60
- exitCode: 3 /* AuthError */
61
- });
62
- }
63
- function ehrNetworkError(subtype, message, options = {}) {
64
- return new EhrCliError("network", subtype, message, {
65
- ...options,
66
- retryable: options.retryable ?? true,
67
- exitCode: 4 /* NetworkError */
68
- });
69
- }
70
- function ehrConfirmationError(action, details) {
71
- return new EhrCliError("confirmation", "required", "\u8BE5\u64CD\u4F5C\u9700\u8981\u660E\u786E\u786E\u8BA4", {
72
- details: { action, ...details },
73
- exitCode: 10 /* ConfirmationRequired */
74
- });
75
- }
76
- function ehrPartialError(message, partialData, details) {
77
- return new EhrCliError("partial", "write_uncertain", message, {
78
- details,
79
- partialData,
80
- exitCode: 11 /* Partial */
81
- });
82
- }
83
-
84
- // src/shortcuts/ehr/continuation.ts
85
66
  function stateRoot(ctx) {
86
67
  const configured = ctx?.config?.path();
87
68
  if (configured) return resolve(configured, "..");
@@ -325,184 +306,6 @@ function createEhrHost(ctx, dependencies = {}) {
325
306
  return { request, getSession, buildCardSign };
326
307
  }
327
308
 
328
- // src/shortcuts/ehr/operations/types.ts
329
- var READ = "read";
330
- var WRITE = "write";
331
- function ehrSuccess(action, data, meta) {
332
- const payload = { ok: true, action, data };
333
- if (meta && Object.keys(meta).length > 0) payload.meta = meta;
334
- return payload;
335
- }
336
- function schemaOf(properties, options = {}) {
337
- return {
338
- type: "object",
339
- additionalProperties: false,
340
- properties,
341
- ...options.required?.length ? { required: options.required } : {},
342
- ...options.description ? { description: options.description } : {}
343
- };
344
- }
345
- var SCHEMA_DATE = { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$" };
346
- var SCHEMA_MONTH = { type: "string", pattern: "^\\d{4}-\\d{2}$" };
347
- var SCHEMA_ID = { oneOf: [{ type: "integer" }, { type: "string", pattern: "^\\d+$" }] };
348
- var SCHEMA_ID_LIST = { type: "array", items: SCHEMA_ID };
349
- var SCHEMA_STRING_LIST = { type: "array", items: { type: "string" } };
350
-
351
- // src/shortcuts/ehr/operations/shared.ts
352
- var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
353
- function asObject(value) {
354
- if (!value || typeof value !== "object" || Array.isArray(value)) {
355
- throw ehrValidationError("input_not_object", "\u8F93\u5165\u5FC5\u987B\u662F JSON \u5BF9\u8C61");
356
- }
357
- return value;
358
- }
359
- function assertNoForbiddenKeys(input) {
360
- for (const key of Object.keys(input)) {
361
- if (FORBIDDEN_KEYS.has(key)) {
362
- throw ehrValidationError("forbidden_key", `\u8F93\u5165\u5305\u542B\u975E\u6CD5\u5B57\u6BB5\uFF1A${key}`);
363
- }
364
- }
365
- }
366
- var DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
367
- var MONTH_PATTERN = /^\d{4}-\d{2}$/;
368
- function isDate(value) {
369
- return typeof value === "string" && DATE_PATTERN.test(value);
370
- }
371
- function isMonth(value) {
372
- return typeof value === "string" && MONTH_PATTERN.test(value);
373
- }
374
- function isRealDate(value) {
375
- const [year, month, day] = value.split("-").map(Number);
376
- if (month < 1 || month > 12) return false;
377
- const daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate();
378
- return day >= 1 && day <= daysInMonth;
379
- }
380
- function assertDate(value, field) {
381
- if (!isDate(value)) throw ehrValidationError("date_invalid", `${field} \u5FC5\u987B\u662F YYYY-MM-DD \u683C\u5F0F`);
382
- const text = value;
383
- if (!isRealDate(text)) {
384
- throw ehrValidationError("date_invalid", `${field} \u4E0D\u662F\u5408\u6CD5\u65E5\u671F\uFF08\u6708\u4EFD\u9700 01-12\uFF0C\u65E5\u671F\u9700\u5728\u8BE5\u6708\u6709\u6548\u8303\u56F4\u5185\uFF09\uFF1A${text}`);
385
- }
386
- return text;
387
- }
388
- function assertMonth(value, field) {
389
- if (!isMonth(value)) throw ehrValidationError("month_invalid", `${field} \u5FC5\u987B\u662F YYYY-MM \u683C\u5F0F`);
390
- const text = value;
391
- const month = Number(text.slice(5));
392
- if (month < 1 || month > 12) {
393
- throw ehrValidationError("month_invalid", `${field} \u7684\u6708\u4EFD\u5FC5\u987B\u5728 01-12\uFF1A${text}`);
394
- }
395
- return text;
396
- }
397
- function toNumber(value, field) {
398
- if (value === void 0 || value === null || value === "") return void 0;
399
- const numeric = Number(value);
400
- if (!Number.isFinite(numeric)) {
401
- throw ehrValidationError("number_invalid", `${field} \u5FC5\u987B\u662F\u6570\u5B57`);
402
- }
403
- return numeric;
404
- }
405
- function toPositiveInt(value, field) {
406
- if (value === void 0 || value === null || value === "") return void 0;
407
- const numeric = Number(value);
408
- if (!Number.isFinite(numeric)) {
409
- throw ehrValidationError("number_invalid", `${field} \u5FC5\u987B\u662F\u6570\u5B57`);
410
- }
411
- const truncated = Math.trunc(numeric);
412
- if (truncated !== numeric || truncated <= 0) {
413
- throw ehrValidationError("number_invalid", `${field} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
414
- }
415
- return truncated;
416
- }
417
- function toIdList(value, field) {
418
- if (value === void 0 || value === null) return void 0;
419
- if (!Array.isArray(value)) throw ehrValidationError("id_list_invalid", `${field} \u5FC5\u987B\u662F ID \u6570\u7EC4`);
420
- return value.map((item, index) => {
421
- if (item === void 0 || item === null || item === "") {
422
- throw ehrValidationError("id_list_invalid", `${field}[${index}] \u4E0D\u80FD\u4E3A\u7A7A`);
423
- }
424
- const numeric = Number(item);
425
- if (!Number.isFinite(numeric)) {
426
- throw ehrValidationError("id_list_invalid", `${field}[${index}] \u5FC5\u987B\u662F\u6570\u5B57 ID`);
427
- }
428
- return numeric;
429
- });
430
- }
431
- function pickDefined(value, keys) {
432
- const result = {};
433
- for (const key of keys) {
434
- if (value[key] !== void 0) result[key] = value[key];
435
- }
436
- return result;
437
- }
438
- function compact(body) {
439
- for (const key of Object.keys(body)) {
440
- if (body[key] === void 0) delete body[key];
441
- }
442
- return body;
443
- }
444
- function assertDateRange(args, startField, endField) {
445
- const start = args[startField];
446
- const end = args[endField];
447
- if ((start === void 0 || start === null) !== (end === void 0 || end === null)) {
448
- throw ehrValidationError("date_range_incomplete", `${startField} \u4E0E ${endField} \u5FC5\u987B\u6210\u5BF9\u63D0\u4F9B`);
449
- }
450
- if (start === void 0 || start === null) return {};
451
- const startDate = assertDate(start, startField);
452
- const endDate = assertDate(end, endField);
453
- if (startDate > endDate) {
454
- throw ehrValidationError("date_range_invalid", `${startField} \u4E0D\u5F97\u665A\u4E8E ${endField}`);
455
- }
456
- return { start: startDate, end: endDate };
457
- }
458
- function requireDateRange(args, startField = "beginDate", endField = "endDate") {
459
- const start = args[startField];
460
- const end = args[endField];
461
- if (start === void 0 || start === null || start === "" || end === void 0 || end === null || end === "") {
462
- throw ehrValidationError("date_range_required", `${startField} \u4E0E ${endField} \u5FC5\u586B\uFF0C\u683C\u5F0F YYYY-MM-DD`);
463
- }
464
- const range = assertDateRange(args, startField, endField);
465
- return { beginDate: range.start, endDate: range.end };
466
- }
467
- function unwrapData(response) {
468
- if (!response || typeof response !== "object") return response;
469
- return response.data;
470
- }
471
- function normalizePaged(response) {
472
- const data = unwrapData(response);
473
- const page = {};
474
- const candidates = [data?.page, response?.page, data, response];
475
- for (const source of candidates) {
476
- if (!source || typeof source !== "object") continue;
477
- for (const key of ["pageNo", "current", "pageSize", "total", "totalCount", "totalPage", "tableId"]) {
478
- if (source[key] !== void 0 && page[key] === void 0) page[key] = source[key];
479
- }
480
- }
481
- return { data, page };
482
- }
483
- function toPageQuery(args) {
484
- const page = {};
485
- const current = toPositiveInt(args?.current, "current");
486
- const pageSize = toNumber(args?.pageSize, "pageSize");
487
- const pageNo = toNumber(args?.pageNo, "pageNo");
488
- if (current !== void 0) page.current = current;
489
- if (pageSize !== void 0) page.pageSize = pageSize;
490
- if (pageNo !== void 0) page.pageNo = pageNo;
491
- return page;
492
- }
493
- var SECOND_AUTH_TOKEN_DEFAULT = "DONT_NEED";
494
- function resolveSecondAuthToken(value) {
495
- if (typeof value === "string" && value.trim()) return value.trim();
496
- return SECOND_AUTH_TOKEN_DEFAULT;
497
- }
498
- function assertEnum(value, allowed, field) {
499
- const text = typeof value === "string" ? value : String(value);
500
- if (!allowed.includes(text)) {
501
- throw ehrValidationError("enum_invalid", `${field} \u53D6\u503C\u5FC5\u987B\u662F ${allowed.join(" / ")} \u4E4B\u4E00`);
502
- }
503
- return text;
504
- }
505
-
506
309
  // src/shortcuts/ehr/operations/attend-read.ts
507
310
  var ATTEND_STATUS = [
508
311
  "SIGN_IN_LATE",
@@ -1229,9 +1032,39 @@ var ehrAttendAppealSaveApply = {
1229
1032
  }
1230
1033
  throw error;
1231
1034
  }
1035
+ const session = await env.host.getSession();
1036
+ const appealListUrl = ehrPageUrl(session.baseUrl, EHR_PAGES.attendAbnormal);
1037
+ let appeal = null;
1038
+ let locateWarning;
1039
+ try {
1040
+ const listResp = await env.host.request({
1041
+ method: "POST",
1042
+ path: "/api/attend/web/appeal/getAppealList",
1043
+ body: {
1044
+ beginDate: addDays(String(args.attendDay), -1),
1045
+ endDate: addDays(String(args.attendDay), 1),
1046
+ current: 1,
1047
+ pageSize: 50
1048
+ }
1049
+ });
1050
+ const payload2 = listResp?.data;
1051
+ const rows = Array.isArray(payload2?.data) ? payload2.data : Array.isArray(payload2?.rows) ? payload2.rows : Array.isArray(payload2) ? payload2 : [];
1052
+ const day = String(args.attendDay);
1053
+ const desc = String(args.appealDesc ?? "");
1054
+ const pick = rows.find((r) => String(r?.attendDay ?? r?.attend_date ?? r?.attendDate ?? "").startsWith(day)) || (desc ? rows.find((r) => String(r?.appealDesc ?? r?.appeal_desc ?? "").includes(desc)) : null) || null;
1055
+ if (pick) appeal = pick;
1056
+ else locateWarning = "\u672A\u5728\u7533\u8BC9\u5217\u8868\u4E2D\u5B9A\u4F4D\u5230\u672C\u6B21\u8BB0\u5F55\uFF08\u53EF\u80FD\u521A\u63D0\u4EA4\u5C1A\u5728\u540C\u6B65\uFF09\uFF0C\u8BF7\u7A0D\u540E\u7528 ehr.attend.appeal.list \u590D\u6838";
1057
+ } catch (e) {
1058
+ locateWarning = "\u7533\u8BC9\u5217\u8868\u56DE\u67E5\u5931\u8D25\uFF1A" + (e?.message || "\u672A\u77E5\u539F\u56E0") + "\uFF1B\u8BF7\u7A0D\u540E\u7528 ehr.attend.appeal.list \u590D\u6838";
1059
+ }
1232
1060
  return ehrSuccess("ehr.attend.appeal.save.apply", {
1233
1061
  status: "COMPLETE",
1234
1062
  result: response?.data,
1063
+ /** 定位到的新建申诉记录(含申诉 ID 等,处理申诉时用其 id) */
1064
+ appeal,
1065
+ /** 考勤异常/申诉页面地址(已取证路由,禁止改拼) */
1066
+ appealListUrl,
1067
+ ...locateWarning ? { locateWarning } : {},
1235
1068
  verifyHint: "\u53EF\u901A\u8FC7 ehr.attend.appeal.list \u56DE\u67E5\u7533\u8BC9\u662F\u5426\u5DF2\u751F\u6210"
1236
1069
  });
1237
1070
  }
@@ -1560,7 +1393,8 @@ var contactSearch = {
1560
1393
  body: compact(body)
1561
1394
  });
1562
1395
  const { data, page } = normalizePaged(response);
1563
- return ehrSuccess("ehr.hr.contact.search", data, { page });
1396
+ const hrSession = await host.getSession();
1397
+ return ehrSuccess("ehr.hr.contact.search", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrEmployeeManager), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
1564
1398
  }
1565
1399
  };
1566
1400
  var contactCount = {
@@ -1684,7 +1518,8 @@ var birthdayList = {
1684
1518
  body
1685
1519
  });
1686
1520
  const { data, page } = normalizePaged(response);
1687
- return ehrSuccess("ehr.hr.birthday.list", data, { page });
1521
+ const hrSession = await host.getSession();
1522
+ return ehrSuccess("ehr.hr.birthday.list", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrBirthdayList), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
1688
1523
  }
1689
1524
  };
1690
1525
  var empchangeConditions = {
@@ -1758,7 +1593,8 @@ var empchangeList = {
1758
1593
  body: compact(body)
1759
1594
  });
1760
1595
  const { data, page } = normalizePaged(response);
1761
- return ehrSuccess("ehr.hr.empchange.list", data, { page });
1596
+ const hrSession = await host.getSession();
1597
+ return ehrSuccess("ehr.hr.empchange.list", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrPersonalStateChange), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
1762
1598
  }
1763
1599
  };
1764
1600
  var hrContactOperations = [
@@ -2104,7 +1940,8 @@ var reportDeptTable = {
2104
1940
  body: buildPersonReport(args, "departmentName")
2105
1941
  });
2106
1942
  const { data, page } = normalizePaged(response);
2107
- return ehrSuccess("ehr.hr.report.dept.table", data, { page });
1943
+ const hrSession = await host.getSession();
1944
+ return ehrSuccess("ehr.hr.report.dept.table", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrReportDeptPerson), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
2108
1945
  }
2109
1946
  };
2110
1947
  var reportPosiTable = {
@@ -2123,7 +1960,8 @@ var reportPosiTable = {
2123
1960
  body: buildPersonReport(args, "positionName")
2124
1961
  });
2125
1962
  const { data, page } = normalizePaged(response);
2126
- return ehrSuccess("ehr.hr.report.posi.table", data, { page });
1963
+ const hrSession = await host.getSession();
1964
+ return ehrSuccess("ehr.hr.report.posi.table", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrReportPosiPerson), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
2127
1965
  }
2128
1966
  };
2129
1967
  var reportJobTable = {
@@ -2142,7 +1980,8 @@ var reportJobTable = {
2142
1980
  body: buildPersonReport(args, "jobName")
2143
1981
  });
2144
1982
  const { data, page } = normalizePaged(response);
2145
- return ehrSuccess("ehr.hr.report.job.table", data, { page });
1983
+ const hrSession = await host.getSession();
1984
+ return ehrSuccess("ehr.hr.report.job.table", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrReportJobPerson), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
2146
1985
  }
2147
1986
  };
2148
1987
  var reportJobTypeTable = {
@@ -2249,7 +2088,8 @@ var estEmpTable = {
2249
2088
  }
2250
2089
  });
2251
2090
  const { data, page } = normalizePaged(response);
2252
- return ehrSuccess("ehr.hr.est.emp.table", data, { page });
2091
+ const hrSession = await host.getSession();
2092
+ return ehrSuccess("ehr.hr.est.emp.table", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrTpReportTp), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
2253
2093
  }
2254
2094
  };
2255
2095
  var estDcEmpTable = {
@@ -2295,7 +2135,8 @@ var estDcEmpTable = {
2295
2135
  }
2296
2136
  });
2297
2137
  const { data, page } = normalizePaged(response);
2298
- return ehrSuccess("ehr.hr.est.dc.emp.table", data, { page });
2138
+ const hrSession = await host.getSession();
2139
+ return ehrSuccess("ehr.hr.est.dc.emp.table", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrTpReportHw), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
2299
2140
  }
2300
2141
  };
2301
2142
  var turnEmpTable = {
@@ -2342,7 +2183,8 @@ var turnEmpTable = {
2342
2183
  body
2343
2184
  });
2344
2185
  const { data, page } = normalizePaged(response);
2345
- return ehrSuccess("ehr.hr.turn.emp.table", data, { page });
2186
+ const hrSession = await host.getSession();
2187
+ return ehrSuccess("ehr.hr.turn.emp.table", data, { page, viewUrl: ehrPageUrl(hrSession.baseUrl, EHR_PAGES.hrTpReportTurn), profileUrlTemplate: ehrPageUrl(hrSession.baseUrl, "/sp/hrm/profileInfo/{\u4EBA\u5458id}") });
2346
2188
  }
2347
2189
  };
2348
2190
  var propsSa = {
@@ -2419,925 +2261,6 @@ var hrReportOperations = [
2419
2261
  propsTable
2420
2262
  ];
2421
2263
 
2422
- // src/shortcuts/ehr/operations/recruit.ts
2423
- import { randomBytes as randomBytes2, randomUUID } from "node:crypto";
2424
- import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2425
- import { homedir as homedir2 } from "node:os";
2426
- import { join, resolve as resolve2 } from "node:path";
2427
- var RECRUIT_APP_TAG = "weaver-recruit-service";
2428
- var RECRUIT_TAGS = {
2429
- talent_pool: "uf_rcrt_talent_pool",
2430
- interview_plan: "uf_rcrt_interview_plan",
2431
- interview_detail: "uf_rcrt_interview_detail",
2432
- demand_manage: "uf_rcrt_demand_manage",
2433
- position_manage: "uf_rcrt_position_manage",
2434
- entry_manage: "uf_rcrt_entry_manage",
2435
- offer: "uf_rcrt_offer",
2436
- offer_sending: "uf_rcrt_offer_sending",
2437
- candidate_manage: "uf_rcrt_candidate_manage",
2438
- entry_transact: "uf_rcrt_entry_transact",
2439
- entry_cancel: "uf_rcrt_entry_cancel",
2440
- entry_change: "uf_rcrt_entry_change",
2441
- dictionary: "uf_rcrt_dictionary",
2442
- biz_config: "uf_rcrt_biz_config_mapper"
2443
- };
2444
- var LIST_KEYWORDS = {
2445
- talent: ["\u4EBA\u624D\u5E93", "\u4EBA\u624D\u7BA1\u7406"],
2446
- demand: ["\u62DB\u8058\u9700\u6C42", "\u9700\u6C42"],
2447
- position: ["\u62DB\u8058\u804C\u4F4D", "\u804C\u4F4D\u5217\u8868", "\u804C\u4F4D\u7BA1\u7406"],
2448
- entry: ["\u5165\u804C\u7BA1\u7406"],
2449
- offer: ["\u5F55\u7528\u7BA1\u7406", "offer\u7BA1\u7406", "offer"],
2450
- interview: ["\u9762\u8BD5\u7BA1\u7406"],
2451
- biz_config: ["\u4E1A\u52A1\u914D\u7F6E\u6620\u5C04\u8868"]
2452
- };
2453
- var LIST_TYPES = {
2454
- demand: { listKey: "demand", formType: "demand_manage", title: "\u62DB\u8058\u9700\u6C42" },
2455
- position: { listKey: "position", formType: "position_manage", title: "\u62DB\u8058\u804C\u4F4D" },
2456
- entry: { listKey: "entry", formType: "entry_manage", title: "\u5165\u804C\u7BA1\u7406" },
2457
- offer: { listKey: "offer", formType: "offer", title: "\u5F55\u7528\u7BA1\u7406" }
2458
- };
2459
- var INTERNAL_KEYS = /* @__PURE__ */ new Set([
2460
- "id",
2461
- "form_data_id",
2462
- "form_table_id",
2463
- "rowKey",
2464
- "ebrecord_rowkey",
2465
- "isFollow",
2466
- "is_flow",
2467
- "data_status",
2468
- "data_status_value",
2469
- "update_time",
2470
- "formDataId",
2471
- "isFollowSearch",
2472
- "isShareToMe"
2473
- ]);
2474
- var DISPLAY_UI_KEYS = /* @__PURE__ */ new Set([
2475
- ...INTERNAL_KEYS,
2476
- "operate",
2477
- "isToDo",
2478
- "todoPropSpan",
2479
- "flow_status"
2480
- ]);
2481
- function randHex32() {
2482
- return randomUUID().replace(/-/g, "");
2483
- }
2484
- function randHex6() {
2485
- return randomBytes2(3).toString("hex");
2486
- }
2487
- function cacheDirFor(baseUrl) {
2488
- const url = new URL(baseUrl);
2489
- const host = url.hostname + (url.port ? `_${url.port}` : "");
2490
- const envRoot = process.env.E10_SKILL_CACHE_DIR;
2491
- if (envRoot) return join(envRoot, host);
2492
- const home = process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve2(homedir2(), ".weaver-work-cli");
2493
- return join(home, "env-cache", "recruit-ops", host);
2494
- }
2495
- function namesOf(field) {
2496
- if (!field) return null;
2497
- let arr;
2498
- if (Array.isArray(field)) arr = field;
2499
- else if (typeof field === "object") arr = [field];
2500
- else if (typeof field === "string") {
2501
- const text = field.trim();
2502
- if (!text.startsWith("[") && !text.startsWith("{")) return text || null;
2503
- try {
2504
- arr = JSON.parse(text);
2505
- } catch {
2506
- return text || null;
2507
- }
2508
- if (!Array.isArray(arr)) arr = [arr];
2509
- } else return null;
2510
- const names = arr.map((item) => item && typeof item === "object" ? item.name || item.username || item.text || item.title : null).filter(Boolean);
2511
- return names.length ? names.join("\u3001") : null;
2512
- }
2513
- function isPlainId(value) {
2514
- return value != null && /^\d+$/.test(String(value).trim());
2515
- }
2516
- var RecruitContextError = class extends Error {
2517
- subtype;
2518
- constructor(subtype, message) {
2519
- super(message);
2520
- this.subtype = subtype;
2521
- }
2522
- };
2523
- async function fetchFormFields(host, appId, objId) {
2524
- const form = new FormData();
2525
- form.set("groupId", appId);
2526
- form.set("sourceId", objId);
2527
- form.set("sourceType", "FORM");
2528
- form.set("detailFieldsGroup", "true");
2529
- form.set("enableMerge", "true");
2530
- form.set("customParam", '{"terminal":""}');
2531
- form.set("params", "[]");
2532
- const response = await host.request({
2533
- method: "POST",
2534
- path: "/api/ebuilder/common/ds/fields",
2535
- multipart: form
2536
- });
2537
- const fmap = {};
2538
- const omap = {};
2539
- for (const group of response?.data || []) {
2540
- for (const field of group?.fields || []) {
2541
- const config = field?.config || {};
2542
- const dataKey = config.dataKey || "";
2543
- const fieldId = String(field?.id || "");
2544
- if (dataKey && fieldId) fmap[dataKey] = fieldId;
2545
- if (fieldId && Array.isArray(config.options) && config.options.length) {
2546
- const map = {};
2547
- for (const option of config.options) {
2548
- if (!option || typeof option !== "object") continue;
2549
- const name = option.name;
2550
- let selectionId = option.selectionId;
2551
- if (!selectionId && typeof option.extra === "string") {
2552
- try {
2553
- const extra = JSON.parse(option.extra);
2554
- selectionId = extra.selectionId || extra.id;
2555
- } catch {
2556
- }
2557
- }
2558
- if (!selectionId) selectionId = option.value || option.valueKey || option.id;
2559
- if (selectionId && name) map[String(selectionId)] = name;
2560
- }
2561
- if (Object.keys(map).length) omap[fieldId] = map;
2562
- }
2563
- }
2564
- }
2565
- return { fmap, omap };
2566
- }
2567
- async function resolveRecruitEnv(host, appId) {
2568
- const forms = {};
2569
- let tenantKey = "";
2570
- for (const [logic, tag] of Object.entries(RECRUIT_TAGS)) {
2571
- const response = await host.request({
2572
- path: "/api/ebuilder/form/obj/getObjsByTag",
2573
- query: { appId, tag }
2574
- });
2575
- const entry = Array.isArray(response?.data) && response.data.length ? response.data[0] : null;
2576
- forms[logic] = {
2577
- objId: String(entry?.id || ""),
2578
- formId: String(entry?.formId || ""),
2579
- objName: entry?.objName || ""
2580
- };
2581
- if (!tenantKey && entry?.tenantKey) tenantKey = String(entry.tenantKey);
2582
- }
2583
- const menuResponse = await host.request({
2584
- path: "/api/cusapp/app/allMenus",
2585
- query: { isFront: 1, appId, terminalType: "PC" }
2586
- });
2587
- const flat = [];
2588
- const walk = (nodes) => {
2589
- for (const node of nodes || []) {
2590
- if (node && typeof node === "object") {
2591
- flat.push(node);
2592
- walk(node.children || node.childs);
2593
- }
2594
- }
2595
- };
2596
- const menuData = menuResponse?.data;
2597
- walk(typeof menuData === "object" && menuData ? menuData.cusAppMenus || menuData : menuData);
2598
- const searchMenus = [];
2599
- const pages = [];
2600
- for (const node of flat) {
2601
- if (node.pageType === "SEARCH") {
2602
- searchMenus.push({ menuName: node.menuName, listId: String(node.pageId || ""), cusMenuId: String(node.id || "") });
2603
- } else if (node.pageType === "PAGE") {
2604
- pages.push({ menuName: node.menuName, pageId: String(node.pageId || "") });
2605
- }
2606
- }
2607
- const fields = {};
2608
- const fieldOptions = {};
2609
- const resolvedForms = Object.entries(forms).filter(([, v]) => v.objId);
2610
- await Promise.all(resolvedForms.map(async ([logic, entry]) => {
2611
- const { fmap, omap } = await fetchFormFields(host, appId, entry.objId);
2612
- if (Object.keys(fmap).length) fields[entry.objId] = fmap;
2613
- if (Object.keys(omap).length) fieldOptions[entry.objId] = omap;
2614
- void logic;
2615
- }));
2616
- const dictByName = {};
2617
- const dictRaw = [];
2618
- const dictMenu = searchMenus.find((m) => (m.menuName || "").trim() === "\u6570\u636E\u5B57\u5178") || null;
2619
- const dictObjId = forms.dictionary?.objId || "";
2620
- if (dictObjId && dictMenu) {
2621
- const size = 500;
2622
- let current = 1;
2623
- for (; ; ) {
2624
- const response = await host.request({
2625
- path: `/api/ebuilder/form/list/getListDatas?passid=${dictObjId}&cid=${dictObjId}`,
2626
- body: {
2627
- current,
2628
- pageSize: size,
2629
- tableuid: randHex32(),
2630
- listId: dictMenu.listId,
2631
- searchParamData: { searchType: "3", type: "and" },
2632
- terminal: "PC",
2633
- browser: "Chrome",
2634
- requiredMatch: 1,
2635
- mapResult: 1,
2636
- recheckQuery: 1,
2637
- isFollow: 0,
2638
- isShareToMe: 0,
2639
- filterCondition: {},
2640
- cusMenuId: dictMenu.cusMenuId,
2641
- urlPageTitle: "\u6570\u636E\u5B57\u5178",
2642
- menuName: "\u6570\u636E\u5B57\u5178"
2643
- },
2644
- headers: { ebBusinessId: dictMenu.listId }
2645
- });
2646
- const rows = response?.data?.data || [];
2647
- for (const row of rows) {
2648
- const name = row.selection_name;
2649
- const optionId = row.form_data_id;
2650
- if (name && optionId) {
2651
- (dictByName[String(name)] ||= []).push(String(optionId));
2652
- dictRaw.push({ selection_name: String(name), option_id: String(optionId), dict_type: row.dict_type, browser_key: row.browser_key });
2653
- }
2654
- }
2655
- if (rows.length < size) break;
2656
- current += 1;
2657
- }
2658
- }
2659
- let ebdDetailPageId = "";
2660
- let selfUse = "0";
2661
- const bizConfig = forms.biz_config || { objId: "", formId: "", objName: "" };
2662
- const bizConfigMenu = searchMenus.find((m) => (m.menuName || "").trim() === "\u4E1A\u52A1\u914D\u7F6E\u6620\u5C04\u8868") || null;
2663
- if (bizConfig.objId && bizConfigMenu) {
2664
- try {
2665
- const { fmap } = await fetchFormFields(host, appId, bizConfig.objId);
2666
- const keyField = fmap.config_key || "";
2667
- const valueField = fmap.config || "";
2668
- if (keyField && valueField) {
2669
- let current = 1;
2670
- for (; ; ) {
2671
- const response = await host.request({
2672
- path: `/api/ebuilder/form/list/getListDatas?passid=${bizConfig.objId}&cid=${bizConfig.objId}`,
2673
- body: {
2674
- current,
2675
- pageSize: 100,
2676
- tableuid: randHex32(),
2677
- listId: bizConfigMenu.listId,
2678
- searchParamData: { searchType: "3", type: "and" },
2679
- terminal: "PC",
2680
- browser: "Chrome",
2681
- requiredMatch: 1,
2682
- mapResult: 1,
2683
- recheckQuery: 1,
2684
- isFollow: 0,
2685
- isShareToMe: 0,
2686
- filterCondition: {},
2687
- cusMenuId: bizConfigMenu.cusMenuId,
2688
- urlPageTitle: "\u4E1A\u52A1\u914D\u7F6E\u6620\u5C04\u8868",
2689
- menuName: "\u4E1A\u52A1\u914D\u7F6E\u6620\u5C04\u8868"
2690
- },
2691
- headers: { ebBusinessId: bizConfigMenu.listId }
2692
- });
2693
- const rows = response?.data?.data || [];
2694
- if (!rows.length) break;
2695
- for (const row of rows) {
2696
- if (String(row.config_key || "") === "28" && row.config) {
2697
- const match = String(row.config).match(/\/sp\/ebdpage\/view\/(\d+)/);
2698
- if (match) ebdDetailPageId = match[1];
2699
- }
2700
- if (String(row.config_key || "") === "-9999" && String(row.config) === "1") selfUse = "1";
2701
- }
2702
- if (rows.length < 100) break;
2703
- current += 1;
2704
- }
2705
- }
2706
- } catch {
2707
- }
2708
- }
2709
- if (!ebdDetailPageId) {
2710
- try {
2711
- const response = await host.request({
2712
- path: "/api/bs/ebuilder/page/list",
2713
- query: { appid: appId }
2714
- });
2715
- for (const page of Array.isArray(response?.data) ? response.data : []) {
2716
- if ((page?.name || "") === "\u4EBA\u624D\u5361\u7247" && page?.type === "PAGE" && page?.id) {
2717
- ebdDetailPageId = String(page.id);
2718
- break;
2719
- }
2720
- }
2721
- } catch {
2722
- }
2723
- }
2724
- const options = {};
2725
- for (const [dataKey, logic] of [["recruit_status", "talent_pool"], ["channel", "talent_pool"]]) {
2726
- const entry = forms[logic] || { objId: "", formId: "", objName: "" };
2727
- const fieldId = (fields[entry.objId] || {})[dataKey] || "";
2728
- if (fieldId && entry.objId) {
2729
- const response = await host.request({
2730
- path: "/api/ebuilder/common/ds/options",
2731
- query: { fieldName: fieldId, objId: entry.objId, sourceType: "FORM", groupId: appId }
2732
- });
2733
- if (Array.isArray(response?.data)) {
2734
- options[dataKey] = {};
2735
- for (const option of response.data) {
2736
- if (option?.id) options[dataKey][String(option.id)] = option.name;
2737
- }
2738
- }
2739
- }
2740
- }
2741
- const lists = {
2742
- talent: null,
2743
- demand: null,
2744
- position: null,
2745
- entry: null,
2746
- offer: null,
2747
- interview: null,
2748
- dict: dictMenu,
2749
- biz_config: bizConfigMenu,
2750
- all_search: searchMenus,
2751
- pages
2752
- };
2753
- for (const [biz, keywords] of Object.entries(LIST_KEYWORDS)) {
2754
- for (const menu of searchMenus) {
2755
- if (keywords.some((keyword) => (menu.menuName || "").includes(keyword))) {
2756
- lists[biz] = menu;
2757
- break;
2758
- }
2759
- }
2760
- }
2761
- return {
2762
- appId,
2763
- tenantKey,
2764
- forms,
2765
- lists,
2766
- fields,
2767
- field_options: fieldOptions,
2768
- options,
2769
- dict: { by_name: dictByName, raw: dictRaw },
2770
- ebdDetailPageId,
2771
- selfUse
2772
- };
2773
- }
2774
- function createRecruitContextLoader() {
2775
- const memo = /* @__PURE__ */ new Map();
2776
- async function load(host) {
2777
- const session = await host.getSession();
2778
- const baseUrl = session.baseUrl.replace(/\/+$/, "");
2779
- const memoKey = baseUrl;
2780
- let cached = memo.get(memoKey);
2781
- if (cached) return cached;
2782
- let appId = "";
2783
- let diskCache = null;
2784
- try {
2785
- const dir = cacheDirFor(baseUrl);
2786
- if (existsSync2(dir)) {
2787
- const files = readdirSync(dir).filter((file) => file.endsWith(".json"));
2788
- if (files.length === 1) {
2789
- appId = files[0].replace(/\.json$/, "");
2790
- diskCache = JSON.parse(readFileSync(join(dir, files[0]), "utf8"));
2791
- }
2792
- }
2793
- } catch {
2794
- }
2795
- if (diskCache) {
2796
- memo.set(memoKey, diskCache);
2797
- return diskCache;
2798
- }
2799
- const gate = await host.request({
2800
- path: "/api/bs/ebuilder/app/tags/getAppIds",
2801
- query: { tag: RECRUIT_APP_TAG }
2802
- });
2803
- const appIds = gate?.data;
2804
- if (!Array.isArray(appIds) || !appIds.length) {
2805
- throw new RecruitContextError(
2806
- "recruit_service_not_installed",
2807
- "\u5F53\u524D\u79DF\u6237\u672A\u5B89\u88C5\u62DB\u8058\u670D\u52A1\uFF08weaver-recruit-service\uFF09\uFF0C\u65E0\u6CD5\u4F7F\u7528\u62DB\u8058\u80FD\u529B\u3002\u8BF7\u5148\u5728 E10 \u5E94\u7528\u5E02\u573A\u5B89\u88C5\u300C\u62DB\u8058\u300D\u5E94\u7528\u3002"
2808
- );
2809
- }
2810
- appId = String(appIds[0]);
2811
- const resolved = await resolveRecruitEnv(host, appId);
2812
- try {
2813
- const dir = cacheDirFor(baseUrl);
2814
- mkdirSync(dir, { recursive: true });
2815
- writeFileSync(
2816
- join(dir, `${appId}.json`),
2817
- JSON.stringify({
2818
- __meta__: {
2819
- capturedAt: (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19),
2820
- note: "recruit-ops \u73AF\u5883\u53C2\u6570\u7F13\u5B58\uFF08weaver-work-cli \u8FD0\u884C\u65F6\u89E3\u6790\u4EA7\u7269\uFF0C\u4E0D\u542B\u8BA4\u8BC1\u5B57\u6BB5\uFF09"
2821
- },
2822
- envKey: `${dir.split(/[\\/]/).pop()}|${appId}`,
2823
- ...resolved
2824
- }, null, 2),
2825
- "utf8"
2826
- );
2827
- } catch {
2828
- }
2829
- memo.set(memoKey, resolved);
2830
- return resolved;
2831
- }
2832
- return { load };
2833
- }
2834
- function buildListBody(params) {
2835
- return {
2836
- current: params.current ?? 1,
2837
- pageSize: params.pageSize ?? 10,
2838
- tableuid: randHex32(),
2839
- listId: params.listId,
2840
- searchParamData: {
2841
- searchType: "3",
2842
- type: "and",
2843
- ...params.conditions && Object.keys(params.conditions).length ? { commonSearchFields: params.conditions } : {}
2844
- },
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: params.cusMenuId,
2854
- urlPageTitle: params.title,
2855
- menuName: params.menuName
2856
- };
2857
- }
2858
- async function fetchListRows(host, objId, listId, body) {
2859
- const countBody = { ...body };
2860
- delete countBody.current;
2861
- delete countBody.pageSize;
2862
- const [listResponse, countResponse] = await Promise.all([
2863
- host.request({
2864
- path: `/api/ebuilder/form/list/getListDatas?passid=${objId}&cid=${objId}`,
2865
- body,
2866
- headers: { ebBusinessId: listId }
2867
- }),
2868
- host.request({
2869
- path: `/api/ebuilder/form/list/getListDataCount?passid=${objId}&cid=${objId}`,
2870
- body: countBody,
2871
- headers: { ebBusinessId: listId }
2872
- }).catch(() => null)
2873
- ]);
2874
- const data = normalizePaged(listResponse).data;
2875
- const rawRows = data?.data || [];
2876
- const displayRows = data?.displayData;
2877
- const hasDisplay = Array.isArray(displayRows) && displayRows.length > 0;
2878
- const rows = hasDisplay ? displayRows : rawRows;
2879
- const countData = countResponse ? normalizePaged(countResponse).data : null;
2880
- const pageSize = typeof body.pageSize === "number" ? body.pageSize : typeof body.pageSize === "string" ? Number(body.pageSize) || rows.length : rows.length;
2881
- const countUsable = countData !== null && typeof countData.total === "number";
2882
- let total;
2883
- let totalExact;
2884
- let totalNote;
2885
- if (countUsable) {
2886
- total = countData.total;
2887
- totalExact = true;
2888
- } else if (rows.length < pageSize) {
2889
- total = rows.length;
2890
- totalExact = true;
2891
- totalNote = "\u8BA1\u6570\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u672C\u9875\u672A\u6EE1\uFF08\u5DF2\u5230\u672B\u9875\uFF09\uFF0C\u603B\u6570\u6309\u5DF2\u53D6\u56DE\u884C\u6570\u5F97\u51FA";
2892
- } else {
2893
- total = null;
2894
- totalExact = false;
2895
- totalNote = "\u8BA1\u6570\u63A5\u53E3\u4E0D\u53EF\u7528\u4E14\u672C\u9875\u5DF2\u6EE1\uFF0C\u603B\u6570\u4E0D\u53EF\u77E5\uFF08\u53EF\u8C03\u5927 pageSize \u53D6\u56DE\u5168\u90E8\u884C\u6570\uFF09";
2896
- }
2897
- return { rows, displayRows: hasDisplay ? displayRows : null, total, totalExact, totalNote };
2898
- }
2899
- var contextResolve = {
2900
- name: "ehr.recruit.context.resolve",
2901
- resource: "recruit",
2902
- method: "context.resolve",
2903
- action: "ehr.recruit.context.resolve",
2904
- summary: "\u89E3\u6790\u62DB\u8058\u6A21\u5757\u8FD0\u884C\u65F6\u53C2\u6570\uFF08appId/\u8868\u5355 objId/\u5217\u8868 listId+cusMenuId/\u5B57\u6BB5\u6620\u5C04\uFF09\uFF0C\u4E3B\u8981\u4F9B\u8C03\u8BD5\u4E0E\u515C\u5E95\uFF1B\u4E1A\u52A1 operation \u4F1A\u9690\u5F0F\u81EA\u52A8\u89E3\u6790",
2905
- capability: "recruit",
2906
- risk: READ,
2907
- inputSchema: schemaOf({}),
2908
- handler: async (_args, env) => {
2909
- if (!env.recruitContext) {
2910
- throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
2911
- }
2912
- const cache = await env.recruitContext.load(env.host);
2913
- return ehrSuccess("ehr.recruit.context.resolve", {
2914
- appId: cache.appId,
2915
- tenantKey: cache.tenantKey || null,
2916
- forms: cache.forms,
2917
- lists: cache.lists,
2918
- ebdDetailPageId: cache.ebdDetailPageId || null,
2919
- selfUse: cache.selfUse
2920
- });
2921
- }
2922
- };
2923
- var candidateSearch = {
2924
- name: "ehr.recruit.candidate.search",
2925
- resource: "recruit",
2926
- method: "candidate.search",
2927
- action: "ehr.recruit.candidate.search",
2928
- 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",
2929
- capability: "recruit",
2930
- risk: READ,
2931
- inputSchema: schemaOf({
2932
- name: { type: "string", description: "\u59D3\u540D\u6A21\u7CCA\u5339\u914D" },
2933
- mobile: { type: "string", description: "\u624B\u673A\u53F7\u6A21\u7CCA\u5339\u914D" },
2934
- current: SCHEMA_ID,
2935
- pageSize: SCHEMA_ID
2936
- }),
2937
- handler: async (args, env) => {
2938
- if (!env.recruitContext) {
2939
- throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
2940
- }
2941
- const cache = await env.recruitContext.load(env.host);
2942
- const talentList = cache.lists.talent;
2943
- const talentObjId = cache.forms.talent_pool?.objId;
2944
- if (!talentList?.listId || !talentObjId) {
2945
- throw new RecruitContextError("recruit_context_missing", "\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\u5148\u8FD0\u884C ehr.recruit.context.resolve \u68C0\u67E5\u73AF\u5883\u3002");
2946
- }
2947
- const fields = cache.fields[talentObjId] || {};
2948
- const nameField = "field_" + (fields.candidate_name || fields.name);
2949
- const mobileField = "field_" + fields.telephone;
2950
- const conditions = {};
2951
- if (args.name) conditions[nameField] = [{ [nameField]: args.name, [`${nameField}_condition_type`]: "like" }];
2952
- if (args.mobile) conditions[mobileField] = [{ [mobileField]: args.mobile, [`${mobileField}_condition_type`]: "like" }];
2953
- const body = buildListBody({
2954
- listId: talentList.listId,
2955
- cusMenuId: talentList.cusMenuId,
2956
- menuName: talentList.menuName || "\u4EBA\u624D\u7BA1\u7406",
2957
- title: "\u4EBA\u624D\u5E93",
2958
- current: toPositiveInt(args.current, "current") ?? 1,
2959
- pageSize: toNumber(args.pageSize, "pageSize") ?? 10,
2960
- conditions
2961
- });
2962
- const { rows, displayRows, total, totalExact, totalNote } = await fetchListRows(env.host, talentObjId, talentList.listId, body);
2963
- let creatorNameMap = {};
2964
- if (!displayRows) {
2965
- const pureIds = [...new Set(rows.map((row) => row?.creator).filter(isPlainId).map((value) => String(value).trim()))];
2966
- if (pureIds.length) {
2967
- creatorNameMap = {};
2968
- for (let index = 0; index < pureIds.length; index += 100) {
2969
- const batch = pureIds.slice(index, index + 100);
2970
- try {
2971
- const response = await env.host.request({
2972
- path: "/api/hrm/common/getEmployeeByIds",
2973
- body: { userIdList: batch, externalIdList: [] },
2974
- headers: { devtype: "5" }
2975
- });
2976
- for (const user of response?.data?.userList || []) {
2977
- if (user?.id != null) creatorNameMap[String(user.id)] = user.name || null;
2978
- }
2979
- } catch {
2980
- }
2981
- }
2982
- }
2983
- }
2984
- const statusMap = cache.options?.recruit_status || {};
2985
- const dictNameMap = {};
2986
- for (const item of cache.dict?.raw || []) {
2987
- if (item.option_id && item.selection_name) dictNameMap[item.option_id] = item.selection_name;
2988
- }
2989
- const dataRows = rows.map((row) => {
2990
- let creator = null;
2991
- if (displayRows) {
2992
- creator = namesOf(row?.creator);
2993
- } else if (row?.creator != null) {
2994
- const raw = String(row.creator).trim();
2995
- creator = isPlainId(raw) ? creatorNameMap[raw] || raw : namesOf(raw) || raw;
2996
- }
2997
- return {
2998
- talentId: row?.form_data_id || row?.id || null,
2999
- name: row?.candidate_name || row?.name || null,
3000
- mobile: row?.telephone || null,
3001
- channel: dictNameMap[row?.channel] || row?.channel || null,
3002
- status: statusMap[row?.recruit_status] || row?.recruit_status || null,
3003
- creator,
3004
- createTime: row?.create_time || null
3005
- };
3006
- });
3007
- return ehrSuccess("ehr.recruit.candidate.search", { total, totalExact, ...totalNote ? { totalNote } : {}, rows: dataRows });
3008
- }
3009
- };
3010
- var candidateList = {
3011
- name: "ehr.recruit.candidate.list",
3012
- resource: "recruit",
3013
- method: "candidate.list",
3014
- action: "ehr.recruit.candidate.list",
3015
- summary: "\u67E5\u8BE2\u62DB\u8058\u4E1A\u52A1\u5217\u8868\uFF08demand \u62DB\u8058\u9700\u6C42 / position \u62DB\u8058\u804C\u4F4D / entry \u5165\u804C\u7BA1\u7406 / offer \u5F55\u7528\u7BA1\u7406\uFF09\uFF0CdisplayData \u663E\u793A\u5C42\u96F6\u7FFB\u8BD1\u8F93\u51FA",
3016
- capability: "recruit",
3017
- risk: READ,
3018
- inputSchema: schemaOf({
3019
- type: { type: "string", enum: ["demand", "position", "entry", "offer"], description: "\u5217\u8868\u7C7B\u578B" },
3020
- current: SCHEMA_ID,
3021
- pageSize: SCHEMA_ID
3022
- }, { required: ["type"] }),
3023
- handler: async (args, env) => {
3024
- const type = typeof args.type === "string" ? args.type : "";
3025
- const listType = LIST_TYPES[type];
3026
- if (!listType) {
3027
- throw ehrValidationError("enum_invalid", `type \u53D6\u503C\u5FC5\u987B\u662F ${Object.keys(LIST_TYPES).join(" / ")} \u4E4B\u4E00`);
3028
- }
3029
- if (!env.recruitContext) {
3030
- throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
3031
- }
3032
- const cache = await env.recruitContext.load(env.host);
3033
- const listParam = cache.lists[listType.listKey];
3034
- if (!listParam?.listId) {
3035
- throw new RecruitContextError(
3036
- "recruit_list_unavailable",
3037
- `\u5F53\u524D\u73AF\u5883\u672A\u542F\u7528\u300C${listType.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\u3002`
3038
- );
3039
- }
3040
- const formObjId = cache.forms[listType.formType]?.objId;
3041
- if (!formObjId) {
3042
- throw new RecruitContextError("recruit_context_missing", `\u672A\u89E3\u6790\u5230\u300C${listType.title}\u300D\u5BF9\u5E94\u8868\u5355\uFF0C\u65E0\u6CD5\u67E5\u8BE2\u3002`);
3043
- }
3044
- const body = buildListBody({
3045
- listId: listParam.listId,
3046
- cusMenuId: listParam.cusMenuId,
3047
- menuName: listParam.menuName || listType.title,
3048
- title: listType.title,
3049
- current: toPositiveInt(args.current, "current") ?? 1,
3050
- pageSize: toNumber(args.pageSize, "pageSize") ?? 10
3051
- });
3052
- const { rows, displayRows, total, totalExact, totalNote } = await fetchListRows(env.host, formObjId, listParam.listId, body);
3053
- const outRows = rows.map((row) => {
3054
- const cleaned = { form_data_id: row?.form_data_id || row?.id };
3055
- for (const [key, value] of Object.entries(row || {})) {
3056
- if (displayRows) {
3057
- if (DISPLAY_UI_KEYS.has(key) || key === "form_data_id") continue;
3058
- if (key.endsWith("Span") || key.endsWith("Action")) continue;
3059
- const text2 = String(value ?? "");
3060
- if (!text2) continue;
3061
- if (typeof value === "object" || typeof value === "string" && text2.trimStart().startsWith("[")) {
3062
- const names2 = namesOf(value);
3063
- cleaned[key] = names2 != null ? names2 : text2;
3064
- continue;
3065
- }
3066
- cleaned[key] = text2;
3067
- continue;
3068
- }
3069
- if (INTERNAL_KEYS.has(key)) continue;
3070
- const text = String(value ?? "");
3071
- if (!text) continue;
3072
- if (/^\d{16,}$/.test(text)) continue;
3073
- const names = typeof value === "object" || typeof value === "string" && text.trimStart().startsWith("[") ? namesOf(value) : null;
3074
- cleaned[key] = names != null ? names : text;
3075
- }
3076
- return cleaned;
3077
- });
3078
- return ehrSuccess("ehr.recruit.candidate.list", {
3079
- type,
3080
- total,
3081
- totalExact,
3082
- ...totalNote ? { totalNote } : {},
3083
- current: body.current,
3084
- pageSize: body.pageSize,
3085
- rows: outRows
3086
- });
3087
- }
3088
- };
3089
- function personNames(value) {
3090
- const text = String(value ?? "").trim();
3091
- if (!text) return "";
3092
- if (text.startsWith("[")) {
3093
- try {
3094
- const arr = JSON.parse(text);
3095
- if (Array.isArray(arr)) {
3096
- const names = arr.map((item) => typeof item === "string" ? item : item?.name ?? item?.username ?? "").filter(Boolean);
3097
- if (names.length) return names.join("\u3001");
3098
- }
3099
- } catch {
3100
- }
3101
- }
3102
- return text;
3103
- }
3104
- function nameLike(value) {
3105
- const text = String(value ?? "").trim();
3106
- if (!text || /^\d{16,}$/.test(text)) return null;
3107
- return text;
3108
- }
3109
- async function resolveCurrentUserName(host) {
3110
- try {
3111
- const session = await host.getSession();
3112
- const baseUrl = String(session?.baseUrl || "");
3113
- if (!baseUrl) return "";
3114
- const resp = await fetch(
3115
- `${baseUrl}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(baseUrl)}`,
3116
- {
3117
- method: "POST",
3118
- headers: {
3119
- accept: "application/json",
3120
- "content-type": "application/json",
3121
- eteamsid: String(session?.eteamsId || ""),
3122
- cookie: typeof session?.cookieHeader === "function" ? session.cookieHeader() : "",
3123
- "user-agent": typeof session?.uaHeader === "function" ? session.uaHeader() : "weaver-work-cli",
3124
- origin: baseUrl
3125
- },
3126
- redirect: "manual"
3127
- }
3128
- );
3129
- const data = await resp.json().catch(() => ({}));
3130
- const cu = data.currentUser ?? data.data?.currentUser ?? null;
3131
- return cu ? String(cu.username ?? cu.id ?? "") : "";
3132
- } catch {
3133
- return "";
3134
- }
3135
- }
3136
- var interviewFeedbackTodo = {
3137
- name: "ehr.recruit.interview.feedbackTodo",
3138
- resource: "recruit",
3139
- method: "interview.feedbackTodo",
3140
- action: "ehr.recruit.interview.feedbackTodo",
3141
- summary: "\u9762\u8BD5\u5F85\u529E\uFF1A**\u53EA\u8FD4\u56DE\u6211\u4F5C\u4E3A\u9762\u8BD5\u5B98\u7684 AI \u9762\u8BD5\u5F85\u53CD\u9988\u6E05\u5355**\uFF08\u5019\u9009\u4EBA\u5DF2\u7B54\u5B8C\u3001\u5DF2\u6709\u5F55\u50CF\u3001\u6211\u8FD8\u6CA1\u586B\u610F\u89C1\uFF09\uFF1B\u6309\u9700\u7FFB\u9875 + \u5BA2\u6237\u7AEF\u7B5B\u9009\uFF0C\u4E0D\u8FD4\u56DE\u5168\u91CF\u9762\u8BD5",
3142
- capability: "recruit",
3143
- risk: READ,
3144
- inputSchema: schemaOf({
3145
- maxPages: { type: "integer", minimum: 1, maximum: 50, description: "\u6700\u591A\u7FFB\u9875\u6570\uFF08\u9ED8\u8BA4 5\uFF09" },
3146
- pageSize: { type: "integer", minimum: 1, maximum: 200, description: "\u5355\u9875\u6761\u6570\uFF08\u9ED8\u8BA4 200\uFF0C\u5B9E\u6D4B\u670D\u52A1\u7AEF\u4E0A\u9650 200\uFF09" }
3147
- }),
3148
- handler: async (args, env) => {
3149
- if (!env.recruitContext) throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
3150
- const cache = await env.recruitContext.load(env.host);
3151
- const listParam = cache.lists.interview;
3152
- const form = cache.forms.interview_plan;
3153
- if (!listParam?.listId || !form?.objId) {
3154
- throw new RecruitContextError(
3155
- "recruit_list_unavailable",
3156
- "\u5F53\u524D\u73AF\u5883\u672A\u542F\u7528\u300C\u9762\u8BD5\u7BA1\u7406\u300D\u529F\u80FD\uFF08\u672A\u627E\u5230\u9762\u8BD5\u5217\u8868/\u8868\u5355\u53C2\u6570\uFF0C\u6309\u80FD\u529B\u63A2\u6D4B\u62E6\u622A\uFF09\u3002"
3157
- );
3158
- }
3159
- const pageSize = toNumber(args.pageSize, "pageSize") ?? 200;
3160
- const maxPages = toNumber(args.maxPages, "maxPages") ?? 5;
3161
- const me = await resolveCurrentUserName(env.host);
3162
- const fields = cache.fields[form.objId] || {};
3163
- const fieldOpts = cache.field_options[form.objId] || {};
3164
- const statusOpts = fieldOpts[fields.union_status] || {};
3165
- const typeOpts = fieldOpts[fields.interview_type] || {};
3166
- const toCn = (map, raw) => {
3167
- const text = String(raw ?? "");
3168
- return map[text] ?? text;
3169
- };
3170
- const statusOf = (row) => toCn(statusOpts, row?.union_status_key ?? row?.union_status);
3171
- const typeOf = (row) => toCn(typeOpts, row?.interview_type_key ?? row?.interview_type);
3172
- const interviewerOf = (row) => personNames(row?.interviewer);
3173
- const scanned = [];
3174
- let lastPageCount = 0;
3175
- for (let i = 0; i < maxPages; i++) {
3176
- const body = buildListBody({
3177
- listId: listParam.listId,
3178
- cusMenuId: listParam.cusMenuId,
3179
- menuName: listParam.menuName || "\u9762\u8BD5\u7BA1\u7406",
3180
- title: "\u9762\u8BD5\u7BA1\u7406",
3181
- current: i + 1,
3182
- pageSize
3183
- });
3184
- const page = await fetchListRows(env.host, form.objId, listParam.listId, body);
3185
- const use = page.displayRows && page.displayRows.length ? page.displayRows : page.rows;
3186
- scanned.push(...use);
3187
- lastPageCount = use.length;
3188
- if (use.length < pageSize) break;
3189
- }
3190
- const pending = scanned.filter((row) => /待反馈/.test(statusOf(row)));
3191
- const mineAll = me ? pending.filter((row) => interviewerOf(row).includes(me)) : [];
3192
- const mineAi = mineAll.filter((row) => /AI\s*面试/i.test(typeOf(row)));
3193
- const slim = (row) => ({
3194
- form_data_id: row?.form_data_id ?? row?.id ?? null,
3195
- candidate_name: nameLike(row?.candidate_name) ?? nameLike(row?.talent_id) ?? nameLike(row?.name),
3196
- position_name: nameLike(row?.position_name) ?? nameLike(row?.position_manage_id),
3197
- start_time: row?.start_time ?? null,
3198
- interview_type_key: typeOf(row) || null,
3199
- interview_session: row?.interview_session ?? null,
3200
- interviewer: interviewerOf(row) || null,
3201
- union_status_key: statusOf(row) || null,
3202
- subject: row?.subject ?? null,
3203
- talent_id: row?.talent_id ?? null,
3204
- position_manage_id: row?.position_manage_id ?? null
3205
- });
3206
- const notes = [];
3207
- if (!me) notes.push("\u672A\u80FD\u89E3\u6790\u5F53\u524D\u767B\u5F55\u4EBA\u59D3\u540D\uFF08teamsCheck \u5931\u8D25\uFF09\uFF0C\u65E0\u6CD5\u6309\u9762\u8BD5\u5B98\u8FC7\u6EE4");
3208
- if (lastPageCount >= pageSize && scanned.length >= pageSize * maxPages) {
3209
- notes.push(`\u5DF2\u626B\u63CF ${scanned.length} \u6761\uFF08\u8FBE maxPages=${maxPages} \u4E0A\u9650\uFF09\uFF0C\u5982\u6000\u7591\u9057\u6F0F\u53EF\u52A0\u5927 maxPages`);
3210
- }
3211
- return ehrSuccess("ehr.recruit.interview.feedbackTodo", {
3212
- currentUser: me || null,
3213
- scanned: scanned.length,
3214
- myAiPendingFeedback: { count: mineAi.length, rows: mineAi.map(slim) },
3215
- myOtherPendingFeedback: {
3216
- count: mineAll.length - mineAi.length,
3217
- rows: mineAll.filter((row) => !/AI\s*面试/i.test(typeOf(row))).map(slim)
3218
- },
3219
- ...notes.length ? { note: notes.join("\uFF1B") } : {}
3220
- });
3221
- }
3222
- };
3223
- var candidateCard = {
3224
- name: "ehr.recruit.candidate.card",
3225
- resource: "recruit",
3226
- method: "candidate.card",
3227
- action: "ehr.recruit.candidate.card",
3228
- summary: "\u67E5\u770B\u4EBA\u624D\u8BE6\u60C5\u5361\u7247\uFF1A\u6743\u9650\u6821\u9A8C + \u56DB\u5F20\u5361\u7247\uFF08\u57FA\u672C\u4FE1\u606F/\u5E94\u8058\u8FDB\u5C55/\u9762\u8BD5\u5B89\u6392/\u6C9F\u901A\u5907\u6CE8\uFF09\u5E76\u884C\u62C9\u53D6\uFF0C\u59D3\u540D\u4E0E\u5B57\u5178\u5168\u90E8\u5185\u8054\u89E3\u6790",
3229
- capability: "recruit",
3230
- risk: READ,
3231
- inputSchema: schemaOf(
3232
- { talentId: { oneOf: [{ type: "integer" }, { type: "string", pattern: "^\\d+$" }], description: "\u4EBA\u624D ID\uFF0C\u6765\u81EA candidate.search \u7684 talentId" } },
3233
- { required: ["talentId"] }
3234
- ),
3235
- handler: async (args, env) => {
3236
- const talentId = args.talentId;
3237
- if (talentId === void 0 || talentId === null || talentId === "") {
3238
- throw ehrValidationError("talent_id_required", "talentId \u5FC5\u586B");
3239
- }
3240
- if (!/^\d+$/.test(String(talentId).trim())) {
3241
- throw ehrValidationError("talent_id_invalid", "talentId \u5FC5\u987B\u662F\u6570\u5B57 ID");
3242
- }
3243
- const id = String(talentId).trim();
3244
- if (!env.recruitContext) {
3245
- throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
3246
- }
3247
- const cache = await env.recruitContext.load(env.host);
3248
- const talentListId = cache.lists.talent?.listId || "";
3249
- const extra = talentListId ? { ebBusinessId: talentListId } : {};
3250
- const rk1 = randHex6();
3251
- const [rightsResponse, prepareResponse] = await Promise.all([
3252
- env.host.request({
3253
- path: `/api/recruit/talent/hasRights?formDataId=${id}`,
3254
- method: "POST",
3255
- headers: extra
3256
- }),
3257
- env.host.request({
3258
- path: `/api/recruit/candidate/getCandidateCardPrepareQuery?talentId=${id}&randomkey_${rk1}=${rk1}`,
3259
- headers: extra
3260
- })
3261
- ]);
3262
- const rights = rightsResponse?.data || [];
3263
- if (!rights.includes("READ")) {
3264
- throw ehrValidationError("recruit_rights_denied", "\u5F53\u524D\u8D26\u53F7\u65E0\u8BE5\u4EBA\u624D\u8BE6\u60C5\u7684 READ \u6743\u9650\uFF0C\u65E0\u6CD5\u67E5\u770B\u3002");
3265
- }
3266
- const batchId = prepareResponse?.data?.batchId;
3267
- if (!batchId) {
3268
- throw new RecruitContextError("recruit_batch_id_missing", "\u672A\u53D6\u5230 batchId\uFF08prepareQuery \u8FD4\u56DE\u5F02\u5E38\uFF09\uFF0C\u65E0\u6CD5\u67E5\u770B\u8BE6\u60C5\u5361\u7247\u3002");
3269
- }
3270
- const rk2 = randHex6();
3271
- const v = randHex6();
3272
- const query = `talentId=${id}&batchId=${batchId}&v=${v}&randomkey_${rk2}=${rk2}`;
3273
- const cardBody = { talentId: id, batchId, current: 1, pageSize: 100 };
3274
- const [base, other, interview, remark] = await Promise.all([
3275
- env.host.request({ path: `/api/recruit/candidate/getCandidateCardBaseInfo?${query}`, headers: extra }),
3276
- env.host.request({ path: `/api/recruit/candidate/getCandidateCardOtherInfo?${query}`, headers: extra }),
3277
- env.host.request({ path: "/api/recruit/candidate/getCandidateCardInterviewInfo", body: cardBody, headers: extra }),
3278
- env.host.request({ path: "/api/recruit/candidate/getCandidateCardRemark", body: cardBody, headers: extra })
3279
- ]);
3280
- const statusMap = cache.options?.recruit_status || {};
3281
- const talent = base?.data?.talent || {};
3282
- const currentBatch = other?.data?.currentCandidateBatch || {};
3283
- const interviewList = interview?.data?.list || [];
3284
- const remarkList = remark?.data?.list || [];
3285
- const baseUrl = (await env.host.getSession()).baseUrl.replace(/\/+$/, "");
3286
- return ehrSuccess("ehr.recruit.candidate.card", {
3287
- talentId: id,
3288
- cardLink: cache.ebdDetailPageId ? `${baseUrl}/sp/ebdpage/view/${cache.ebdDetailPageId}?talentId=${id}${String(cache.selfUse ?? "") === "1" ? "&recruitType=useSelf" : ""}` : null,
3289
- baseInfo: {
3290
- name: talent?.hrm?.name,
3291
- mobile: talent?.telephone,
3292
- channel: talent?.channel,
3293
- status: statusMap[base?.data?.recruitStatus] || base?.data?.recruitStatus || null,
3294
- createTime: talent?.createTime
3295
- },
3296
- position: {
3297
- position: currentBatch?.position?.name,
3298
- stage: currentBatch?.currentStageName,
3299
- stages: (currentBatch?.allStage || []).map((stage) => stage?.stageName).filter(Boolean),
3300
- archiveStatus: currentBatch?.archiveStatus?.name,
3301
- employManager: namesOf(currentBatch?.employManager),
3302
- channel: currentBatch?.channel,
3303
- candidateManageId: currentBatch?.id || currentBatch?.candidateManageId || null,
3304
- batchId: currentBatch?.batchId || batchId,
3305
- positionId: currentBatch?.position?.id || null
3306
- },
3307
- interviews: interviewList.map((item) => ({
3308
- subject: item?.subject,
3309
- startTime: item?.startTime || null,
3310
- endTime: item?.endTime || null,
3311
- status: namesOf(item?.unionStatus),
3312
- type: namesOf(item?.interviewType),
3313
- round: namesOf(item?.interviewSession),
3314
- interviewer: namesOf(item?.interviewer),
3315
- arranger: namesOf(item?.creator),
3316
- updater: namesOf(item?.updater),
3317
- position: namesOf(item?.positionManageId),
3318
- feedbacks: (item?.interviewFeedBack || []).map((feedback) => ({
3319
- result: feedback?.result?.[0]?.name || null,
3320
- comment: feedback?.judgement ? feedback.judgement.replace(/<[^>]+>/g, "").trim() : null,
3321
- feedbacker: namesOf(feedback?.updater),
3322
- createTime: feedback?.createTime
3323
- }))
3324
- })),
3325
- remarks: remarkList.map((item) => ({
3326
- time: item?.contactTime || item?.createTime,
3327
- creator: item?.creator?.username || item?.creator?.name || null,
3328
- content: item?.remark
3329
- }))
3330
- });
3331
- }
3332
- };
3333
- var recruitOperations = [
3334
- contextResolve,
3335
- candidateSearch,
3336
- candidateList,
3337
- candidateCard,
3338
- interviewFeedbackTodo
3339
- ];
3340
-
3341
2264
  // src/shortcuts/ehr/operations/recruit-write.ts
3342
2265
  import { createHash as createHash3 } from "node:crypto";
3343
2266
  var CONTINUATION_TTL_SECONDS2 = 600;
@@ -3631,21 +2554,25 @@ async function resolveCandidateForWrite(host, cache, talentId) {
3631
2554
  throw ehrValidationError("recruit_batch_id_missing", "\u672A\u53D6\u5230 batchId\uFF08prepareQuery \u8FD4\u56DE\u5F02\u5E38\uFF09\uFF0C\u65E0\u6CD5\u5B9A\u4F4D\u5019\u9009\u4EBA\u5E94\u8058\u6279\u6B21");
3632
2555
  }
3633
2556
  const v = randomHex6();
3634
- const otherResponse = await host.request({
3635
- path: `/api/recruit/candidate/getCandidateCardOtherInfo?talentId=${talentId}&batchId=${batchId}&v=${v}&randomkey_ab=ab`,
3636
- headers: extra
3637
- });
3638
- const batch = otherResponse?.data?.currentCandidateBatch || {};
3639
- let name = batch?.talent?.name || "";
3640
- if (!name) {
3641
- const baseResponse = await host.request({
2557
+ const [otherResponse, baseResponse] = await Promise.all([
2558
+ host.request({
2559
+ path: `/api/recruit/candidate/getCandidateCardOtherInfo?talentId=${talentId}&batchId=${batchId}&v=${v}&randomkey_ab=ab`,
2560
+ headers: extra
2561
+ }),
2562
+ host.request({
3642
2563
  path: `/api/recruit/candidate/getCandidateCardBaseInfo?talentId=${talentId}&batchId=${batchId}&v=${v}&randomkey_cd=cd`,
3643
2564
  headers: extra
3644
- });
3645
- name = baseResponse?.data?.talent?.hrm?.name || "";
3646
- }
2565
+ }).catch(() => null)
2566
+ ]);
2567
+ const batch = otherResponse?.data?.currentCandidateBatch || {};
2568
+ const talentBase = baseResponse?.data?.talent || {};
2569
+ const name = batch?.talent?.name || talentBase?.hrm?.name || talentBase?.name || "";
2570
+ const email = String(talentBase.email || talentBase.hrm?.email || "").trim();
2571
+ const mobile = String(talentBase.telephone || talentBase.mobile || "").trim();
3647
2572
  return {
3648
2573
  name,
2574
+ email,
2575
+ mobile,
3649
2576
  candidateManageId: batch?.id || batch?.candidateManageId || null,
3650
2577
  positionId: batch?.position?.id || null,
3651
2578
  positionName: batch?.position?.name || null,
@@ -3855,6 +2782,7 @@ var candidateCreateApply = {
3855
2782
  }
3856
2783
  };
3857
2784
  var INTERVIEW_TYPES = ["\u73B0\u573A\u9762\u8BD5", "\u89C6\u9891\u9762\u8BD5", "\u7535\u8BDD\u9762\u8BD5", "AI\u9762\u8BD5"];
2785
+ var AI_DEFAULT_PLAN_NAME = "\u5927\u6A21\u578B\u9762\u8BD5";
3858
2786
  var INTERVIEW_DATAKEYS = [
3859
2787
  "subject",
3860
2788
  "start_time",
@@ -3877,15 +2805,150 @@ function defaultInterviewWindow() {
3877
2805
  const end = new Date(now.getTime() + 60 * 60 * 1e3);
3878
2806
  return { start: fmt(now), end: fmt(end) };
3879
2807
  }
2808
+ async function resolveCurrentUserForWrite(host) {
2809
+ try {
2810
+ const session = await host.getSession();
2811
+ const baseUrl = String(session?.baseUrl || "");
2812
+ if (!baseUrl) return null;
2813
+ const resp = await fetch(
2814
+ `${baseUrl}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(baseUrl)}`,
2815
+ {
2816
+ method: "POST",
2817
+ headers: {
2818
+ accept: "application/json",
2819
+ "content-type": "application/json",
2820
+ eteamsid: String(session?.eteamsId || ""),
2821
+ cookie: typeof session?.cookieHeader === "function" ? session.cookieHeader() : "",
2822
+ "user-agent": typeof session?.uaHeader === "function" ? session.uaHeader() : "weaver-work-cli",
2823
+ origin: baseUrl
2824
+ },
2825
+ redirect: "manual"
2826
+ }
2827
+ );
2828
+ const data = await resp.json().catch(() => ({}));
2829
+ const cu = data?.currentUser ?? data?.data?.currentUser ?? null;
2830
+ if (!cu?.id) return null;
2831
+ return { id: String(cu.id), name: String(cu.username || cu.id) };
2832
+ } catch {
2833
+ return null;
2834
+ }
2835
+ }
2836
+ async function fetchLayoutIdForForm(host, formId) {
2837
+ try {
2838
+ const r = await host.request({
2839
+ path: `/api/ebuilder/form/form/core/getFormLayout?formId=${encodeURIComponent(formId)}`,
2840
+ method: "GET"
2841
+ });
2842
+ const id = r?.data?.formLayout?.id;
2843
+ if (id) return String(id);
2844
+ } catch {
2845
+ }
2846
+ try {
2847
+ const r = await host.request({
2848
+ path: "/api/ebuilder/form/form/core/getFormLayout",
2849
+ method: "POST",
2850
+ body: { module: "ebuilderform", formId, terminalType: "PC" }
2851
+ });
2852
+ const id = r?.data?.formLayout?.id;
2853
+ if (id) return String(id);
2854
+ } catch {
2855
+ }
2856
+ return formId;
2857
+ }
2858
+ var DIGITAL_PLAN_NAME_KEYS = ["msfa_name", "digital_name", "plan_name", "scheme_name"];
2859
+ async function listDigitalPlansForWrite(host, cache, formId, layoutId, fieldId) {
2860
+ try {
2861
+ const r = await host.request({
2862
+ path: "/api/ebuilder/form/common/browser/complete/ebuilder",
2863
+ body: {
2864
+ browserMultiple: false,
2865
+ newly: 1,
2866
+ searchValue: "",
2867
+ formParam: JSON.stringify({ formId, layoutId, fieldId, filterItems: [], module: "ebuilderform", dataDetails: [] }),
2868
+ openScroll: true,
2869
+ controlId: fieldId,
2870
+ businessId: formId,
2871
+ variableMap: {}
2872
+ }
2873
+ });
2874
+ if (r?.status !== false && r?.data) {
2875
+ const list = r.data.data || [];
2876
+ const items2 = list.filter((x) => x && x.id).map((x) => ({ id: String(x.id), name: String(x.name || "").trim() }));
2877
+ if (items2.length) return { available: true, items: items2 };
2878
+ }
2879
+ } catch {
2880
+ }
2881
+ const menus = cache.lists?.all_search || [];
2882
+ const menu = menus.find((m) => (m.menuName || "").includes("\u9762\u8BD5\u6570\u5B57\u4EBA")) || menus.find((m) => (m.menuName || "").includes("\u6570\u5B57\u4EBA\u914D\u7F6E"));
2883
+ if (!menu?.listId) {
2884
+ return { available: false, items: [], reason: "\u5173\u8054\u5B57\u6BB5\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" };
2885
+ }
2886
+ let objId = "";
2887
+ try {
2888
+ const r = await host.request({
2889
+ path: `/api/ebuilder/form/obj/getObjsByTag?appId=${encodeURIComponent(String(cache.appId || ""))}&tag=uf_rcrt_digital_human`,
2890
+ method: "GET"
2891
+ });
2892
+ const d = r?.data;
2893
+ if (Array.isArray(d) && d.length) objId = String(d[0]?.id || "");
2894
+ } catch {
2895
+ }
2896
+ if (!objId) return { available: false, items: [], reason: "\u672A\u80FD\u89E3\u6790\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D\u5BF9\u8C61 id" };
2897
+ const rows = [];
2898
+ for (let cur = 1; cur <= 20; cur++) {
2899
+ const r = await host.request({
2900
+ path: `/api/ebuilder/form/list/getListDatas?passid=${objId}&cid=${objId}`,
2901
+ body: {
2902
+ current: cur,
2903
+ pageSize: 100,
2904
+ tableuid: randomHex32(),
2905
+ listId: menu.listId,
2906
+ searchParamData: { searchType: "3", type: "and" },
2907
+ terminal: "PC",
2908
+ browser: "Chrome",
2909
+ requiredMatch: 1,
2910
+ mapResult: 1,
2911
+ recheckQuery: 1,
2912
+ isFollow: 0,
2913
+ isShareToMe: 0,
2914
+ filterCondition: {},
2915
+ cusMenuId: menu.cusMenuId,
2916
+ urlPageTitle: menu.menuName,
2917
+ menuName: menu.menuName
2918
+ },
2919
+ headers: { ebBusinessId: String(menu.listId) }
2920
+ });
2921
+ const page = r?.data?.data || [];
2922
+ rows.push(...page);
2923
+ if (page.length < 100) break;
2924
+ }
2925
+ const items = rows.map((x) => {
2926
+ const nk = DIGITAL_PLAN_NAME_KEYS.find((k) => String(x[k] || "").trim());
2927
+ return { id: String(x.form_data_id || x.id || ""), name: String(nk ? x[nk] : "").trim() };
2928
+ }).filter((x) => x.id);
2929
+ if (!items.length) return { available: false, items: [], reason: "\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D\u6682\u65E0\u53EF\u9009\u65B9\u6848" };
2930
+ return { available: true, items };
2931
+ }
2932
+ function pickDigitalPlanForWrite(items, key) {
2933
+ const k = String(key || "").trim();
2934
+ if (!k) return { plan: null, ambiguous: [] };
2935
+ const byId = items.filter((x) => x.id === k);
2936
+ if (byId.length === 1) return { plan: byId[0], ambiguous: [] };
2937
+ const byExact = items.filter((x) => x.name === k);
2938
+ if (byExact.length === 1) return { plan: byExact[0], ambiguous: [] };
2939
+ const byLike = items.filter((x) => x.name.includes(k));
2940
+ if (byLike.length === 1) return { plan: byLike[0], ambiguous: [] };
2941
+ return { plan: null, ambiguous: byLike };
2942
+ }
3880
2943
  async function buildInterviewPlan(env, cache, args) {
3881
2944
  const host = env.host;
3882
2945
  const talentId = requiredString(args, "talentId", "talent_id_required", "\u4EBA\u624D ID");
3883
2946
  if (!/^\d+$/.test(talentId)) throw ehrValidationError("talent_id_invalid", "talentId \u5FC5\u987B\u662F\u6570\u5B57 ID");
3884
- const interviewer = requiredString(args, "interviewer", "interviewer_required", "\u9762\u8BD5\u5B98\u59D3\u540D");
3885
2947
  const type = typeof args.type === "string" && args.type.trim() ? args.type.trim() : "\u73B0\u573A\u9762\u8BD5";
3886
2948
  if (!INTERVIEW_TYPES.includes(type)) {
3887
2949
  throw ehrValidationError("enum_invalid", `type\uFF08\u9762\u8BD5\u65B9\u5F0F\uFF09\u53D6\u503C\u5FC5\u987B\u662F ${INTERVIEW_TYPES.join(" / ")} \u4E4B\u4E00`);
3888
2950
  }
2951
+ const isAI = type === "AI\u9762\u8BD5";
3889
2952
  const session = typeof args.session === "string" && args.session.trim() ? args.session.trim() : "\u521D\u8BD5";
3890
2953
  const candidate = await resolveCandidateForWrite(host, cache, talentId);
3891
2954
  const positionId = args.positionId ? String(args.positionId) : candidate.positionId;
@@ -3914,9 +2977,69 @@ async function buildInterviewPlan(env, cache, args) {
3914
2977
  if (!sessionOptionId) {
3915
2978
  throw ehrValidationError("enum_invalid", `\u9762\u8BD5\u8F6E\u6B21\u300C${session}\u300D\u4E0D\u5728\u679A\u4E3E\u4E2D\uFF08${Object.keys(sessionMap).join(" / ")}\uFF09`);
3916
2979
  }
3917
- const interviewerId = await resolvePersonId(host, fieldIdOf("interviewer"), interviewer);
3918
- if (!interviewerId) {
3919
- throw ehrValidationError("interviewer_not_found", `\u9762\u8BD5\u5B98\u300C${interviewer}\u300D\u89E3\u6790\u4E0D\u5230 userId\uFF08browser/complete/resource \u65E0\u5339\u914D\uFF09`);
2980
+ const interviewerInput = typeof args.interviewer === "string" ? args.interviewer.trim() : "";
2981
+ let interviewerId = null;
2982
+ let interviewer = interviewerInput;
2983
+ if (interviewerInput) {
2984
+ interviewerId = await resolvePersonId(host, fieldIdOf("interviewer"), interviewerInput);
2985
+ if (!interviewerId) {
2986
+ throw ehrValidationError("interviewer_not_found", `\u9762\u8BD5\u5B98\u300C${interviewerInput}\u300D\u89E3\u6790\u4E0D\u5230 userId\uFF08browser/complete/resource \u65E0\u5339\u914D\uFF09`);
2987
+ }
2988
+ } else {
2989
+ const me = await resolveCurrentUserForWrite(host);
2990
+ if (!me) {
2991
+ throw ehrValidationError("interviewer_unresolved", "\u672A\u6307\u5B9A\u9762\u8BD5\u5B98\uFF0C\u4E14\u65E0\u6CD5\u89E3\u6790\u5F53\u524D\u767B\u5F55\u4EBA\uFF08teamsCheck \u5931\u8D25\uFF09\uFF1B\u8BF7\u7528 interviewer \u53C2\u6570\u6307\u5B9A");
2992
+ }
2993
+ interviewerId = await resolvePersonId(host, fieldIdOf("interviewer"), me.name);
2994
+ if (!interviewerId) {
2995
+ throw ehrValidationError(
2996
+ "interviewer_unresolved",
2997
+ `\u672A\u6307\u5B9A\u9762\u8BD5\u5B98\uFF0C\u5F53\u524D\u767B\u5F55\u4EBA\u300C${me.name}\u300D\u5728\u4EBA\u5458\u5B57\u6BB5\u4E2D\u89E3\u6790\u4E0D\u5230\uFF1B\u8BF7\u7528 interviewer \u53C2\u6570\u6307\u5B9A`
2998
+ );
2999
+ }
3000
+ interviewer = me.name;
3001
+ }
3002
+ let aiPlan = null;
3003
+ let aiPlans = [];
3004
+ let email = String(args.email || "").trim() || candidate.email;
3005
+ const mobile = candidate.mobile;
3006
+ if (isAI) {
3007
+ const digitalFid = fieldIdOf("digital_id");
3008
+ if (!digitalFid) {
3009
+ throw ehrValidationError("recruit_field_missing", "\u9762\u8BD5\u8BA1\u5212\u8868\u5355\u7F3A\u5C11\u300CAI\u9762\u8BD5\u65B9\u6848\u300D\u5B57\u6BB5\uFF08digital_id\uFF09\uFF0C\u65E0\u6CD5\u5B89\u6392 AI \u9762\u8BD5");
3010
+ }
3011
+ const layoutId = await fetchLayoutIdForForm(host, formId);
3012
+ const plans = await listDigitalPlansForWrite(host, cache, formId, layoutId, digitalFid);
3013
+ aiPlans = plans.items;
3014
+ if (!plans.available) {
3015
+ throw ehrValidationError(
3016
+ "ai_plan_unavailable",
3017
+ `AI \u9762\u8BD5\u5FC5\u987B\u9009\u62E9\u300CAI\u9762\u8BD5\u65B9\u6848\u300D\uFF0C\u4F46${plans.reason || "\u6682\u65E0\u53EF\u9009\u65B9\u6848"}\uFF1B\u8BF7\u5148\u5728\u8BE5\u73AF\u5883\u7EF4\u62A4\u300C\u9762\u8BD5\u6570\u5B57\u4EBA\u914D\u7F6E\u300D`
3018
+ );
3019
+ }
3020
+ const wanted = typeof args.aiPlan === "string" && args.aiPlan.trim() ? args.aiPlan.trim() : AI_DEFAULT_PLAN_NAME;
3021
+ const picked = pickDigitalPlanForWrite(aiPlans, wanted);
3022
+ if (picked.plan) {
3023
+ aiPlan = picked.plan;
3024
+ } else if (picked.ambiguous.length > 1) {
3025
+ throw ehrValidationError(
3026
+ "ai_plan_ambiguous",
3027
+ `AI\u9762\u8BD5\u65B9\u6848\u300C${wanted}\u300D\u547D\u4E2D\u591A\u4E2A\uFF1A${picked.ambiguous.map((x) => x.name).join("\u3001")}\uFF0C\u8BF7\u7528 aiPlan \u6307\u660E\u5177\u4F53\u65B9\u6848`,
3028
+ { candidates: picked.ambiguous }
3029
+ );
3030
+ } else {
3031
+ throw ehrValidationError(
3032
+ "ai_plan_not_found",
3033
+ `\u672A\u627E\u5230 AI\u9762\u8BD5\u65B9\u6848\u300C${wanted}\u300D\uFF0C\u8BF7\u7528 aiPlan \u4ECE\u5019\u9009\u4E2D\u6307\u5B9A\uFF08\u5019\u9009\uFF1A${aiPlans.map((x) => x.name).join("\u3001")}\uFF09`,
3034
+ { candidates: aiPlans }
3035
+ );
3036
+ }
3037
+ if (!email) {
3038
+ throw ehrValidationError("email_required", "AI \u9762\u8BD5\u8981\u6C42\u586B\u5199\u5019\u9009\u4EBA\u90AE\u7BB1\uFF1A\u4EBA\u624D\u8D44\u6599\u91CC\u6CA1\u6709\u90AE\u7BB1\uFF0C\u8BF7\u7528 email \u53C2\u6570\u63D0\u4F9B");
3039
+ }
3040
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
3041
+ throw ehrValidationError("email_invalid", `\u5019\u9009\u4EBA\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E\uFF1A${email}`);
3042
+ }
3920
3043
  }
3921
3044
  const window = defaultInterviewWindow();
3922
3045
  const start = typeof args.startTime === "string" && args.startTime.trim() ? args.startTime.trim() : window.start;
@@ -3934,7 +3057,10 @@ async function buildInterviewPlan(env, cache, args) {
3934
3057
  { formField: { id: fieldIdOf("interviewer") }, dataOptions: [{ optionId: interviewerId, content: interviewer }] },
3935
3058
  { formField: { id: fieldIdOf("position_manage_id") }, dataOptions: [{ optionId: positionId, content: candidate.positionName }] },
3936
3059
  { formField: { id: fieldIdOf("talent_id") }, dataOptions: [{ optionId: talentId, content: candidate.name }] },
3937
- { formField: { id: fieldIdOf("candidate_manage_id") }, dataOptions: [{ optionId: candidate.candidateManageId, content: candidate.candidateManageId }] }
3060
+ { formField: { id: fieldIdOf("candidate_manage_id") }, dataOptions: [{ optionId: candidate.candidateManageId, content: candidate.candidateManageId }] },
3061
+ ...mobile ? [{ formField: { id: fieldIdOf("mobile") }, content: mobile }] : [],
3062
+ ...email ? [{ formField: { id: fieldIdOf("email") }, content: email }] : [],
3063
+ ...aiPlan ? [{ formField: { id: fieldIdOf("digital_id") }, dataOptions: [{ optionId: aiPlan.id, content: aiPlan.name }] }] : []
3938
3064
  ];
3939
3065
  return {
3940
3066
  formId,
@@ -3943,13 +3069,20 @@ async function buildInterviewPlan(env, cache, args) {
3943
3069
  candidateName: candidate.name,
3944
3070
  positionName: candidate.positionName,
3945
3071
  interviewTime: `${start} ~ ${end}`,
3946
- interviewer
3072
+ interviewer,
3073
+ email: email || void 0,
3074
+ mobile: mobile || void 0,
3075
+ isAIInterview: isAI,
3076
+ aiPlan,
3077
+ aiPlans: isAI ? aiPlans : void 0
3947
3078
  };
3948
3079
  }
3949
3080
  var interviewArrangeSchema = {
3950
3081
  talentId: { type: "string", pattern: "^\\d+$", description: "\u4EBA\u624D ID\uFF08candidate.search \u8FD4\u56DE\u7684 talentId\uFF09" },
3951
- interviewer: { type: "string", description: "\u9762\u8BD5\u5B98\u59D3\u540D\uFF08\u987B\u53EF\u89E3\u6790\u4E3A\u7CFB\u7EDF\u4EBA\u5458\uFF09" },
3082
+ interviewer: { type: "string", description: "\u9762\u8BD5\u5B98\u59D3\u540D\uFF08\u53EF\u9009\uFF1B\u4E0D\u586B\u9ED8\u8BA4\u5F53\u524D\u767B\u5F55\u4EBA\uFF09" },
3952
3083
  type: { type: "string", enum: [...INTERVIEW_TYPES], description: "\u9762\u8BD5\u65B9\u5F0F\uFF0C\u9ED8\u8BA4\u73B0\u573A\u9762\u8BD5" },
3084
+ aiPlan: { type: "string", description: "AI \u9762\u8BD5\u65B9\u6848\uFF08\u65B9\u6848\u540D\u6216 id\uFF1Btype=AI\u9762\u8BD5 \u65F6\u751F\u6548\uFF0C\u9ED8\u8BA4\u300C\u5927\u6A21\u578B\u9762\u8BD5\u300D\uFF09" },
3085
+ email: { type: "string", description: "\u5019\u9009\u4EBA\u90AE\u7BB1\uFF08AI \u9762\u8BD5\u5FC5\u586B\uFF1B\u4E0D\u586B\u5219\u53D6\u4EBA\u624D\u8D44\u6599\u91CC\u7684\u90AE\u7BB1\uFF09" },
3953
3086
  session: { type: "string", description: "\u9762\u8BD5\u8F6E\u6B21\uFF08\u5982 \u521D\u8BD5/\u590D\u8BD5\uFF09\uFF0C\u9ED8\u8BA4\u521D\u8BD5\uFF1B\u987B\u5728\u679A\u4E3E\u5185" },
3954
3087
  positionId: { type: "string", description: "\u5E94\u8058\u804C\u4F4D ID\uFF1B\u4E0D\u586B\u65F6\u4ECE\u4EBA\u624D\u5F53\u524D\u5E94\u8058\u6279\u6B21\u7EE7\u627F" },
3955
3088
  startTime: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$", description: "\u5F00\u59CB\u65F6\u95F4 YYYY-MM-DD HH:mm\uFF0C\u9ED8\u8BA4\u5F53\u524D\u65F6\u95F4" },
@@ -3965,7 +3098,7 @@ var interviewArrangePrepare = {
3965
3098
  capability: "recruit",
3966
3099
  risk: WRITE,
3967
3100
  requiresConfirmation: true,
3968
- inputSchema: schemaOf({ ...interviewArrangeSchema }, { required: ["talentId", "interviewer"] }),
3101
+ inputSchema: schemaOf({ ...interviewArrangeSchema }, { required: ["talentId"] }),
3969
3102
  handler: async (args, env) => {
3970
3103
  const cache = await requireRecruitCache(env);
3971
3104
  const plan = await buildInterviewPlan(env, cache, args);
@@ -3993,6 +3126,11 @@ var interviewArrangePrepare = {
3993
3126
  session: args.session || "\u521D\u8BD5",
3994
3127
  time: plan.interviewTime,
3995
3128
  interviewer: plan.interviewer,
3129
+ ...plan.isAIInterview ? {
3130
+ aiPlan: plan.aiPlan ? `${plan.aiPlan.name}\uFF08id=${plan.aiPlan.id}\uFF09` : "(\u672A\u6307\u5B9A)",
3131
+ email: plan.email || "(\u7F3A\u5931)",
3132
+ mobile: plan.mobile || "(\u4EBA\u624D\u8D44\u6599\u65E0\u624B\u673A\u53F7)"
3133
+ } : {},
3996
3134
  notify: notify ? "\u5C06\u89E6\u53D1\u300C\u65B0\u5EFA\u65E5\u7A0B\u300D\u9762\u8BD5\u901A\u77E5\u52A8\u4F5C\u6D41\uFF08\u5916\u90E8\u901A\u77E5\uFF09" : "\u9762\u8BD5\u901A\u77E5\u9ED8\u8BA4\u4E0D\u89E6\u53D1"
3997
3135
  },
3998
3136
  willSubmit: { formId: plan.formId, objId: plan.objId, dataDetails: plan.dataDetails },
@@ -5747,42 +4885,106 @@ async function uploadResumeFile(env, filePath, fieldId, formId) {
5747
4885
  }
5748
4886
  return { fileid: String(fileid), name, size };
5749
4887
  }
5750
- async function applyPlatformTags(env, talentId, tagNames) {
5751
- if (!tagNames.length) return { applied: [], skipped: [] };
5752
- const list = await env.host.request({
4888
+ async function queryTagByName(env, name) {
4889
+ const pageSize = 100;
4890
+ const bodyOf = (type) => ({ type, name, order: 8, pageSize, pageNo: 1, search: 1, tagGroup: 0, tagModules: ["ebuilderform"] });
4891
+ const rs = await Promise.all(
4892
+ [0, 1].map(
4893
+ (type) => env.host.request({
4894
+ path: "/api/my/tag/query",
4895
+ method: "POST",
4896
+ body: bodyOf(type),
4897
+ operation: "ehr.recruit.candidate.import.apply"
4898
+ }).catch(() => null)
4899
+ )
4900
+ );
4901
+ const items = [];
4902
+ let filtered = true;
4903
+ for (const r of rs) {
4904
+ const d = r?.data || {};
4905
+ const list = Array.isArray(d.data) ? d.data : [];
4906
+ const total = Number(d.total || 0);
4907
+ if (list.length >= pageSize && total >= pageSize) filtered = false;
4908
+ items.push(...list);
4909
+ }
4910
+ return { items, filtered };
4911
+ }
4912
+ async function fetchTagsOfType(env, type) {
4913
+ const pageSize = 100;
4914
+ const bodyOf = (pageNo) => ({ type, name: "", order: 8, pageSize, pageNo, search: 1, tagGroup: 0, tagModules: ["ebuilderform"] });
4915
+ const requestPage = (pageNo) => env.host.request({
5753
4916
  path: "/api/my/tag/query",
5754
4917
  method: "POST",
5755
- body: { tagModules: ["ebuilderform"], current: 1, pageSize: 500 },
4918
+ body: bodyOf(pageNo),
5756
4919
  operation: "ehr.recruit.candidate.import.apply"
5757
- });
5758
- const rows = list?.data?.data || list?.data?.list || [];
4920
+ }).catch(() => null);
4921
+ const first = await requestPage(1);
4922
+ const d = first?.data || {};
4923
+ const rows = Array.isArray(d.data) ? [...d.data] : [];
4924
+ const total = Number(d.total || 0);
4925
+ const pages = Math.min(40, Math.max(1, Math.ceil(total / pageSize)));
4926
+ if (pages > 1 && rows.length) {
4927
+ const rest = Array.from({ length: pages - 1 }, (_, i) => i + 2);
4928
+ for (let i = 0; i < rest.length; i += 5) {
4929
+ const batch = rest.slice(i, i + 5);
4930
+ const rs = await Promise.all(batch.map((p) => requestPage(p)));
4931
+ for (const r of rs) {
4932
+ const list = r?.data?.data;
4933
+ if (Array.isArray(list)) rows.push(...list);
4934
+ }
4935
+ }
4936
+ }
4937
+ return rows;
4938
+ }
4939
+ async function resolveTagIds(env, tagNames) {
4940
+ const norm = (s) => String(s == null ? "" : s).trim().toLowerCase();
4941
+ const uniqNames = [...new Set(tagNames.map((n) => String(n)).filter(Boolean))];
5759
4942
  const byName = /* @__PURE__ */ new Map();
5760
- for (const row of rows) {
5761
- const name = String(row.name ?? row.tagName ?? "").trim();
5762
- const id = row.tagId ?? row.id;
5763
- if (name && id !== void 0 && !byName.has(name)) byName.set(name, { id: String(id), name });
4943
+ const absorb = (row) => {
4944
+ const name = String(row?.name ?? row?.tagName ?? "").trim();
4945
+ const id = row?.tagId ?? row?.id;
4946
+ if (!name || id === void 0) return;
4947
+ const key = norm(name);
4948
+ const prev = byName.get(key);
4949
+ if (!prev) byName.set(key, { id: String(id), name });
4950
+ else if ((row?.type ?? 0) === 0) byName.set(key, { id: String(id), name });
4951
+ };
4952
+ const probes = await Promise.all(uniqNames.map((n) => queryTagByName(env, n)));
4953
+ const nameQueryUsable = probes.every((p) => p.filtered);
4954
+ if (nameQueryUsable) {
4955
+ for (const p of probes) for (const item of p.items) absorb(item);
4956
+ } else {
4957
+ for (const type of [0, 1]) {
4958
+ const rows = await fetchTagsOfType(env, type).catch(() => []);
4959
+ for (const item of rows) absorb(item);
4960
+ }
5764
4961
  }
5765
- const tags = [];
5766
- const applied = [];
4962
+ const resolved = [];
4963
+ const found = [];
5767
4964
  const skipped = [];
5768
4965
  for (const name of tagNames) {
5769
- const hit = byName.get(name);
4966
+ const hit = byName.get(norm(name));
5770
4967
  if (!hit) {
5771
4968
  skipped.push(name);
5772
4969
  continue;
5773
4970
  }
5774
- tags.push({ id: hit.id, name: hit.name, type: 0 });
5775
- applied.push(name);
4971
+ resolved.push({ id: hit.id, name: hit.name, type: 0 });
4972
+ found.push(name);
5776
4973
  }
5777
- if (tags.length) {
4974
+ return { resolved, found, skipped };
4975
+ }
4976
+ async function applyPlatformTags(env, talentId, tagNames) {
4977
+ if (!tagNames.length) return { applied: [], skipped: [] };
4978
+ const r = await resolveTagIds(env, tagNames);
4979
+ if (r.resolved.length) {
5778
4980
  await env.host.request({
5779
4981
  path: "/api/my/tag/common/batchAddEntityTag",
5780
4982
  method: "POST",
5781
- body: { tags, batchDatas: [{ entityId: talentId, module: "ebuilderform" }] },
4983
+ body: { tags: r.resolved, batchDatas: [{ entityId: talentId, module: "ebuilderform" }] },
5782
4984
  operation: "ehr.recruit.candidate.import.apply"
5783
4985
  });
5784
4986
  }
5785
- return { applied, skipped };
4987
+ return { applied: r.found, skipped: r.skipped };
5786
4988
  }
5787
4989
  async function requireCache2(env) {
5788
4990
  if (!env.recruitContext) throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
@@ -5972,7 +5174,11 @@ var candidateImportPrepare = {
5972
5174
  handler: async (args, env) => {
5973
5175
  const cache = await requireCache2(env);
5974
5176
  const plan = await buildImportPlan(env, cache, args);
5975
- const dup = await checkDuplicateByMobile(env, cache, plan.mobile);
5177
+ const tagNames = Array.isArray(args.tags) ? args.tags.map(String).filter(Boolean) : [];
5178
+ const [dup, tagCheck] = await Promise.all([
5179
+ checkDuplicateByMobile(env, cache, plan.mobile),
5180
+ tagNames.length ? resolveTagIds(env, tagNames).then((rt) => ({ requested: tagNames, found: rt.found, missing: rt.skipped })).catch((error) => ({ requested: tagNames, missing: [], error: error?.message || "\u6807\u7B7E\u9884\u68C0\u5931\u8D25" })) : Promise.resolve(null)
5181
+ ]);
5976
5182
  if (dup.length > 1) {
5977
5183
  throw ehrValidationError("duplicate_ambiguous", `\u624B\u673A\u53F7 ${plan.mobile} \u5B58\u5728\u591A\u6761\u5019\u9009\uFF08\u6570\u636E\u5F02\u5E38\uFF09\uFF0C\u8BF7\u4EBA\u5DE5\u6838\u5BF9\u540E\u518D\u51B3\u5B9A\uFF0C\u4E0D\u81EA\u52A8\u65B0\u5EFA`);
5978
5184
  }
@@ -6012,6 +5218,7 @@ var candidateImportPrepare = {
6012
5218
  details: plan.details,
6013
5219
  tags: Array.isArray(args.tags) ? args.tags : []
6014
5220
  },
5221
+ ...tagCheck ? { tagCheck } : {},
6015
5222
  continuation,
6016
5223
  expiresInSeconds: 600
6017
5224
  });
@@ -6077,37 +5284,28 @@ var candidateImportApply = {
6077
5284
  { candidateName: payload.candidateName, telephone: payload.telephone }
6078
5285
  );
6079
5286
  }
6080
- let batchOk = null;
6081
- let batchNote;
6082
- try {
6083
- await env.host.request({
6084
- path: `/api/recruit/candidate/initTalentBatch?formDataId=${encodeURIComponent(talentId)}`,
6085
- method: "POST",
6086
- operation: "ehr.recruit.candidate.import.apply"
6087
- });
6088
- batchOk = true;
6089
- } catch (error) {
6090
- batchOk = false;
6091
- batchNote = `\u4EBA\u624D\u5DF2\u5EFA\u6863\uFF0C\u4F46\u521D\u59CB\u5316\u4EBA\u624D\u6279\u6B21\u5931\u8D25\uFF1A${error?.message || "\u672A\u77E5\u9519\u8BEF"}\uFF08\u53EF\u5728\u4EBA\u624D\u5361\u7247\u624B\u52A8\u5904\u7406\uFF09`;
6092
- }
6093
- let labelMatch = null;
6094
- try {
6095
- const r = await env.host.request({
6096
- path: `/api/recruit/label/matchTalentLabel?talentId=${encodeURIComponent(talentId)}`,
6097
- method: "POST",
6098
- body: {},
6099
- operation: "ehr.recruit.candidate.import.apply"
6100
- });
6101
- labelMatch = r?.data === void 0 ? "TRIGGERED" : String(r.data);
6102
- } catch {
6103
- labelMatch = "FAILED\uFF08\u4E0D\u5F71\u54CD\u5EFA\u6863\uFF0C\u53EF\u7A0D\u540E\u5728\u4EBA\u624D\u5361\u7247\u624B\u52A8\u89E6\u53D1\uFF09";
6104
- }
6105
- let tagResult = { applied: [], skipped: [] };
6106
- try {
6107
- tagResult = await applyPlatformTags(env, talentId, Array.isArray(payload.tags) ? payload.tags.map(String) : []);
6108
- } catch (error) {
6109
- tagResult = { applied: [], skipped: Array.isArray(payload.tags) ? payload.tags.map(String) : [] };
6110
- }
5287
+ const batchTask = env.host.request({
5288
+ path: `/api/recruit/candidate/initTalentBatch?formDataId=${encodeURIComponent(talentId)}`,
5289
+ method: "POST",
5290
+ operation: "ehr.recruit.candidate.import.apply"
5291
+ }).then(() => ({ ok: true, note: void 0 })).catch((error) => ({
5292
+ ok: false,
5293
+ note: `\u4EBA\u624D\u5DF2\u5EFA\u6863\uFF0C\u4F46\u521D\u59CB\u5316\u4EBA\u624D\u6279\u6B21\u5931\u8D25\uFF1A${error?.message || "\u672A\u77E5\u9519\u8BEF"}\uFF08\u53EF\u5728\u4EBA\u624D\u5361\u7247\u624B\u52A8\u5904\u7406\uFF09`
5294
+ }));
5295
+ const labelTask = env.host.request({
5296
+ path: `/api/recruit/label/matchTalentLabel?talentId=${encodeURIComponent(talentId)}`,
5297
+ method: "POST",
5298
+ body: {},
5299
+ operation: "ehr.recruit.candidate.import.apply"
5300
+ }).then((r) => r?.data === void 0 ? "TRIGGERED" : String(r.data)).catch(() => "FAILED\uFF08\u4E0D\u5F71\u54CD\u5EFA\u6863\uFF0C\u53EF\u7A0D\u540E\u5728\u4EBA\u624D\u5361\u7247\u624B\u52A8\u89E6\u53D1\uFF09");
5301
+ const tagTask = applyPlatformTags(
5302
+ env,
5303
+ talentId,
5304
+ Array.isArray(payload.tags) ? payload.tags.map(String) : []
5305
+ ).catch(() => ({ applied: [], skipped: Array.isArray(payload.tags) ? payload.tags.map(String) : [] }));
5306
+ const [batchOutcome, labelMatch, tagResult] = await Promise.all([batchTask, labelTask, tagTask]);
5307
+ const batchOk = batchOutcome.ok;
5308
+ const batchNote = batchOutcome.note;
6111
5309
  const baseUrl = session.baseUrl.replace(/\/+$/, "");
6112
5310
  const extra = String(cache.selfUse) === "1" ? "&recruitType=useSelf" : "";
6113
5311
  const talentCardUrl = cache.ebdDetailPageId ? `${baseUrl}/sp/ebdpage/view/${cache.ebdDetailPageId}?talentId=${talentId}${extra}` : null;