weaver-work-cli 0.1.32 → 0.1.36

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 (211) hide show
  1. package/README.md +2 -2
  2. package/dist/chunks/{archive-UMGX72K6.js → archive-XBVH7JU7.js} +2 -2
  3. package/dist/chunks/{archive-oa-N2IYBLF3.js → archive-oa-MZM3EKFQ.js} +2 -2
  4. package/dist/chunks/{asset-GLEGKFG5.js → asset-4XO7UOUW.js} +2 -2
  5. package/dist/chunks/{attachment-content-AQNX5N3S.js → attachment-content-VXSWANAC.js} +3 -2
  6. package/dist/chunks/{blog-E44OTVDA.js → blog-PGZKOPSC.js} +2 -2
  7. package/dist/chunks/{caizhilian-DQUFZXZL.js → caizhilian-BH4ZVGUN.js} +2 -2
  8. package/dist/chunks/{calendar-JKPIRKFD.js → calendar-MXEMTIO4.js} +2 -2
  9. package/dist/chunks/{chunk-QWY6PGL6.js → chunk-2LV74IWP.js} +361 -56
  10. package/dist/chunks/chunk-3SO6QL6U.js +73 -0
  11. package/dist/chunks/chunk-5DQEEGCC.js +229 -0
  12. package/dist/chunks/{chunk-G3QJULO2.js → chunk-6LLJFKJE.js} +1 -1
  13. package/dist/chunks/{chunk-C6R3B2ML.js → chunk-BPE37FPB.js} +35 -7
  14. package/dist/chunks/chunk-BYATLLEF.js +68 -0
  15. package/dist/chunks/chunk-BYBSCN4X.js +932 -0
  16. package/dist/chunks/{chunk-CVZ2JOSW.js → chunk-F746NKNF.js} +1 -1
  17. package/dist/chunks/{chunk-GCG2NMWR.js → chunk-HXJQI5NM.js} +4 -2
  18. package/dist/chunks/{chunk-YRVDW4WN.js → chunk-I6QGIBDM.js} +2 -2
  19. package/dist/chunks/{chunk-H7J4DC5R.js → chunk-JCSN3JGM.js} +2 -2
  20. package/dist/chunks/{chunk-2GA5ZIQL.js → chunk-JNG42JKG.js} +30 -1
  21. package/dist/chunks/{chunk-DW6AUUKS.js → chunk-JUXVYKII.js} +9 -4
  22. package/dist/chunks/{chunk-6RKD4DRZ.js → chunk-JYKABYCV.js} +2 -2
  23. package/dist/chunks/{chunk-6EFWDL7Y.js → chunk-NCOVUAZK.js} +2 -2
  24. package/dist/chunks/{chunk-E4Z23GHM.js → chunk-QHFANTX7.js} +1114 -904
  25. package/dist/chunks/{ebuilder-form-V5FG2EHC.js → chunk-QYGO5EE2.js} +22 -1148
  26. package/dist/chunks/{chunk-IURUFGZK.js → chunk-T72N74ZT.js} +50 -33
  27. package/dist/chunks/{chunk-H6XSTSLM.js → chunk-TC6MZAO3.js} +137 -204
  28. package/dist/chunks/{chunk-WKI2FBIQ.js → chunk-VPUTVEMC.js} +737 -341
  29. package/dist/chunks/{chunk-WSKBSQ2U.js → chunk-VPXOPQJI.js} +2 -2
  30. package/dist/chunks/chunk-WXFPWCXG.js +774 -0
  31. package/dist/chunks/{chunk-4YC43QQ4.js → chunk-XMGSYD6S.js} +4 -4
  32. package/dist/chunks/conflict-JAB2LJEZ.js +21 -0
  33. package/dist/chunks/continuation-RN6PZN2M.js +11 -0
  34. package/dist/chunks/contract-XI2EEIZU.js +9110 -0
  35. package/dist/chunks/cowork-SCPGLBA4.js +2527 -0
  36. package/dist/chunks/ebuilder-form-EYLHNOTA.js +121 -0
  37. package/dist/chunks/{ehr-HMFY6VIL.js → ehr-EAP77IMT.js} +1344 -27
  38. package/dist/chunks/{esb-ZYSVB7SG.js → esb-2TGGXEE7.js} +4 -4
  39. package/dist/chunks/{fna-B7RKPAZD.js → fna-2X7ZE7BU.js} +13 -8
  40. package/dist/chunks/{host-OCH2BCCV.js → host-C6EL2RKL.js} +3 -3
  41. package/dist/chunks/{host-45WED2KK.js → host-I4NRK4MX.js} +3 -3
  42. package/dist/chunks/{host-I4Z6BFYX.js → host-Q4LZGCRU.js} +3 -3
  43. package/dist/chunks/host-UEI5FGDM.js +23 -0
  44. package/dist/chunks/{host-KNZ52LJZ.js → host-XCLDDXKQ.js} +3 -3
  45. package/dist/chunks/{hrm-2KAZGUHI.js → hrm-YXERJM76.js} +3 -3
  46. package/dist/chunks/{im-ACJJWH4U.js → im-A4VZN2TQ.js} +88 -12
  47. package/dist/chunks/{invoice-ZK3WCX2U.js → invoice-2Z6YPMIS.js} +3 -3
  48. package/dist/chunks/{jiuchuanhui-5PQD3IL2.js → jiuchuanhui-AR5GMKZB.js} +2 -2
  49. package/dist/chunks/{lazy-dispatch-JQXBW3GM.js → lazy-dispatch-QXW7P4X3.js} +4 -4
  50. package/dist/chunks/{mail-S2HDDXO2.js → mail-7GZ6CPBJ.js} +45 -24
  51. package/dist/chunks/{meeting-QVN3I37L.js → meeting-3NMGA3MB.js} +278 -43
  52. package/dist/chunks/{okr-QNG4VC2Q.js → okr-BZ6XUOAG.js} +2 -2
  53. package/dist/chunks/operations-5QL3AECO.js +11 -0
  54. package/dist/chunks/{plan-A25EMW6U.js → plan-DR5CMUYP.js} +2 -2
  55. package/dist/chunks/{project-G6XV66DZ.js → project-6J63MK33.js} +2 -2
  56. package/dist/chunks/{registry-DC6KR3NH.js → registry-KDD7DHNK.js} +4 -4
  57. package/dist/chunks/{registry-2VLUAESA.js → registry-OZSMDVPP.js} +2 -2
  58. package/dist/chunks/{registry-7C5UMIPL.js → registry-TKX2E7C7.js} +2 -2
  59. package/dist/chunks/{registry-TI3CIUXF.js → registry-VRTNC6UR.js} +8 -6
  60. package/dist/chunks/{room-recommend-WRTHO6Y2.js → room-recommend-XCWRMB5P.js} +4 -3
  61. package/dist/chunks/{schedule-KFIITGSK.js → schedule-IVBICP32.js} +4 -3
  62. package/dist/chunks/{workflow-DJ7SYWAF.js → workflow-D74NFK3C.js} +179 -6
  63. package/dist/index.js +35 -25
  64. package/dist/internal/e10/auth-recovery.js +14 -4
  65. package/dist/internal/e10/fetch-timing.js +31 -0
  66. package/dist/internal/e10/request-runtime.js +5 -0
  67. package/dist/shortcuts/contract/host.js +34 -59
  68. package/dist/shortcuts/cowork/continuation.js +70 -0
  69. package/dist/shortcuts/cowork/errors.js +59 -0
  70. package/dist/shortcuts/cowork/host.js +180 -0
  71. package/dist/shortcuts/cowork/index.js +166 -0
  72. package/dist/shortcuts/cowork/manifest.js +65 -0
  73. package/dist/shortcuts/cowork/operations/discuss-write.js +277 -0
  74. package/dist/shortcuts/cowork/operations/item-write.js +380 -0
  75. package/dist/shortcuts/cowork/operations/member-write.js +283 -0
  76. package/dist/shortcuts/cowork/operations/permission-write.js +286 -0
  77. package/dist/shortcuts/cowork/operations/read.js +423 -0
  78. package/dist/shortcuts/cowork/operations/registry.js +39 -0
  79. package/dist/shortcuts/cowork/operations/review-write.js +157 -0
  80. package/dist/shortcuts/cowork/operations/shared.js +215 -0
  81. package/dist/shortcuts/cowork/operations/types.js +39 -0
  82. package/dist/shortcuts/cowork/operations.js +19 -0
  83. package/dist/shortcuts/ehr/host.js +4 -1
  84. package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +757 -0
  85. package/dist/shortcuts/ehr/operations/recruit-position.js +367 -0
  86. package/dist/shortcuts/ehr/operations/recruit-stats.js +133 -0
  87. package/dist/shortcuts/ehr/operations/recruit-write.js +43 -16
  88. package/dist/shortcuts/ehr/operations/recruit.js +182 -10
  89. package/dist/shortcuts/ehr/operations/registry.js +6 -0
  90. package/dist/shortcuts/fna/operations/expense-chart.js +19 -7
  91. package/dist/shortcuts/im/diagnostics.js +97 -0
  92. package/dist/shortcuts/im/host.js +58 -31
  93. package/dist/shortcuts/im/index.js +7 -2
  94. package/dist/shortcuts/im/operations/at-mention.js +127 -0
  95. package/dist/shortcuts/im/operations/file-user.js +10 -2
  96. package/dist/shortcuts/im/operations/group-read.js +26 -5
  97. package/dist/shortcuts/im/operations/group-write.js +35 -28
  98. package/dist/shortcuts/im/operations/msg-sync.js +7 -2
  99. package/dist/shortcuts/im/operations/msg-write.js +76 -10
  100. package/dist/shortcuts/im/operations/quote-mention.js +119 -0
  101. package/dist/shortcuts/im/operations/session.js +1 -1
  102. package/dist/shortcuts/im/operations/shared.js +10 -0
  103. package/dist/shortcuts/im/operations/types.js +13 -0
  104. package/dist/shortcuts/index.js +10 -0
  105. package/dist/shortcuts/mail/host.js +12 -1
  106. package/dist/shortcuts/mail/operations/types.js +17 -2
  107. package/dist/shortcuts/meeting/host.js +27 -34
  108. package/dist/shortcuts/meeting/index.js +79 -1
  109. package/dist/shortcuts/meeting/manifest.js +10 -0
  110. package/dist/shortcuts/meeting/operations/create.js +4 -1
  111. package/dist/shortcuts/meeting/operations/eb/catalog.js +10 -0
  112. package/dist/shortcuts/meeting/operations/eb/conflict.js +84 -8
  113. package/dist/shortcuts/meeting/operations/eb/create-flow.js +1195 -140
  114. package/dist/shortcuts/meeting/operations/eb/factory.js +170 -8
  115. package/dist/shortcuts/meeting/operations/eb/index.js +16 -3
  116. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +50 -5
  117. package/dist/shortcuts/meeting/operations/eb-fallback.js +163 -0
  118. package/dist/shortcuts/meeting/operations/env.js +99 -46
  119. package/dist/shortcuts/meeting/operations/form-fields.js +110 -0
  120. package/dist/shortcuts/meeting/operations/invitation.js +79 -20
  121. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  122. package/dist/shortcuts/meeting/operations/standard-guard.js +71 -0
  123. package/dist/shortcuts/meeting/operations.js +16 -0
  124. package/dist/shortcuts/workflow/endpoints.js +8 -3
  125. package/dist/shortcuts/workflow/index.js +110 -1
  126. package/dist/shortcuts/workflow/operations/form.js +204 -36
  127. package/dist/shortcuts/workflow/operations/read.js +104 -23
  128. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +448 -0
  129. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupStore.js +106 -0
  130. package/dist/shortcuts/workflow/operations/todoSmartConvert/businessRules.js +47 -0
  131. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +30 -23
  132. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +1 -0
  133. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +1 -0
  134. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +2 -1
  135. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +2 -4
  136. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +138 -181
  137. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +12 -7
  138. package/dist/shortcuts/workflow/operations.js +23 -1
  139. package/docs/e10-auth.md +1 -1
  140. package/docs/ehr.md +1 -1
  141. package/docs/fna.md +2 -1
  142. package/docs/im.md +8 -1
  143. package/docs/todo-smart-convert/README.md +1 -1
  144. package/docs/todo-smart-convert/data-contract.md +2 -2
  145. package/docs/todo-smart-convert/subgroup-rules.md +6 -0
  146. package/package.json +1 -1
  147. package/skill-template/business-info.json +18 -0
  148. package/skill-template/domains/cowork.md +14 -0
  149. package/skill-template/domains/yimiaoban.md +3 -3
  150. package/skill-template/skill-template.md +1 -1
  151. package/skills/weaver-e10-blog/SKILL.md +5 -5
  152. package/skills/weaver-e10-caizhilian/SKILL.md +5 -5
  153. package/skills/weaver-e10-calendar/SKILL.md +5 -5
  154. package/skills/weaver-e10-cowork/SKILL.md +101 -0
  155. package/skills/weaver-e10-cowork/references/cowork-read.md +90 -0
  156. package/skills/weaver-e10-cowork/references/cowork-write.md +131 -0
  157. package/skills/weaver-e10-cowork/references/safety-boundaries.md +45 -0
  158. package/skills/weaver-e10-esb/SKILL.md +5 -5
  159. package/skills/weaver-e10-hrm/SKILL.md +5 -5
  160. package/skills/weaver-e10-jinchengda/SKILL.md +2 -2
  161. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -5
  162. package/skills/weaver-e10-jucailin/SKILL.md +38 -14
  163. package/skills/weaver-e10-jucailin/references/recruit-create.md +104 -0
  164. package/skills/weaver-e10-jucailin/references/recruit-read.md +25 -0
  165. package/skills/weaver-e10-jucailin/references/recruit-write.md +13 -0
  166. package/skills/weaver-e10-jucailin/references/salary.md +15 -11
  167. package/skills/weaver-e10-mail/SKILL.md +17 -14
  168. package/skills/weaver-e10-meeting/SKILL.md +442 -46
  169. package/skills/weaver-e10-meeting/references/meeting-eb.md +135 -3
  170. package/skills/weaver-e10-meeting/references/meeting-invitation.md +28 -2
  171. package/skills/weaver-e10-meeting/references/meeting-queries.md +9 -6
  172. package/skills/weaver-e10-meeting/references/meeting-verification.md +8 -6
  173. package/skills/weaver-e10-meeting.zip +0 -0
  174. package/skills/weaver-e10-okr/SKILL.md +5 -5
  175. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  176. package/skills/weaver-e10-plan/SKILL.md +5 -5
  177. package/skills/weaver-e10-qiyecheng/SKILL.md +5 -2
  178. package/skills/weaver-e10-qiyecheng/references/catalog.md +2 -2
  179. package/skills/weaver-e10-qiyecheng/references/expense-chart.md +15 -1
  180. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +22 -21
  181. package/skills/weaver-e10-shared/SKILL.md +12 -12
  182. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -4
  183. package/skills/weaver-e10-wenshuding/SKILL.md +5 -5
  184. package/skills/weaver-e10-workflow/SKILL.md +29 -5
  185. package/skills/weaver-e10-workflow/aigroup/default.json +88 -0
  186. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +66 -14
  187. package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +1 -1
  188. package/skills/weaver-e10-workflow/references/workflow-memory.md +1 -1
  189. package/skills/weaver-e10-workflow/references/workflow-query.md +12 -10
  190. package/skills/weaver-e10-workflow/references/workflow-view.md +2 -2
  191. package/skills/weaver-e10-workflow/tools/todo-serve.cjs +239 -0
  192. package/skills/weaver-e10-workflow/{scripts → tools}/todo-summary.cjs +218 -28
  193. package/skills/weaver-e10-yepiaotong/SKILL.md +10 -8
  194. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +7 -7
  195. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +10 -10
  196. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +40 -37
  197. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +4 -4
  198. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +27 -38
  199. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +4 -4
  200. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +37 -29
  201. package/skills/weaver-e10-yimiaoban/SKILL.md +156 -33
  202. package/skills/weaver-e10-yimiaoban/references/ding-write.md +1 -1
  203. package/skills/weaver-e10-yimiaoban/references/error-codes.md +5 -1
  204. package/skills/weaver-e10-yimiaoban/references/group-read.md +5 -1
  205. package/skills/weaver-e10-yimiaoban/references/group-write.md +6 -4
  206. package/skills/weaver-e10-yimiaoban/references/msg-read.md +1 -0
  207. package/skills/weaver-e10-yimiaoban/references/msg-write.md +34 -2
  208. package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +5 -2
  209. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +79 -58
  210. package/skills/weaver-e10-yimiaoban/references/user-remind.md +2 -2
  211. package/skills/weaver-e10-ziguanjia/SKILL.md +5 -5
@@ -4,11 +4,11 @@ import {
4
4
  E10ApiError,
5
5
  createE10RequestRuntime,
6
6
  requireE10Session
7
- } from "./chunk-DW6AUUKS.js";
7
+ } from "./chunk-JUXVYKII.js";
8
8
  import {
9
9
  E10_SESSION_EXPIRED_MESSAGE,
10
10
  getActiveProfile
11
- } from "./chunk-2GA5ZIQL.js";
11
+ } from "./chunk-JNG42JKG.js";
12
12
  import {
13
13
  CliError,
14
14
  isCliError,
@@ -17,7 +17,7 @@ import {
17
17
  import "./chunk-BUSC6UAE.js";
18
18
 
19
19
  // src/shortcuts/ehr/index.ts
20
- import { readFile as readFile2 } from "node:fs/promises";
20
+ import { readFile as readFile3 } from "node:fs/promises";
21
21
 
22
22
  // src/shortcuts/ehr/continuation.ts
23
23
  import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
@@ -221,7 +221,7 @@ function mapRuntimeNetworkError(error) {
221
221
  function createEhrHost(ctx, dependencies = {}) {
222
222
  let e10Session = null;
223
223
  let sessionContext = null;
224
- const requireSession = dependencies.requireSession || requireE10Session;
224
+ const requireSession = dependencies.requireSession || ((innerCtx) => requireE10Session(innerCtx, { skipProfileEnrichment: true }));
225
225
  async function ensureSession() {
226
226
  if (!e10Session) {
227
227
  e10Session = await requireSession(ctx);
@@ -2876,8 +2876,24 @@ async function fetchListRows(host, objId, listId, body) {
2876
2876
  const hasDisplay = Array.isArray(displayRows) && displayRows.length > 0;
2877
2877
  const rows = hasDisplay ? displayRows : rawRows;
2878
2878
  const countData = countResponse ? normalizePaged(countResponse).data : null;
2879
- const total = countData?.total !== void 0 ? countData.total : rows.length;
2880
- return { rows, displayRows: hasDisplay ? displayRows : null, total };
2879
+ const pageSize = typeof body.pageSize === "number" ? body.pageSize : typeof body.pageSize === "string" ? Number(body.pageSize) || rows.length : rows.length;
2880
+ const countUsable = countData !== null && typeof countData.total === "number";
2881
+ let total;
2882
+ let totalExact;
2883
+ let totalNote;
2884
+ if (countUsable) {
2885
+ total = countData.total;
2886
+ totalExact = true;
2887
+ } else if (rows.length < pageSize) {
2888
+ total = rows.length;
2889
+ totalExact = true;
2890
+ 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";
2891
+ } else {
2892
+ total = null;
2893
+ totalExact = false;
2894
+ 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";
2895
+ }
2896
+ return { rows, displayRows: hasDisplay ? displayRows : null, total, totalExact, totalNote };
2881
2897
  }
2882
2898
  var contextResolve = {
2883
2899
  name: "ehr.recruit.context.resolve",
@@ -2942,7 +2958,7 @@ var candidateSearch = {
2942
2958
  pageSize: toNumber(args.pageSize, "pageSize") ?? 10,
2943
2959
  conditions
2944
2960
  });
2945
- const { rows, displayRows, total } = await fetchListRows(env.host, talentObjId, talentList.listId, body);
2961
+ const { rows, displayRows, total, totalExact, totalNote } = await fetchListRows(env.host, talentObjId, talentList.listId, body);
2946
2962
  let creatorNameMap = {};
2947
2963
  if (!displayRows) {
2948
2964
  const pureIds = [...new Set(rows.map((row) => row?.creator).filter(isPlainId).map((value) => String(value).trim()))];
@@ -2987,7 +3003,7 @@ var candidateSearch = {
2987
3003
  createTime: row?.create_time || null
2988
3004
  };
2989
3005
  });
2990
- return ehrSuccess("ehr.recruit.candidate.search", { total, rows: dataRows });
3006
+ return ehrSuccess("ehr.recruit.candidate.search", { total, totalExact, ...totalNote ? { totalNote } : {}, rows: dataRows });
2991
3007
  }
2992
3008
  };
2993
3009
  var candidateList = {
@@ -3032,7 +3048,7 @@ var candidateList = {
3032
3048
  current: toPositiveInt(args.current, "current") ?? 1,
3033
3049
  pageSize: toNumber(args.pageSize, "pageSize") ?? 10
3034
3050
  });
3035
- const { rows, displayRows, total } = await fetchListRows(env.host, formObjId, listParam.listId, body);
3051
+ const { rows, displayRows, total, totalExact, totalNote } = await fetchListRows(env.host, formObjId, listParam.listId, body);
3036
3052
  const outRows = rows.map((row) => {
3037
3053
  const cleaned = { form_data_id: row?.form_data_id || row?.id };
3038
3054
  for (const [key, value] of Object.entries(row || {})) {
@@ -3061,12 +3077,148 @@ var candidateList = {
3061
3077
  return ehrSuccess("ehr.recruit.candidate.list", {
3062
3078
  type,
3063
3079
  total,
3080
+ totalExact,
3081
+ ...totalNote ? { totalNote } : {},
3064
3082
  current: body.current,
3065
3083
  pageSize: body.pageSize,
3066
3084
  rows: outRows
3067
3085
  });
3068
3086
  }
3069
3087
  };
3088
+ function personNames(value) {
3089
+ const text = String(value ?? "").trim();
3090
+ if (!text) return "";
3091
+ if (text.startsWith("[")) {
3092
+ try {
3093
+ const arr = JSON.parse(text);
3094
+ if (Array.isArray(arr)) {
3095
+ const names = arr.map((item) => typeof item === "string" ? item : item?.name ?? item?.username ?? "").filter(Boolean);
3096
+ if (names.length) return names.join("\u3001");
3097
+ }
3098
+ } catch {
3099
+ }
3100
+ }
3101
+ return text;
3102
+ }
3103
+ function nameLike(value) {
3104
+ const text = String(value ?? "").trim();
3105
+ if (!text || /^\d{16,}$/.test(text)) return null;
3106
+ return text;
3107
+ }
3108
+ async function resolveCurrentUserName(host) {
3109
+ try {
3110
+ const session = await host.getSession();
3111
+ const baseUrl = String(session?.baseUrl || "");
3112
+ if (!baseUrl) return "";
3113
+ const resp = await fetch(
3114
+ `${baseUrl}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(baseUrl)}`,
3115
+ {
3116
+ method: "POST",
3117
+ headers: {
3118
+ accept: "application/json",
3119
+ "content-type": "application/json",
3120
+ eteamsid: String(session?.eteamsId || ""),
3121
+ cookie: typeof session?.cookieHeader === "function" ? session.cookieHeader() : "",
3122
+ "user-agent": typeof session?.uaHeader === "function" ? session.uaHeader() : "weaver-work-cli",
3123
+ origin: baseUrl
3124
+ },
3125
+ redirect: "manual"
3126
+ }
3127
+ );
3128
+ const data = await resp.json().catch(() => ({}));
3129
+ const cu = data.currentUser ?? data.data?.currentUser ?? null;
3130
+ return cu ? String(cu.username ?? cu.id ?? "") : "";
3131
+ } catch {
3132
+ return "";
3133
+ }
3134
+ }
3135
+ var interviewFeedbackTodo = {
3136
+ name: "ehr.recruit.interview.feedbackTodo",
3137
+ resource: "recruit",
3138
+ method: "interview.feedbackTodo",
3139
+ action: "ehr.recruit.interview.feedbackTodo",
3140
+ 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",
3141
+ capability: "recruit",
3142
+ risk: READ,
3143
+ inputSchema: schemaOf({
3144
+ maxPages: { type: "integer", minimum: 1, maximum: 50, description: "\u6700\u591A\u7FFB\u9875\u6570\uFF08\u9ED8\u8BA4 5\uFF09" },
3145
+ 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" }
3146
+ }),
3147
+ handler: async (args, env) => {
3148
+ if (!env.recruitContext) throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
3149
+ const cache = await env.recruitContext.load(env.host);
3150
+ const listParam = cache.lists.interview;
3151
+ const form = cache.forms.interview_plan;
3152
+ if (!listParam?.listId || !form?.objId) {
3153
+ throw new RecruitContextError(
3154
+ "recruit_list_unavailable",
3155
+ "\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"
3156
+ );
3157
+ }
3158
+ const pageSize = toNumber(args.pageSize, "pageSize") ?? 200;
3159
+ const maxPages = toNumber(args.maxPages, "maxPages") ?? 5;
3160
+ const me = await resolveCurrentUserName(env.host);
3161
+ const fields = cache.fields[form.objId] || {};
3162
+ const fieldOpts = cache.field_options[form.objId] || {};
3163
+ const statusOpts = fieldOpts[fields.union_status] || {};
3164
+ const typeOpts = fieldOpts[fields.interview_type] || {};
3165
+ const toCn = (map, raw) => {
3166
+ const text = String(raw ?? "");
3167
+ return map[text] ?? text;
3168
+ };
3169
+ const statusOf = (row) => toCn(statusOpts, row?.union_status_key ?? row?.union_status);
3170
+ const typeOf = (row) => toCn(typeOpts, row?.interview_type_key ?? row?.interview_type);
3171
+ const interviewerOf = (row) => personNames(row?.interviewer);
3172
+ const scanned = [];
3173
+ let lastPageCount = 0;
3174
+ for (let i = 0; i < maxPages; i++) {
3175
+ const body = buildListBody({
3176
+ listId: listParam.listId,
3177
+ cusMenuId: listParam.cusMenuId,
3178
+ menuName: listParam.menuName || "\u9762\u8BD5\u7BA1\u7406",
3179
+ title: "\u9762\u8BD5\u7BA1\u7406",
3180
+ current: i + 1,
3181
+ pageSize
3182
+ });
3183
+ const page = await fetchListRows(env.host, form.objId, listParam.listId, body);
3184
+ const use = page.displayRows && page.displayRows.length ? page.displayRows : page.rows;
3185
+ scanned.push(...use);
3186
+ lastPageCount = use.length;
3187
+ if (use.length < pageSize) break;
3188
+ }
3189
+ const pending = scanned.filter((row) => /待反馈/.test(statusOf(row)));
3190
+ const mineAll = me ? pending.filter((row) => interviewerOf(row).includes(me)) : [];
3191
+ const mineAi = mineAll.filter((row) => /AI\s*面试/i.test(typeOf(row)));
3192
+ const slim = (row) => ({
3193
+ form_data_id: row?.form_data_id ?? row?.id ?? null,
3194
+ candidate_name: nameLike(row?.candidate_name) ?? nameLike(row?.talent_id) ?? nameLike(row?.name),
3195
+ position_name: nameLike(row?.position_name) ?? nameLike(row?.position_manage_id),
3196
+ start_time: row?.start_time ?? null,
3197
+ interview_type_key: typeOf(row) || null,
3198
+ interview_session: row?.interview_session ?? null,
3199
+ interviewer: interviewerOf(row) || null,
3200
+ union_status_key: statusOf(row) || null,
3201
+ subject: row?.subject ?? null,
3202
+ talent_id: row?.talent_id ?? null,
3203
+ position_manage_id: row?.position_manage_id ?? null
3204
+ });
3205
+ const notes = [];
3206
+ 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");
3207
+ if (lastPageCount >= pageSize && scanned.length >= pageSize * maxPages) {
3208
+ 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`);
3209
+ }
3210
+ return ehrSuccess("ehr.recruit.interview.feedbackTodo", {
3211
+ currentUser: me || null,
3212
+ scanned: scanned.length,
3213
+ myAiPendingFeedback: { count: mineAi.length, rows: mineAi.map(slim) },
3214
+ myOtherPendingFeedback: {
3215
+ count: mineAll.length - mineAi.length,
3216
+ rows: mineAll.filter((row) => !/AI\s*面试/i.test(typeOf(row))).map(slim)
3217
+ },
3218
+ ...notes.length ? { note: notes.join("\uFF1B") } : {}
3219
+ });
3220
+ }
3221
+ };
3070
3222
  var candidateCard = {
3071
3223
  name: "ehr.recruit.candidate.card",
3072
3224
  resource: "recruit",
@@ -3181,7 +3333,8 @@ var recruitOperations = [
3181
3333
  contextResolve,
3182
3334
  candidateSearch,
3183
3335
  candidateList,
3184
- candidateCard
3336
+ candidateCard,
3337
+ interviewFeedbackTodo
3185
3338
  ];
3186
3339
 
3187
3340
  // src/shortcuts/ehr/operations/recruit-write.ts
@@ -3212,6 +3365,24 @@ async function requireRecruitCache(env) {
3212
3365
  }
3213
3366
  return env.recruitContext.load(env.host);
3214
3367
  }
3368
+ function recruitTalentCardUrl(cache, baseUrl, talentId) {
3369
+ if (!cache.ebdDetailPageId || !talentId) return null;
3370
+ const extra = String(cache.selfUse) === "1" ? "&recruitType=useSelf" : "";
3371
+ return `${baseUrl.replace(/\/+$/, "")}/sp/ebdpage/view/${cache.ebdDetailPageId}?talentId=${talentId}${extra}`;
3372
+ }
3373
+ function recruitSlideLocateUrl(cache, baseUrl, listKey, formKey, dataId) {
3374
+ const listId = cache.lists?.[listKey]?.listId;
3375
+ const objId = cache.forms?.[formKey]?.objId;
3376
+ if (!listId || !objId || !dataId || !cache.appId) return null;
3377
+ const base = baseUrl.replace(/\/+$/, "");
3378
+ return `${base}/sp/ebdfpage/list/${listId}/card/0/${objId}/${dataId}/${listId}?id=${dataId}&openSource=field&openSourceId=${dataId}&openSourceAppId=${cache.appId}`;
3379
+ }
3380
+ function recruitOpenListUrl(cache, baseUrl, listKey, talentId) {
3381
+ const menu = cache.lists?.[listKey];
3382
+ if (!menu?.listId) return null;
3383
+ const extra = talentId ? `?talentId=${talentId}${String(cache.selfUse) === "1" ? "&recruitType=useSelf" : ""}` : "";
3384
+ return `${baseUrl.replace(/\/+$/, "")}/sp/ebdfpage/list/${menu.listId}${extra}`;
3385
+ }
3215
3386
  async function checkDuplicateByMobile(env, cache, mobile) {
3216
3387
  const talent = cache.lists.talent;
3217
3388
  const objId = cache.forms.talent_pool?.objId;
@@ -3364,12 +3535,6 @@ async function resolvePersonId(host, fieldId, keyword) {
3364
3535
  const first = list[0];
3365
3536
  return first ? String(first.id || first.value || "") || null : null;
3366
3537
  }
3367
- async function getInterviewPortalFieldIds(host) {
3368
- const response = await host.request({
3369
- path: "/api/recruit/candidate/getFieldIdsForInterview"
3370
- });
3371
- return response?.data || {};
3372
- }
3373
3538
  async function saveFormData(host, params) {
3374
3539
  const layoutId = params.layoutId || params.formId;
3375
3540
  const customParam = {
@@ -3682,7 +3847,9 @@ var candidateCreateApply = {
3682
3847
  status: "COMPLETE",
3683
3848
  talentId: newId,
3684
3849
  initTalentBatch: initMsg,
3685
- searchHint: `\u53EF\u7528 ehr.recruit.candidate.search \u6309 name=${name} \u6216 mobile=${mobile} \u56DE\u67E5`
3850
+ searchHint: `\u53EF\u7528 ehr.recruit.candidate.search \u6309 name=${name} \u6216 mobile=${mobile} \u56DE\u67E5`,
3851
+ talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, newId),
3852
+ talentListLocateUrl: recruitOpenListUrl(cache, lease.baseUrl, "talent", newId)
3686
3853
  });
3687
3854
  }
3688
3855
  };
@@ -3730,11 +3897,10 @@ async function buildInterviewPlan(env, cache, args) {
3730
3897
  const objId = cacheObjId(cache, "interview_plan", "\u9762\u8BD5\u8BA1\u5212\u8868\u5355");
3731
3898
  const formId = cacheFormId(cache, "interview_plan");
3732
3899
  const fields = cache.fields[objId] || {};
3733
- const portalFields = await getInterviewPortalFieldIds(host);
3734
- const fieldIdOf = (key) => fields[key] || portalFields[key] || "";
3900
+ const fieldIdOf = (key) => fields[key] || "";
3735
3901
  for (const key of INTERVIEW_DATAKEYS) {
3736
3902
  if (!fieldIdOf(key)) {
3737
- throw ehrValidationError("recruit_field_missing", `\u9762\u8BD5\u8BA1\u5212\u8868\u5355\u7F3A\u5C11\u5B57\u6BB5 ${key}\uFF08ds/fields \u4E0E\u95E8\u6237\u5B57\u6BB5\u5747\u89E3\u6790\u4E0D\u5230\uFF09`);
3903
+ throw ehrValidationError("recruit_field_missing", `\u9762\u8BD5\u8BA1\u5212\u8868\u5355\u7F3A\u5C11\u5B57\u6BB5 ${key}\uFF08\u73AF\u5883\u7F13\u5B58 ds/fields \u672A\u89E3\u6790\u5230\uFF0C\u8BF7\u91CD\u5EFA\u73AF\u5883\u7F13\u5B58\u540E\u91CD\u8BD5\uFF09`);
3738
3904
  }
3739
3905
  }
3740
3906
  const typeMap = await getDsOptions(host, objId, fieldIdOf("interview_type"), cache.appId);
@@ -3907,7 +4073,9 @@ var interviewArrangeApply = {
3907
4073
  assigned: assigned?.data || {},
3908
4074
  candidate: `${plan.candidateName}\uFF08talentId=${args.talentId}\uFF09`,
3909
4075
  time: plan.interviewTime,
3910
- interviewer: plan.interviewer
4076
+ interviewer: plan.interviewer,
4077
+ talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, args.talentId),
4078
+ interviewLocateUrl: recruitSlideLocateUrl(cache, lease.baseUrl, "interview", "interview_plan", planFormId)
3911
4079
  });
3912
4080
  }
3913
4081
  };
@@ -4158,7 +4326,9 @@ var offerSendApply = {
4158
4326
  validateOffer: "\u901A\u8FC7",
4159
4327
  sendOffer: "SUCCESS",
4160
4328
  runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg },
4161
- nextStep: "\u56DE\u67E5 offer_status_select \u5E94\u5DF2\u53D8\u66F4\u4E3A\u300C\u5DF2\u53D1\u9001\u5F85\u53CD\u9988\u300D"
4329
+ nextStep: "\u56DE\u67E5 offer_status_select \u5E94\u5DF2\u53D8\u66F4\u4E3A\u300C\u5DF2\u53D1\u9001\u5F85\u53CD\u9988\u300D",
4330
+ talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, payload.talentId),
4331
+ offerListUrl: recruitOpenListUrl(cache, lease.baseUrl, "offer")
4162
4332
  });
4163
4333
  }
4164
4334
  };
@@ -4437,7 +4607,8 @@ var entryProcessApply = {
4437
4607
  saveFormData: "\u5DF2\u66F4\u65B0",
4438
4608
  saveEstPreEntry: "\u5DF2\u843D\u8D26\u53F7",
4439
4609
  runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg },
4440
- nextStep: "\u56DE\u67E5\u5165\u804C\u5217\u8868 transact_status \u5E94\u7531 1 \u53D8\u4E3A 2"
4610
+ nextStep: "\u56DE\u67E5\u5165\u804C\u5217\u8868 transact_status \u5E94\u7531 1 \u53D8\u4E3A 2",
4611
+ entryListUrl: recruitOpenListUrl(cache, lease.baseUrl, "entry")
4441
4612
  });
4442
4613
  }
4443
4614
  };
@@ -4666,7 +4837,8 @@ var feedbackEditApply = {
4666
4837
  feedbackId: payload.feedbackId,
4667
4838
  saved: Boolean(savedId),
4668
4839
  verify: backFeedback ? { result: backFeedback.result, comment: backFeedback.judgement, changed } : "\u56DE\u67E5\u672A\u53D6\u5230\u8BE5\u53CD\u9988",
4669
- ...changed ? {} : { warning: "\u56DE\u67E5\u672A\u786E\u8BA4\u53D8\u66F4\u751F\u6548\uFF0C\u8BF7\u4EBA\u5DE5\u5728 E10 \u4E2D\u6838\u5BF9\u9762\u8BD5\u53CD\u9988\uFF0C\u7981\u6B62\u81EA\u52A8\u91CD\u8BD5" }
4840
+ ...changed ? {} : { warning: "\u56DE\u67E5\u672A\u786E\u8BA4\u53D8\u66F4\u751F\u6548\uFF0C\u8BF7\u4EBA\u5DE5\u5728 E10 \u4E2D\u6838\u5BF9\u9762\u8BD5\u53CD\u9988\uFF0C\u7981\u6B62\u81EA\u52A8\u91CD\u8BD5" },
4841
+ talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, payload.talentId)
4670
4842
  });
4671
4843
  }
4672
4844
  };
@@ -4785,6 +4957,7 @@ var feedbackUrgeApply = {
4785
4957
  requireConfirm2(args, "ehr.recruit.candidate.interview.feedback.urge.apply");
4786
4958
  const lease = await verifyLease2(args, "ehr.recruit.candidate.interview.feedback.urge.apply", env);
4787
4959
  const payload = lease.payload || {};
4960
+ const cache = await requireRecruitCache(env);
4788
4961
  if (!payload.feedbackRecordId || !payload.esbActionId || !payload.interviewDetailObjId) {
4789
4962
  throw ehrValidationError("continuation_payload_invalid", "continuation \u7F3A\u5C11\u50AC\u8BC4\u53C2\u6570\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C prepare");
4790
4963
  }
@@ -4803,7 +4976,8 @@ var feedbackUrgeApply = {
4803
4976
  return ehrSuccess("ehr.recruit.candidate.interview.feedback.urge.apply", {
4804
4977
  status: "COMPLETE",
4805
4978
  feedbackRecordId: payload.feedbackRecordId,
4806
- runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg }
4979
+ runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg },
4980
+ talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, payload.talentId)
4807
4981
  });
4808
4982
  }
4809
4983
  };
@@ -4822,6 +4996,1146 @@ var recruitWriteOperations = [
4822
4996
  feedbackUrgeApply
4823
4997
  ];
4824
4998
 
4999
+ // src/shortcuts/ehr/operations/recruit-stats.ts
5000
+ var STATS_MODULES = {
5001
+ talent: { listKey: "talent", formType: "talent_pool", title: "\u4EBA\u624D\u5E93" },
5002
+ demand: { listKey: "demand", formType: "demand_manage", title: "\u62DB\u8058\u9700\u6C42" },
5003
+ position: { listKey: "position", formType: "position_manage", title: "\u62DB\u8058\u804C\u4F4D" },
5004
+ entry: { listKey: "entry", formType: "entry_manage", title: "\u5165\u804C\u7BA1\u7406" },
5005
+ offer: { listKey: "offer", formType: "offer", title: "\u5F55\u7528\u7BA1\u7406" },
5006
+ interview: {
5007
+ listKey: "interview",
5008
+ formType: "interview_plan",
5009
+ title: "\u9762\u8BD5\u5B89\u6392",
5010
+ note: "\u9762\u8BD5\u7BA1\u7406\u5217\u8868\u53EF\u80FD\u6309\u9875\u9762\u914D\u7F6E\u53EA\u663E\u793A\u8FD1\u671F\u8BB0\u5F55\uFF0C\u8BA1\u6570\u4EC5\u4F9B\u91CF\u7EA7\u53C2\u8003"
5011
+ }
5012
+ };
5013
+ var DEFAULT_MODULES = ["talent", "demand", "position", "entry", "offer"];
5014
+ async function countModule(env, cache, key, pageSize, maxPages) {
5015
+ const cfg = STATS_MODULES[key];
5016
+ const list = cache.lists[cfg.listKey];
5017
+ const objId = cache.forms[cfg.formType]?.objId;
5018
+ if (!list?.listId || !objId) {
5019
+ return {
5020
+ key,
5021
+ module: cfg.title,
5022
+ available: false,
5023
+ total: null,
5024
+ totalExact: false,
5025
+ scannedRows: 0,
5026
+ note: "\u5F53\u524D\u73AF\u5883\u672A\u542F\u7528\u8BE5\u529F\u80FD\u83DC\u5355\uFF08\u672A\u627E\u5230\u5217\u8868\u6216\u8868\u5355\uFF09\uFF0C\u5DF2\u8DF3\u8FC7"
5027
+ };
5028
+ }
5029
+ const baseBody = buildListBody({
5030
+ listId: list.listId,
5031
+ cusMenuId: list.cusMenuId,
5032
+ menuName: list.menuName || cfg.title,
5033
+ title: cfg.title,
5034
+ pageSize
5035
+ });
5036
+ let scanned = 0;
5037
+ let total = null;
5038
+ let totalExact = false;
5039
+ let note = cfg.note;
5040
+ for (let current = 1; current <= maxPages; current++) {
5041
+ const page = await fetchListRows(env.host, objId, list.listId, { ...baseBody, current, pageSize });
5042
+ if (current === 1 && page.total !== null && page.totalExact) {
5043
+ total = page.total;
5044
+ totalExact = true;
5045
+ break;
5046
+ }
5047
+ scanned += page.rows.length;
5048
+ if (page.rows.length < pageSize) {
5049
+ total = scanned;
5050
+ totalExact = true;
5051
+ break;
5052
+ }
5053
+ }
5054
+ if (total === null) {
5055
+ totalExact = false;
5056
+ note = `\u8BA1\u6570\u63A5\u53E3\u4E0D\u53EF\u7528\u4E14\u8FBE\u626B\u63CF\u4E0A\u9650\uFF08${maxPages} \u9875 \xD7 ${pageSize} \u884C\uFF09\u4ECD\u672A\u53D6\u5B8C\uFF0C\u603B\u6570\u4E0D\u53EF\u77E5\uFF1B\u53EF\u8C03\u5927 pageSize/maxPages \u91CD\u8BD5`;
5057
+ }
5058
+ return { key, module: cfg.title, available: true, total, totalExact, scannedRows: scanned, ...note ? { note } : {} };
5059
+ }
5060
+ var recruitStats = {
5061
+ name: "ehr.recruit.stats",
5062
+ resource: "recruit",
5063
+ method: "stats",
5064
+ action: "ehr.recruit.stats",
5065
+ summary: '\u62DB\u8058\u5DE5\u4F5C\u7EDF\u8BA1\uFF1A\u6309\u6A21\u5757\u7EDF\u8BA1\u8BB0\u5F55\u6570\uFF08\u4EBA\u624D\u5E93/\u62DB\u8058\u9700\u6C42/\u62DB\u8058\u804C\u4F4D/\u5165\u804C\u7BA1\u7406/\u5F55\u7528\u7BA1\u7406/\u9762\u8BD5\u5B89\u6392\uFF09\uFF0C\u8BA1\u6570\u4E0D\u53EF\u7528\u65F6\u5982\u5B9E\u6807"\u603B\u6570\u4E0D\u53EF\u77E5"',
5066
+ capability: "recruit",
5067
+ risk: READ,
5068
+ inputSchema: schemaOf({
5069
+ modules: {
5070
+ type: "array",
5071
+ items: { type: "string", enum: Object.keys(STATS_MODULES) },
5072
+ uniqueItems: true,
5073
+ description: `\u8981\u7EDF\u8BA1\u7684\u6A21\u5757\uFF0C\u9ED8\u8BA4 ${DEFAULT_MODULES.join(",")}`
5074
+ },
5075
+ pageSize: { type: "integer", minimum: 1, maximum: 200, description: "\u5355\u9875\u884C\u6570\uFF0C\u9ED8\u8BA4 100\uFF08\u8BA1\u6570\u63A5\u53E3\u4E0D\u53EF\u7528\u65F6\u7528\u4E8E\u5206\u9875\u5B9E\u63A2\uFF09" },
5076
+ maxPages: { type: "integer", minimum: 1, maximum: 20, description: "\u6700\u591A\u7FFB\u9875\u6570\uFF0C\u9ED8\u8BA4 5" }
5077
+ }),
5078
+ handler: async (args, env) => {
5079
+ if (!env.recruitContext) {
5080
+ throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
5081
+ }
5082
+ const requested = args.modules === void 0 ? DEFAULT_MODULES : Array.isArray(args.modules) ? args.modules.map(String) : [];
5083
+ if (!requested.length) {
5084
+ throw ehrValidationError("modules_required", `modules \u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4\uFF0C\u53EF\u9009\u503C\uFF1A${Object.keys(STATS_MODULES).join(" / ")}`);
5085
+ }
5086
+ for (const key of requested) {
5087
+ if (!STATS_MODULES[key]) {
5088
+ throw ehrValidationError("enum_invalid", `modules \u542B\u4E0D\u652F\u6301\u7684\u503C\uFF1A${key}\uFF08\u53EF\u9009 ${Object.keys(STATS_MODULES).join(" / ")}\uFF09`);
5089
+ }
5090
+ }
5091
+ const pageSize = Number.isInteger(args.pageSize) ? Number(args.pageSize) : 100;
5092
+ const maxPages = Number.isInteger(args.maxPages) ? Number(args.maxPages) : 5;
5093
+ const cache = await env.recruitContext.load(env.host);
5094
+ const modules = await Promise.all(
5095
+ requested.map((key) => countModule(env, cache, key, pageSize, maxPages))
5096
+ );
5097
+ const known = modules.filter((m) => m.available && m.total !== null);
5098
+ const unknown = modules.filter((m) => m.available && m.total === null);
5099
+ const unavailable = modules.filter((m) => !m.available);
5100
+ return ehrSuccess("ehr.recruit.stats", {
5101
+ modules,
5102
+ summary: {
5103
+ knownTotal: known.reduce((sum, m) => sum + m.total, 0),
5104
+ knownModules: known.map((m) => m.key),
5105
+ unknownModules: unknown.map((m) => m.key),
5106
+ unavailableModules: unavailable.map((m) => m.key),
5107
+ note: unknown.length ? "\u5B58\u5728\u603B\u6570\u4E0D\u53EF\u77E5\u7684\u6A21\u5757\uFF08\u89C1\u5BF9\u5E94 note\uFF09\uFF0C\u672A\u8BA1\u5165 knownTotal" : void 0
5108
+ }
5109
+ }, { pageSize, maxPages });
5110
+ }
5111
+ };
5112
+ var recruitStatsOperations = [recruitStats];
5113
+
5114
+ // src/shortcuts/ehr/operations/recruit-position.ts
5115
+ import { createHash as createHash4 } from "node:crypto";
5116
+ var TEXT_KEYS = ["position_name"];
5117
+ var MTEXT_KEYS = ["position_duty", "work_request"];
5118
+ var PERSON_DEFAULT_KEYS = ["recr_principal", "employ_manager"];
5119
+ var REF_COPY_KEYS = [
5120
+ "position_nature",
5121
+ "job_category",
5122
+ "education",
5123
+ "work_years",
5124
+ "work_location",
5125
+ "salary_min",
5126
+ "salary_max",
5127
+ "recruit_flow"
5128
+ ];
5129
+ var TEXT_DEFAULTS = { recruit_count: "1", number_of_pay_months: "12" };
5130
+ var OPTION_DEFAULTS = { salary_type: "\u6708\u85AA", recruit_type: "\u793E\u4F1A\u62DB\u8058", status: "\u62DB\u8058\u4E2D" };
5131
+ var SET_FIELDS = [
5132
+ { arg: "education", key: "education", kind: "dict", label: "\u5B66\u5386\u8981\u6C42\uFF08\u6309\u6570\u636E\u5B57\u5178\u540D\u79F0\uFF09" },
5133
+ { arg: "workYears", key: "work_years", kind: "dict", label: "\u5DE5\u4F5C\u5E74\u9650\uFF08\u6309\u6570\u636E\u5B57\u5178\u540D\u79F0\uFF09" },
5134
+ { arg: "jobCategory", key: "job_category", kind: "dict", label: "\u804C\u4F4D\u7C7B\u522B\uFF08\u6309\u6570\u636E\u5B57\u5178\u540D\u79F0\uFF09" },
5135
+ { arg: "positionNature", key: "position_nature", kind: "dict", label: "\u804C\u4F4D\u5C5E\u6027\uFF08\u6309\u6570\u636E\u5B57\u5178\u540D\u79F0\uFF09" },
5136
+ { arg: "salaryType", key: "salary_type", kind: "option", label: "\u85AA\u916C\u7C7B\u578B\uFF08\u6708\u85AA/\u5E74\u85AA\u7B49\uFF09" },
5137
+ { arg: "recruitType", key: "recruit_type", kind: "option", label: "\u62DB\u8058\u65B9\u5F0F\uFF08\u793E\u4F1A\u62DB\u8058/\u6821\u56ED\u62DB\u8058\u7B49\uFF09" },
5138
+ { arg: "recruitCount", key: "recruit_count", kind: "number", label: "\u62DB\u8058\u4EBA\u6570" },
5139
+ { arg: "salaryMin", key: "salary_min", kind: "number", label: "\u85AA\u916C\u4E0B\u9650" },
5140
+ { arg: "salaryMax", key: "salary_max", kind: "number", label: "\u85AA\u916C\u4E0A\u9650" },
5141
+ { arg: "numberOfPayMonths", key: "number_of_pay_months", kind: "number", label: "\u85AA\u916C\u6708\u6570" },
5142
+ { arg: "recruitPrincipal", key: "recr_principal", kind: "person", label: "\u62DB\u8058\u8D1F\u8D23\u4EBA\uFF08\u6309\u59D3\u540D\uFF09" },
5143
+ { arg: "employManager", key: "employ_manager", kind: "person", label: "\u7528\u4EBA\u7ECF\u7406\uFF08\u6309\u59D3\u540D\uFF09" },
5144
+ { arg: "interviewer", key: "interviewer", kind: "person", label: "\u9762\u8BD5\u5B98\uFF08\u6309\u59D3\u540D\uFF09" },
5145
+ { arg: "recruitHelper", key: "recr_helper", kind: "person", label: "\u62DB\u8058\u534F\u52A9\u4EBA\uFF08\u6309\u59D3\u540D\uFF09" }
5146
+ ];
5147
+ var SET_SCHEMA = Object.fromEntries(
5148
+ SET_FIELDS.map((f) => [f.arg, f.kind === "number" ? { type: "string", description: `${f.label}\uFF08\u6570\u5B57\uFF0C\u6309\u5B57\u7B26\u4E32\u4F20\uFF09` } : { type: "string", description: f.label }])
5149
+ );
5150
+ function fingerprint3(value) {
5151
+ return createHash4("sha256").update(JSON.stringify(value ?? null)).digest("hex").slice(0, 32);
5152
+ }
5153
+ function requireConfirm3(args, action) {
5154
+ if (args.confirm !== true) {
5155
+ throw ehrConfirmationError(action, { hint: "\u91CD\u65B0\u8C03\u7528\u65F6\u4F20\u5165 confirm=true \u4E0E prepare \u8FD4\u56DE\u7684 continuation" });
5156
+ }
5157
+ }
5158
+ async function requireCache(env) {
5159
+ if (!env.recruitContext) throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
5160
+ return env.recruitContext.load(env.host);
5161
+ }
5162
+ async function fetchReferenceDetails(env, cache, objId, formId) {
5163
+ const list = cache.lists.position;
5164
+ if (!list?.listId) return [];
5165
+ try {
5166
+ const body = buildListBody({
5167
+ listId: list.listId,
5168
+ cusMenuId: list.cusMenuId,
5169
+ menuName: list.menuName || "\u62DB\u8058\u804C\u4F4D",
5170
+ title: "\u62DB\u8058\u804C\u4F4D",
5171
+ pageSize: 1
5172
+ });
5173
+ const page = await fetchListRows(env.host, objId, list.listId, body);
5174
+ const row = page.rows[0];
5175
+ const recordId = row?.form_data_id || row?.id;
5176
+ if (!recordId) return [];
5177
+ const record = await getFormDataRecord(env.host, formId, String(recordId), objId);
5178
+ return toSubmitDetails(record.dataDetails);
5179
+ } catch {
5180
+ return [];
5181
+ }
5182
+ }
5183
+ async function buildPositionPlan(env, cache, args) {
5184
+ const form = cache.forms.position_manage;
5185
+ const objId = form?.objId;
5186
+ const formId = form?.formId || objId;
5187
+ if (!objId || !formId) {
5188
+ throw ehrValidationError(
5189
+ "position_form_unavailable",
5190
+ "\u5F53\u524D\u73AF\u5883\u672A\u542F\u7528\u300C\u62DB\u8058\u804C\u4F4D\u300D\u529F\u80FD\uFF08\u672A\u89E3\u6790\u5230 position_manage \u8868\u5355\uFF09\uFF0C\u65E0\u6CD5\u5EFA\u804C\u4F4D"
5191
+ );
5192
+ }
5193
+ const fields = cache.fields[objId] || {};
5194
+ const fid = (key) => fields[key];
5195
+ for (const key of [...TEXT_KEYS, ...MTEXT_KEYS]) {
5196
+ if (!fid(key)) {
5197
+ throw ehrValidationError("recruit_field_missing", `\u804C\u4F4D\u7BA1\u7406\u7F3A\u5C11\u5B57\u6BB5 ${key}\uFF08ds/fields \u89E3\u6790\u4E0D\u5230\uFF09\uFF0C\u65E0\u6CD5\u5EFA\u804C\u4F4D`);
5198
+ }
5199
+ }
5200
+ const positionName = String(args.positionName ?? "").trim();
5201
+ if (!positionName) throw ehrValidationError("position_name_required", "positionName \u5FC5\u586B");
5202
+ const data = /* @__PURE__ */ new Map();
5203
+ const put = (entry) => {
5204
+ if (entry && entry.formField?.id) data.set(String(entry.formField.id), entry);
5205
+ };
5206
+ const has = (key) => {
5207
+ const id = fid(key);
5208
+ return !!(id && data.has(String(id)));
5209
+ };
5210
+ put({ formField: { id: fid("position_name") }, content: positionName });
5211
+ const duty = typeof args.duty === "string" ? args.duty : "";
5212
+ const requirement = typeof args.requirement === "string" ? args.requirement : "";
5213
+ if (duty && fid("position_duty")) put({ formField: { id: fid("position_duty") }, dataText: { content: duty } });
5214
+ if (requirement && fid("work_request")) put({ formField: { id: fid("work_request") }, dataText: { content: requirement } });
5215
+ const applied = [];
5216
+ const skipped = [];
5217
+ if (args.demandId !== void 0 && args.demandId !== null && args.demandId !== "") {
5218
+ if (!fid("demand")) throw ehrValidationError("recruit_field_missing", "\u804C\u4F4D\u7BA1\u7406\u7F3A\u5C11\u5B57\u6BB5 demand\uFF0C\u65E0\u6CD5\u5173\u8054\u62DB\u8058\u9700\u6C42");
5219
+ const demandId = String(args.demandId).replace(/[^\d]/g, "");
5220
+ if (!demandId) throw ehrValidationError("demand_id_invalid", "demandId \u5FC5\u987B\u662F\u6570\u5B57 ID");
5221
+ put({
5222
+ formField: { id: fid("demand") },
5223
+ dataOptions: [{ optionId: demandId, content: typeof args.demandName === "string" && args.demandName ? args.demandName : demandId }]
5224
+ });
5225
+ applied.push(`demand=${demandId}`);
5226
+ }
5227
+ for (const field of SET_FIELDS) {
5228
+ const raw = args[field.arg];
5229
+ if (raw === void 0 || raw === null || String(raw).trim() === "") continue;
5230
+ const value = String(raw).trim();
5231
+ const fieldId = fid(field.key);
5232
+ if (!fieldId) {
5233
+ skipped.push(`${field.key}\uFF08\u804C\u4F4D\u8868\u5355\u65E0\u8BE5\u5B57\u6BB5\uFF09`);
5234
+ continue;
5235
+ }
5236
+ if (field.kind === "text" || field.kind === "number") {
5237
+ put({ formField: { id: fieldId }, content: value });
5238
+ applied.push(`${field.key}=${value}`);
5239
+ } else if (field.kind === "option") {
5240
+ const options = await getDsOptions(env.host, objId, fieldId, cache.appId);
5241
+ const optionId = options[value];
5242
+ if (!optionId) {
5243
+ skipped.push(`${field.key}=${value}\uFF08\u9009\u9879\u672A\u5339\u914D\uFF1A${Object.keys(options).slice(0, 8).join("/")}\u2026\uFF09`);
5244
+ continue;
5245
+ }
5246
+ put({ formField: { id: fieldId }, dataOptions: [{ optionId: String(optionId), content: value }] });
5247
+ applied.push(`${field.key}=${value}`);
5248
+ } else if (field.kind === "dict") {
5249
+ const ids = cache.dict.by_name[value] || [];
5250
+ const unique = [...new Set(ids.map(String))];
5251
+ if (unique.length !== 1) {
5252
+ skipped.push(`${field.key}=${value}\uFF08\u5B57\u5178${unique.length === 0 ? "\u65E0\u6B64\u9879" : "\u540C\u540D\u591A\u9879\uFF0C\u6B67\u4E49"}\uFF0C\u672A\u5199\u5165\uFF09`);
5253
+ continue;
5254
+ }
5255
+ put({ formField: { id: fieldId }, dataOptions: [{ optionId: unique[0], content: value }] });
5256
+ applied.push(`${field.key}=${value}`);
5257
+ } else {
5258
+ const personId = await resolvePersonId(env.host, fieldId, value);
5259
+ if (!personId) {
5260
+ skipped.push(`${field.key}=${value}\uFF08\u4EBA\u5458\u672A\u5339\u914D\uFF09`);
5261
+ continue;
5262
+ }
5263
+ put({ formField: { id: fieldId }, dataOptions: [{ optionId: String(personId), content: value }] });
5264
+ applied.push(`${field.key}=${value}`);
5265
+ }
5266
+ }
5267
+ for (const [key, name] of Object.entries(OPTION_DEFAULTS)) {
5268
+ const fieldId = fid(key);
5269
+ if (!fieldId || has(key)) continue;
5270
+ const options = await getDsOptions(env.host, objId, fieldId, cache.appId);
5271
+ const optionId = options[name];
5272
+ if (optionId) put({ formField: { id: fieldId }, dataOptions: [{ optionId: String(optionId), content: name }] });
5273
+ }
5274
+ for (const [key, value] of Object.entries(TEXT_DEFAULTS)) {
5275
+ const fieldId = fid(key);
5276
+ if (fieldId && !has(key)) put({ formField: { id: fieldId }, content: value });
5277
+ }
5278
+ const session = await env.host.getSession();
5279
+ for (const key of PERSON_DEFAULT_KEYS) {
5280
+ const fieldId = fid(key);
5281
+ if (fieldId && !has(key) && session.userId) {
5282
+ put({ formField: { id: fieldId }, dataOptions: [{ optionId: session.userId, content: "\u5F53\u524D\u7528\u6237" }] });
5283
+ }
5284
+ }
5285
+ const refDetails = await fetchReferenceDetails(env, cache, objId, formId);
5286
+ const refMap = /* @__PURE__ */ new Map();
5287
+ for (const detail of refDetails) {
5288
+ const id = detail?.formField?.id;
5289
+ if (id) refMap.set(String(id), detail);
5290
+ }
5291
+ const inherited = [];
5292
+ const missingRef = [];
5293
+ for (const key of REF_COPY_KEYS) {
5294
+ const fieldId = fid(key);
5295
+ if (!fieldId || has(key)) continue;
5296
+ const ref = refMap.get(String(fieldId));
5297
+ if (ref) {
5298
+ put(ref);
5299
+ inherited.push(key);
5300
+ } else {
5301
+ missingRef.push(key);
5302
+ }
5303
+ }
5304
+ const inverse = {};
5305
+ for (const [dataKey, fieldId] of Object.entries(fields)) inverse[String(fieldId)] = dataKey;
5306
+ return {
5307
+ objId,
5308
+ formId,
5309
+ dataDetails: [...data.values()],
5310
+ summary: {
5311
+ positionName,
5312
+ dutyLength: duty.length,
5313
+ requirementLength: requirement.length,
5314
+ fieldCount: data.size,
5315
+ filledFields: [...data.keys()].map((id) => inverse[id] || id).sort(),
5316
+ explicitApplied: applied,
5317
+ explicitSkipped: skipped,
5318
+ inheritedFromExistingPosition: inherited,
5319
+ noReferenceValue: missingRef,
5320
+ principalDefault: session.userId ? "\u5F53\u524D\u767B\u5F55\u7528\u6237" : "\u672A\u89E3\u6790\u5230\u5F53\u524D\u7528\u6237"
5321
+ }
5322
+ };
5323
+ }
5324
+ var positionCreatePrepare = {
5325
+ name: "ehr.recruit.position.create.prepare",
5326
+ resource: "recruit",
5327
+ method: "position.create.prepare",
5328
+ action: "ehr.recruit.position.create.prepare",
5329
+ summary: "\u751F\u6210\u804C\u4F4D\u5E76\u5EFA\u5230\u62DB\u8058\u804C\u4F4D\u5217\u8868\u524D\u7684\u786E\u8BA4\u6458\u8981\uFF1A\u89E3\u6790\u804C\u4F4D\u8868\u5355\u5B57\u6BB5\u3001\u5957\u7528\u9ED8\u8BA4\u503C\uFF08\u72B6\u6001/\u6D41\u7A0B/\u8D1F\u8D23\u4EBA/\u5173\u8054\u5B57\u6BB5\uFF09\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6570\u636E",
5330
+ capability: "recruit",
5331
+ risk: WRITE,
5332
+ requiresConfirmation: true,
5333
+ inputSchema: schemaOf(
5334
+ {
5335
+ positionName: { type: "string", maxLength: 120, description: "\u804C\u4F4D\u540D\u79F0\uFF08\u5FC5\u586B\uFF09" },
5336
+ duty: { type: "string", maxLength: 2e4, description: "\u5C97\u4F4D\u804C\u8D23 / JD \u6B63\u6587\uFF08\u5199\u5165\u804C\u4F4D\u804C\u8D23\u591A\u884C\u6587\u672C\uFF09" },
5337
+ requirement: { type: "string", maxLength: 2e4, description: "\u4EFB\u804C\u8981\u6C42\uFF08\u5199\u5165\u5DE5\u4F5C\u4EFB\u804C\u8981\u6C42\u591A\u884C\u6587\u672C\uFF09" },
5338
+ demandId: { ...SCHEMA_ID, description: "\u5173\u8054\u62DB\u8058\u9700\u6C42 id\uFF08\u53EF\u9009\uFF09" },
5339
+ demandName: { type: "string", maxLength: 200, description: "\u5173\u8054\u9700\u6C42\u540D\u79F0\uFF08\u4EC5\u7528\u4E8E\u6458\u8981\u5C55\u793A\uFF09" },
5340
+ ...SET_SCHEMA
5341
+ },
5342
+ { required: ["positionName"] }
5343
+ ),
5344
+ handler: async (args, env) => {
5345
+ const cache = await requireCache(env);
5346
+ const plan = await buildPositionPlan(env, cache, args);
5347
+ const session = await env.host.getSession();
5348
+ const continuation = await env.continuation.issue(
5349
+ {
5350
+ operation: "ehr.recruit.position.create.apply",
5351
+ baseUrl: session.baseUrl,
5352
+ profile: session.profile,
5353
+ userId: session.userId,
5354
+ payload: { objId: plan.objId, formId: plan.formId, positionName: String(args.positionName).trim(), dataDetails: plan.dataDetails },
5355
+ fingerprint: fingerprint3({ objId: plan.objId, dataDetails: plan.dataDetails })
5356
+ },
5357
+ 10 * 60 * 1e3
5358
+ );
5359
+ return ehrSuccess("ehr.recruit.position.create.prepare", {
5360
+ status: "AWAITING_CONFIRMATION",
5361
+ preview: { action: "\u751F\u6210\u804C\u4F4DJD\u5E76\u5EFA\u804C\u4F4D\uFF08saveFormData \u2192 addPosition\uFF09", ...plan.summary },
5362
+ willSubmit: { objId: plan.objId, formId: plan.formId, dataDetails: plan.dataDetails },
5363
+ continuation,
5364
+ expiresInSeconds: 600
5365
+ });
5366
+ }
5367
+ };
5368
+ var positionCreateApply = {
5369
+ name: "ehr.recruit.position.create.apply",
5370
+ resource: "recruit",
5371
+ method: "position.create.apply",
5372
+ action: "ehr.recruit.position.create.apply",
5373
+ summary: "\u6267\u884C\u5EFA\u804C\u4F4D\uFF08saveFormData \u2192 addPosition\uFF09\uFF1B\u5FC5\u987B\u5148 prepare \u5E76\u7ECF\u7528\u6237\u660E\u786E\u786E\u8BA4",
5374
+ capability: "recruit",
5375
+ risk: WRITE,
5376
+ requiresConfirmation: true,
5377
+ inputSchema: schemaOf(
5378
+ {
5379
+ positionName: { type: "string", maxLength: 120 },
5380
+ confirm: { const: true },
5381
+ continuation: { type: "string", minLength: 1 }
5382
+ },
5383
+ { required: ["positionName", "confirm", "continuation"] }
5384
+ ),
5385
+ handler: async (args, env) => {
5386
+ requireConfirm3(args, "ehr.recruit.position.create.apply");
5387
+ const lease = await env.continuation.verify(args.continuation, "ehr.recruit.position.create.apply");
5388
+ const session = await env.host.getSession();
5389
+ if (lease.baseUrl !== session.baseUrl || lease.profile !== session.profile || lease.userId !== session.userId) {
5390
+ throw ehrValidationError("context_mismatch", "\u786E\u8BA4\u4E0A\u4E0B\u6587\u5DF2\u53D8\u5316\uFF08\u79DF\u6237\u6216\u767B\u5F55\u8D26\u53F7\u4E0D\u540C\uFF09\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C prepare");
5391
+ }
5392
+ const positionName = String(args.positionName ?? "").trim();
5393
+ const payload = lease.payload || {};
5394
+ if (payload.positionName !== positionName) {
5395
+ throw ehrValidationError("target_changed", "\u804C\u4F4D\u540D\u79F0\u4E0E prepare \u65F6\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C prepare");
5396
+ }
5397
+ const cache = await requireCache(env);
5398
+ const formDataId = await saveFormData(env.host, {
5399
+ appId: cache.appId,
5400
+ formId: String(payload.formId),
5401
+ objId: String(payload.objId),
5402
+ name: `\u804C\u4F4D\u7BA1\u7406-${positionName}`,
5403
+ dataDetails: payload.dataDetails || []
5404
+ });
5405
+ if (!formDataId) {
5406
+ throw ehrValidationError("save_failed", "saveFormData \u672A\u8FD4\u56DE formDataId\uFF0C\u804C\u4F4D\u672A\u521B\u5EFA");
5407
+ }
5408
+ const response = await env.host.request({
5409
+ path: `/api/recruit/position/v2/addPosition?formDataId=${encodeURIComponent(formDataId)}`,
5410
+ method: "POST",
5411
+ operation: "ehr.recruit.position.create.apply"
5412
+ });
5413
+ const data = response?.data || {};
5414
+ const positionPostId = data.positionPostId ? String(data.positionPostId) : "";
5415
+ const created = response?.status !== false && !!positionPostId;
5416
+ const list = cache.lists.position;
5417
+ const baseUrl = session.baseUrl.replace(/\/+$/, "");
5418
+ const cardUrl = `${baseUrl}/sp/ebdfpage/card/0/${payload.objId}/${formDataId}`;
5419
+ const listLocateUrl = list?.listId ? `${baseUrl}/sp/ebdfpage/list/${list.listId}/card/0/${payload.objId}/${formDataId}/${list.listId}?id=${formDataId}&openSource=field&openSourceId=${formDataId}` : null;
5420
+ if (!created) {
5421
+ throw ehrValidationError(
5422
+ "position_create_failed",
5423
+ `\u804C\u4F4D\u8868\u5355\u5DF2\u4FDD\u5B58\uFF08formDataId=${formDataId}\uFF09\uFF0C\u4F46 addPosition \u672A\u8FD4\u56DE\u804C\u4F4D id\uFF1A${response?.msg || "\u672A\u77E5\u539F\u56E0"}\uFF1B\u8BF7\u5230\u804C\u4F4D\u7BA1\u7406\u6838\u5BF9\u540E\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5`
5424
+ );
5425
+ }
5426
+ return ehrSuccess("ehr.recruit.position.create.apply", {
5427
+ result: "success",
5428
+ formDataId,
5429
+ positionPostId,
5430
+ rpaChecked: data.checkRpa === void 0 ? null : !!data.checkRpa,
5431
+ rpaValidMsg: data.rpaValidMsg || null,
5432
+ positionName,
5433
+ cardUrl,
5434
+ listLocateUrl
5435
+ });
5436
+ }
5437
+ };
5438
+ var recruitPositionOperations = [
5439
+ positionCreatePrepare,
5440
+ positionCreateApply
5441
+ ];
5442
+
5443
+ // src/shortcuts/ehr/operations/recruit-candidate-import.ts
5444
+ import { createHash as createHash5 } from "node:crypto";
5445
+ import { readFile as readFile2, stat } from "node:fs/promises";
5446
+ import { basename } from "node:path";
5447
+ var SUB_TABLES = [
5448
+ {
5449
+ table: "rcrt_education_exp",
5450
+ input: "academicInformations",
5451
+ cols: {
5452
+ school: (r) => r.school,
5453
+ edu_major: (r) => r.edu_major ?? r.major,
5454
+ edu_education: (r) => r.edu_education ?? r.education,
5455
+ degree: (r) => r.degree,
5456
+ study_date: (r) => r.study_date ?? joinRange(r.start_date, r.end_date),
5457
+ faculty: (r) => r.faculty,
5458
+ research_direction: (r) => r.research_direction,
5459
+ major_courses: (r) => r.major_courses
5460
+ }
5461
+ },
5462
+ {
5463
+ table: "rcrt_work_exp",
5464
+ input: "jobInformations",
5465
+ cols: {
5466
+ co_name: (r) => r.co_name ?? r.company,
5467
+ work_position: (r) => r.work_position ?? r.position,
5468
+ work_dept: (r) => r.work_dept ?? r.department,
5469
+ work_desc: (r) => r.work_desc ?? r.job_description,
5470
+ resign_cause: (r) => r.resign_cause,
5471
+ start_end_date: (r) => r.start_end_date ?? joinRange(r.start_date, r.end_date) ?? r.work_date,
5472
+ work_type: (r) => r.work_type
5473
+ }
5474
+ },
5475
+ {
5476
+ table: "rcrt_sxjl",
5477
+ input: "internshipExperiences",
5478
+ cols: {
5479
+ sx_co_name: (r) => r.sx_co_name ?? r.company,
5480
+ position_job: (r) => r.position_job ?? r.position,
5481
+ job_description: (r) => r.job_description,
5482
+ entry_time: (r) => r.entry_time,
5483
+ sx_work_type: (r) => r.sx_work_type
5484
+ }
5485
+ },
5486
+ {
5487
+ table: "rcrt_project_exp",
5488
+ input: "projectExperiences",
5489
+ cols: {
5490
+ pj_name: (r) => r.pj_name ?? r.project_name,
5491
+ pj_role: (r) => r.pj_role,
5492
+ pj_duty: (r) => r.pj_duty,
5493
+ pj_introduction: (r) => r.pj_introduction ?? r.project_description,
5494
+ pj_date: (r) => r.pj_date ?? joinRange(r.start_date, r.end_date)
5495
+ }
5496
+ },
5497
+ {
5498
+ table: "rcrt_award_exp",
5499
+ input: "awardExperiences",
5500
+ cols: {
5501
+ award_name: (r) => r.award_name,
5502
+ awarded: (r) => r.awarded,
5503
+ credentials: (r) => r.credentials,
5504
+ award_date: (r) => r.award_date
5505
+ }
5506
+ },
5507
+ {
5508
+ table: "rcrt_language_skill",
5509
+ input: "languageAbilities",
5510
+ cols: {
5511
+ language_type: (r) => r.language_type,
5512
+ mastery: (r) => r.mastery,
5513
+ ls_ability: (r) => r.ls_ability,
5514
+ literacy: (r) => r.literacy
5515
+ }
5516
+ }
5517
+ ];
5518
+ var DICT_COLS = /* @__PURE__ */ new Set([
5519
+ "edu_education",
5520
+ "degree",
5521
+ "work_type",
5522
+ "sx_work_type",
5523
+ "mastery",
5524
+ "ls_ability",
5525
+ "literacy"
5526
+ ]);
5527
+ function joinRange(start, end) {
5528
+ const s = start === void 0 || start === null ? "" : String(start).trim();
5529
+ const e = end === void 0 || end === null ? "" : String(end).trim();
5530
+ if (!s && !e) return void 0;
5531
+ return `${s},${e}`;
5532
+ }
5533
+ async function resolveSubTableMeta(env, formId) {
5534
+ let data = null;
5535
+ try {
5536
+ const r = await env.host.request({
5537
+ path: `/api/ebuilder/form/form/core/getFormLayout?formId=${encodeURIComponent(formId)}`,
5538
+ method: "GET",
5539
+ operation: "ehr.recruit.candidate.import.prepare"
5540
+ });
5541
+ data = r?.data;
5542
+ } catch {
5543
+ }
5544
+ if (!data) {
5545
+ const r = await env.host.request({
5546
+ path: "/api/ebuilder/form/form/core/getFormLayout",
5547
+ method: "POST",
5548
+ body: { module: "ebuilderform", formId, terminalType: "PC" },
5549
+ operation: "ehr.recruit.candidate.import.prepare"
5550
+ });
5551
+ data = r?.data;
5552
+ }
5553
+ const comps = data?.formLayout?.pageLayouts?.[0]?.comps || [];
5554
+ const out = {};
5555
+ for (const comp of comps) {
5556
+ const cfg = comp?.config || {};
5557
+ if (comp?.type !== "DetailTable" || !cfg.dataKey || !cfg.subFormId) continue;
5558
+ out[String(cfg.dataKey)] = {
5559
+ dataKey: String(cfg.dataKey),
5560
+ subFormId: String(cfg.subFormId),
5561
+ title: String(cfg.title || ""),
5562
+ columns: {}
5563
+ };
5564
+ }
5565
+ for (const comp of comps) {
5566
+ const cfg = comp?.config || {};
5567
+ const subFormId = String(cfg.subFormId || "");
5568
+ if (!subFormId || !cfg.dataKey || !cfg.fieldId) continue;
5569
+ const target = Object.values(out).find((t) => t.subFormId === subFormId);
5570
+ if (!target) continue;
5571
+ const column = {
5572
+ id: String(cfg.fieldId),
5573
+ type: String(comp?.type || ""),
5574
+ title: String(cfg.title || "")
5575
+ };
5576
+ if (cfg.start?.dataKey && cfg.end?.dataKey) {
5577
+ column.startKey = String(cfg.start.dataKey);
5578
+ column.endKey = String(cfg.end.dataKey);
5579
+ }
5580
+ target.columns[String(cfg.dataKey)] = column;
5581
+ }
5582
+ return out;
5583
+ }
5584
+ function makeDictResolver(cache, warnings) {
5585
+ const byId = /* @__PURE__ */ new Map();
5586
+ for (const row of cache.dict?.raw || []) {
5587
+ if (row.option_id && row.selection_name) byId.set(String(row.option_id), String(row.selection_name));
5588
+ }
5589
+ return (value, label) => {
5590
+ const raw = value === void 0 || value === null ? "" : String(value).trim();
5591
+ if (!raw) return "";
5592
+ if (/^\d{6,}$/.test(raw)) {
5593
+ if (byId.has(raw)) return raw;
5594
+ warnings.push(`${label}\uFF1A\u5B57\u5178 id ${raw} \u672A\u5728\u672C\u73AF\u5883\u5B57\u5178\u4E2D\uFF0C\u5DF2\u8DF3\u8FC7`);
5595
+ return "";
5596
+ }
5597
+ const ids = [...new Set((cache.dict?.by_name?.[raw] || []).map(String))];
5598
+ if (ids.length === 1) return ids[0];
5599
+ warnings.push(`${label}\u300C${raw}\u300D${ids.length === 0 ? "\u5728\u5B57\u5178\u4E2D\u4E0D\u5B58\u5728" : "\u547D\u4E2D\u591A\u4E2A\u540C\u540D\u9009\u9879\uFF08\u6B67\u4E49\uFF09"}\uFF0C\u5DF2\u8DF3\u8FC7`);
5600
+ return "";
5601
+ };
5602
+ }
5603
+ function buildSubDetails(structured, meta, nodeOfTable, resolveDict) {
5604
+ const details = {};
5605
+ const stats = {};
5606
+ const nodes = {};
5607
+ const warnings = [];
5608
+ for (const spec of SUB_TABLES) {
5609
+ const sub = meta[spec.table];
5610
+ if (!sub) {
5611
+ stats[spec.table] = 0;
5612
+ continue;
5613
+ }
5614
+ const rows = Array.isArray(structured[spec.input]) ? structured[spec.input] : [];
5615
+ const built = [];
5616
+ for (const row of rows) {
5617
+ if (!row || typeof row !== "object") continue;
5618
+ const out = {};
5619
+ for (const [colKey, pick] of Object.entries(spec.cols)) {
5620
+ const column = sub.columns[colKey];
5621
+ if (!column) continue;
5622
+ let value = pick(row);
5623
+ if (value === void 0 || value === null || String(value).trim() === "") continue;
5624
+ if (DICT_COLS.has(colKey)) {
5625
+ value = resolveDict(value, `${sub.title}.${column.title || colKey}`);
5626
+ if (!value) continue;
5627
+ }
5628
+ if (column.startKey && column.endKey) {
5629
+ const parts = Array.isArray(value) ? value : String(value).split(/[,~]/);
5630
+ const start = String(parts[0] ?? "").trim();
5631
+ const end = String(parts[1] ?? "").trim();
5632
+ if (!start || !end) continue;
5633
+ out[column.startKey] = start;
5634
+ out[column.endKey] = end;
5635
+ continue;
5636
+ }
5637
+ out[colKey] = String(value);
5638
+ }
5639
+ if (Object.keys(out).length > 0) built.push(out);
5640
+ }
5641
+ if (built.length === 0) {
5642
+ stats[spec.table] = 0;
5643
+ continue;
5644
+ }
5645
+ const node = nodeOfTable[sub.subFormId];
5646
+ if (!node) {
5647
+ warnings.push(`\u5B50\u8868\u300C${sub.title || spec.table}\u300D\u5728 getTableMapping \u4E2D\u65E0\u5BF9\u5E94\u8282\u70B9\uFF0C\u672C\u6B21\u4E0D\u5199\u5165\uFF08${built.length} \u884C\uFF09`);
5648
+ stats[spec.table] = 0;
5649
+ continue;
5650
+ }
5651
+ details[node] = built;
5652
+ nodes[spec.table] = node;
5653
+ stats[spec.table] = built.length;
5654
+ }
5655
+ return { details, stats, nodes, warnings };
5656
+ }
5657
+ async function datasetSaveFormData(env, objId, mainTable, details) {
5658
+ const record = { mainTable };
5659
+ for (const [node, rows] of Object.entries(details)) {
5660
+ if (rows.length) record[node] = rows;
5661
+ }
5662
+ const response = await env.host.request({
5663
+ path: "/api/ebuilder/form/dataset/v2/saveFormData",
5664
+ method: "POST",
5665
+ body: {
5666
+ datajson: {
5667
+ datas: [record],
5668
+ header: { objId },
5669
+ operationinfo: { fieldNoFindIgnore: "true", doAction: "false" }
5670
+ }
5671
+ },
5672
+ operation: "ehr.recruit.candidate.import.apply"
5673
+ });
5674
+ const datajson = response?.data?.datajson || {};
5675
+ const code = response?.message?.code;
5676
+ const ok = (code === void 0 || String(code) === "0") && datajson.status !== false;
5677
+ if (!ok) {
5678
+ throw ehrValidationError(
5679
+ "save_failed",
5680
+ `dataset saveFormData \u5931\u8D25\uFF1A${datajson.message || response?.message?.msg || JSON.stringify(response).slice(0, 200)}`
5681
+ );
5682
+ }
5683
+ const ids = Array.isArray(datajson.dataIds) ? datajson.dataIds : [];
5684
+ if (!ids.length) {
5685
+ throw ehrValidationError("save_failed", `dataset saveFormData \u672A\u8FD4\u56DE dataIds\uFF1A${JSON.stringify(response).slice(0, 200)}`);
5686
+ }
5687
+ return String(ids[0]);
5688
+ }
5689
+ async function uploadResumeFile(env, filePath, fieldId, formId) {
5690
+ let buffer;
5691
+ let mtimeMs = Date.now();
5692
+ try {
5693
+ buffer = await readFile2(filePath);
5694
+ mtimeMs = (await stat(filePath)).mtimeMs || Date.now();
5695
+ } catch {
5696
+ throw ehrValidationError("resume_file_unreadable", `\u65E0\u6CD5\u8BFB\u53D6\u7B80\u5386\u6587\u4EF6\uFF1A${filePath}`);
5697
+ }
5698
+ const name = basename(filePath);
5699
+ const size = buffer.length;
5700
+ const baseForm = (withFile) => {
5701
+ const fd2 = new FormData();
5702
+ fd2.append("compressImg", "false");
5703
+ fd2.append("source", "form");
5704
+ fd2.append("fieldId", fieldId);
5705
+ fd2.append("sourceRefId", formId);
5706
+ fd2.append("module", "ebuilderform");
5707
+ fd2.append("authModule", "ebuilderform");
5708
+ fd2.append("formId", formId);
5709
+ fd2.append("refId", formId);
5710
+ fd2.append("size", String(size));
5711
+ fd2.append("commonText", JSON.stringify({ fieldId, formId }));
5712
+ if (withFile) fd2.append("file", new Blob([new Uint8Array(buffer)]), name);
5713
+ return fd2;
5714
+ };
5715
+ const pre = await env.host.request({
5716
+ path: "/api/file/preUploadCheck",
5717
+ method: "POST",
5718
+ multipart: baseForm(true),
5719
+ // 预上传检查必须带文件,否则服务端 500
5720
+ headers: { ebBusinessId: formId, langType: "zh_CN", timeZoneOffset: "-480" },
5721
+ operation: "ehr.recruit.candidate.import.apply"
5722
+ });
5723
+ const preUploadId = pre?.data?.preUploadId;
5724
+ const uploadType = pre?.data?.uploadType || "common";
5725
+ if (!preUploadId) {
5726
+ throw ehrValidationError("resume_preupload_failed", `\u7B80\u5386\u9884\u4E0A\u4F20\u68C0\u67E5\u5931\u8D25\uFF1A${JSON.stringify(pre || {}).slice(0, 200)}`);
5727
+ }
5728
+ const fd = baseForm(false);
5729
+ fd.append("name", name);
5730
+ fd.append("MD5", createHash5("md5").update(buffer).digest("hex"));
5731
+ fd.append("lastModified", String(Math.round(mtimeMs)));
5732
+ fd.append("lastModifiedDate", String(Math.round(mtimeMs)));
5733
+ fd.append("preUploadId", String(preUploadId));
5734
+ fd.append("uploadType", String(uploadType));
5735
+ fd.append("file", new Blob([new Uint8Array(buffer)]), name);
5736
+ const res = await env.host.request({
5737
+ path: "/api/file/module/upload",
5738
+ method: "POST",
5739
+ multipart: fd,
5740
+ headers: { ebBusinessId: formId, langType: "zh_CN", timeZoneOffset: "-480" },
5741
+ operation: "ehr.recruit.candidate.import.apply"
5742
+ });
5743
+ const fileid = res?.data?.fileid || res?.data?.uploadModuleDto?.fileObj?.fileId || res?.data?.fileObj?.fileId;
5744
+ if (!fileid) {
5745
+ throw ehrValidationError("resume_upload_failed", `\u7B80\u5386\u4E0A\u4F20\u672A\u8FD4\u56DE fileid\uFF1A${JSON.stringify(res || {}).slice(0, 200)}`);
5746
+ }
5747
+ return { fileid: String(fileid), name, size };
5748
+ }
5749
+ async function applyPlatformTags(env, talentId, tagNames) {
5750
+ if (!tagNames.length) return { applied: [], skipped: [] };
5751
+ const list = await env.host.request({
5752
+ path: "/api/my/tag/query",
5753
+ method: "POST",
5754
+ body: { tagModules: ["ebuilderform"], current: 1, pageSize: 500 },
5755
+ operation: "ehr.recruit.candidate.import.apply"
5756
+ });
5757
+ const rows = list?.data?.data || list?.data?.list || [];
5758
+ const byName = /* @__PURE__ */ new Map();
5759
+ for (const row of rows) {
5760
+ const name = String(row.name ?? row.tagName ?? "").trim();
5761
+ const id = row.tagId ?? row.id;
5762
+ if (name && id !== void 0 && !byName.has(name)) byName.set(name, { id: String(id), name });
5763
+ }
5764
+ const tags = [];
5765
+ const applied = [];
5766
+ const skipped = [];
5767
+ for (const name of tagNames) {
5768
+ const hit = byName.get(name);
5769
+ if (!hit) {
5770
+ skipped.push(name);
5771
+ continue;
5772
+ }
5773
+ tags.push({ id: hit.id, name: hit.name, type: 0 });
5774
+ applied.push(name);
5775
+ }
5776
+ if (tags.length) {
5777
+ await env.host.request({
5778
+ path: "/api/my/tag/common/batchAddEntityTag",
5779
+ method: "POST",
5780
+ body: { tags, batchDatas: [{ entityId: talentId, module: "ebuilderform" }] },
5781
+ operation: "ehr.recruit.candidate.import.apply"
5782
+ });
5783
+ }
5784
+ return { applied, skipped };
5785
+ }
5786
+ async function requireCache2(env) {
5787
+ if (!env.recruitContext) throw ehrValidationError("recruit_context_unavailable", "\u62DB\u8058\u4E0A\u4E0B\u6587\u52A0\u8F7D\u5668\u672A\u521D\u59CB\u5316");
5788
+ return env.recruitContext.load(env.host);
5789
+ }
5790
+ function str(value) {
5791
+ if (value === void 0 || value === null) return "";
5792
+ return String(value).trim();
5793
+ }
5794
+ async function buildImportPlan(env, cache, args) {
5795
+ const warnings = [];
5796
+ const talent = cache.forms.talent_pool;
5797
+ const objId = talent?.objId;
5798
+ const formId = talent?.formId || objId;
5799
+ if (!objId || !formId) {
5800
+ throw ehrValidationError("talent_form_unavailable", "\u5F53\u524D\u73AF\u5883\u672A\u542F\u7528\u300C\u4EBA\u624D\u5E93\u300D\u529F\u80FD\uFF08\u672A\u89E3\u6790\u5230 talent_pool \u8868\u5355\uFF09");
5801
+ }
5802
+ const fields = cache.fields[objId] || {};
5803
+ const name = str(args.candidateName);
5804
+ const mobile = str(args.telephone);
5805
+ if (!name) throw ehrValidationError("candidate_name_required", "candidateName \u5FC5\u586B");
5806
+ if (!/^\d{6,}$/.test(mobile)) throw ehrValidationError("telephone_invalid", "telephone \u5FC5\u987B\u662F 6 \u4F4D\u4EE5\u4E0A\u6570\u5B57\uFF08\u624B\u673A\u53F7\uFF09");
5807
+ if (!fields.candidate_name || !fields.telephone) {
5808
+ throw ehrValidationError("recruit_field_missing", "\u73AF\u5883\u7F13\u5B58\u7F3A\u4EBA\u624D\u59D3\u540D\u5B57\u6BB5(candidate_name)\u6216\u624B\u673A\u5B57\u6BB5(telephone)");
5809
+ }
5810
+ const resolveDict = makeDictResolver(cache, warnings);
5811
+ const main = {};
5812
+ const putText = (key, value, longText = false) => {
5813
+ if (!fields[key]) return;
5814
+ const text = str(value);
5815
+ if (!text) return;
5816
+ main[key] = text;
5817
+ void longText;
5818
+ };
5819
+ putText("candidate_name", name);
5820
+ putText("telephone", mobile);
5821
+ putText("email", args.email);
5822
+ putText("birthday", args.birthday);
5823
+ putText("age", args.age);
5824
+ putText("college", args.college);
5825
+ putText("graduate_date", args.graduateDate);
5826
+ putText("joinworkdate", args.joinworkdate);
5827
+ putText("intend_position", args.intendPosition);
5828
+ putText("intended_workplace", args.intendedWorkplace);
5829
+ putText("self_desc", args.selfDesc, true);
5830
+ putText("work_ability", args.workAbility, true);
5831
+ for (const [key, inputKey, label] of [
5832
+ ["gender", "genderName", "\u6027\u522B"],
5833
+ ["highest_degree", "highestDegreeName", "\u6700\u9AD8\u5B66\u5386"]
5834
+ ]) {
5835
+ if (!fields[key]) continue;
5836
+ const raw = str(args[inputKey]);
5837
+ if (!raw) continue;
5838
+ const id = resolveDict(raw, label);
5839
+ if (id) main[key] = id;
5840
+ }
5841
+ const channel = str(args.channel);
5842
+ if (channel) {
5843
+ if (!fields.channel) warnings.push("\u4EBA\u624D\u8868\u5355\u65E0\u6E20\u9053\u5B57\u6BB5\uFF0Cchannel \u5DF2\u5FFD\u7565");
5844
+ else {
5845
+ const id = resolveDict(channel, "\u6E20\u9053");
5846
+ if (id) main.channel = id;
5847
+ }
5848
+ }
5849
+ if (fields.is_blacklist) {
5850
+ const noId = resolveDict("\u5426", "\u9ED1\u540D\u5355");
5851
+ if (noId) main.is_blacklist = noId;
5852
+ }
5853
+ let subStats = {};
5854
+ let detailNodes = {};
5855
+ let details = {};
5856
+ try {
5857
+ const mappingResponse = await env.host.request({
5858
+ path: `/api/ebuilder/form/dataset/v1/getTableMapping?objId=${encodeURIComponent(objId)}`,
5859
+ method: "GET",
5860
+ operation: "ehr.recruit.candidate.import.prepare"
5861
+ });
5862
+ const mapping = mappingResponse?.data;
5863
+ if (!mapping?.mainTable) throw new Error("getTableMapping \u672A\u8FD4\u56DE mainTable");
5864
+ const nodeOfTable = {};
5865
+ for (const [node, tableId] of Object.entries(mapping)) {
5866
+ if (node === "mainTable") continue;
5867
+ nodeOfTable[String(tableId)] = node;
5868
+ }
5869
+ const meta = await resolveSubTableMeta(env, formId);
5870
+ const built = buildSubDetails(args, meta, nodeOfTable, resolveDict);
5871
+ details = built.details;
5872
+ subStats = built.stats;
5873
+ detailNodes = built.nodes;
5874
+ warnings.push(...built.warnings);
5875
+ } catch (error) {
5876
+ warnings.push(`\u660E\u7EC6\u8868\u672A\u5199\u5165\uFF1A${error?.message || "\u672A\u77E5\u9519\u8BEF"}\uFF08\u4E3B\u8868\u7167\u5E38\u5EFA\u6863\uFF09`);
5877
+ }
5878
+ const subRowTotal = Object.values(subStats).reduce((sum, n) => sum + n, 0);
5879
+ const summary = {
5880
+ candidateName: name,
5881
+ telephone: mobile,
5882
+ email: str(args.email) || "(\u672A\u586B)",
5883
+ channel: channel || "(\u672A\u6307\u5B9A)",
5884
+ mainFieldCount: Object.keys(main).length,
5885
+ mainFields: Object.keys(main).sort(),
5886
+ subTableRows: subStats,
5887
+ subRowTotal,
5888
+ detailNodes,
5889
+ platformTags: Array.isArray(args.tags) ? args.tags : [],
5890
+ resumeFile: str(args.resumeFile) || "(\u672A\u63D0\u4F9B\u539F\u4EF6)",
5891
+ warnings
5892
+ };
5893
+ return {
5894
+ objId,
5895
+ formId,
5896
+ talentObjId: objId,
5897
+ mainTable: main,
5898
+ details,
5899
+ subStats,
5900
+ detailNodes,
5901
+ warnings,
5902
+ talentName: name,
5903
+ mobile,
5904
+ summary
5905
+ };
5906
+ }
5907
+ var candidateImportPrepare = {
5908
+ name: "ehr.recruit.candidate.import.prepare",
5909
+ resource: "recruit",
5910
+ method: "candidate.import.prepare",
5911
+ action: "ehr.recruit.candidate.import.prepare",
5912
+ summary: "\u7B80\u5386\u5BFC\u5165\u5EFA\u4EBA\u624D\u7684\u786E\u8BA4\u6458\u8981\uFF1A\u624B\u673A\u53F7\u67E5\u91CD + \u4E3B\u8868\u5B57\u6BB5\u6620\u5C04 + 6 \u5F20\u660E\u7EC6\u8868\u884C\u6570 + \u6807\u7B7E\u6838\u9A8C\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6570\u636E",
5913
+ capability: "recruit",
5914
+ risk: WRITE,
5915
+ requiresConfirmation: true,
5916
+ inputSchema: schemaOf({
5917
+ candidateName: { type: "string", maxLength: 60, description: "\u59D3\u540D\uFF08\u5FC5\u586B\uFF09" },
5918
+ telephone: { type: "string", pattern: "^\\d{6,20}$", description: "\u624B\u673A\u53F7\uFF08\u5FC5\u586B\uFF0C\u67E5\u91CD\u4E3B\u952E\uFF09" },
5919
+ email: { type: "string", maxLength: 120 },
5920
+ genderName: { type: "string", description: "\u6027\u522B\u540D\u79F0\uFF08\u5982 \u7537/\u5973\uFF0C\u6309\u672C\u73AF\u5883\u5B57\u5178\u53CD\u67E5\uFF09" },
5921
+ birthday: { type: "string", description: "\u51FA\u751F\u65E5\u671F YYYY-MM-DD" },
5922
+ age: { type: "string", description: "\u5E74\u9F84" },
5923
+ highestDegreeName: { type: "string", description: "\u6700\u9AD8\u5B66\u5386\u540D\u79F0\uFF08\u5982 \u672C\u79D1/\u7855\u58EB\uFF09" },
5924
+ college: { type: "string", maxLength: 120, description: "\u6BD5\u4E1A\u9662\u6821" },
5925
+ graduateDate: { type: "string", description: "\u6BD5\u4E1A\u65F6\u95F4" },
5926
+ joinworkdate: { type: "string", description: "\u53C2\u52A0\u5DE5\u4F5C\u65E5\u671F" },
5927
+ intendPosition: { type: "string", maxLength: 120, description: "\u610F\u5411\u5C97\u4F4D" },
5928
+ intendedWorkplace: { type: "string", maxLength: 120, description: "\u671F\u671B\u5DE5\u4F5C\u5730\u70B9" },
5929
+ selfDesc: { type: "string", maxLength: 2e4, description: "\u81EA\u6211\u8BC4\u4EF7\uFF08\u957F\u6587\u672C\uFF09" },
5930
+ workAbility: { type: "string", maxLength: 2e4, description: "\u804C\u4E1A\u6280\u80FD/\u6280\u672F\u6808\uFF08\u957F\u6587\u672C\uFF09" },
5931
+ channel: { type: "string", maxLength: 60, description: "\u4EBA\u624D\u6765\u6E90\u6E20\u9053\u540D\u79F0\uFF08\u5982 \u667A\u8054\u62DB\u8058/\u5185\u63A8\uFF09" },
5932
+ tags: { type: "array", items: { type: "string" }, maxItems: 20, description: "\u5E73\u53F0\u6807\u7B7E\u540D\u79F0\uFF08\u987B\u4E3A\u672C\u73AF\u5883\u5DF2\u6709\u6807\u7B7E\uFF0C\u540C\u540D\u4F18\u5148\u516C\u5171\uFF1B\u4E0D\u81EA\u52A8\u521B\u5EFA\uFF09" },
5933
+ resumeFile: { type: "string", description: "\u7B80\u5386\u539F\u4EF6\u672C\u5730\u8DEF\u5F84\uFF08\u53EF\u9009\uFF1B\u63D0\u4F9B\u5219\u4E0A\u4F20\u5230\u539F\u59CB\u7B80\u5386\u9644\u4EF6\u5B57\u6BB5\uFF09" },
5934
+ academicInformations: {
5935
+ type: "array",
5936
+ maxItems: 20,
5937
+ description: "\u6559\u80B2\u7ECF\u5386\uFF08school/edu_major/edu_education/degree/study_date/faculty/research_direction/major_courses\uFF09",
5938
+ items: { type: "object" }
5939
+ },
5940
+ jobInformations: {
5941
+ type: "array",
5942
+ maxItems: 30,
5943
+ description: "\u5DE5\u4F5C\u7ECF\u5386\uFF08co_name/work_position/work_dept/work_desc/resign_cause/start_date/end_date/work_type\uFF09",
5944
+ items: { type: "object" }
5945
+ },
5946
+ internshipExperiences: {
5947
+ type: "array",
5948
+ maxItems: 20,
5949
+ description: "\u5B9E\u4E60\u7ECF\u5386\uFF08sx_co_name/position_job/job_description/entry_time/sx_work_type\uFF09",
5950
+ items: { type: "object" }
5951
+ },
5952
+ projectExperiences: {
5953
+ type: "array",
5954
+ maxItems: 30,
5955
+ description: "\u9879\u76EE\u7ECF\u5386\uFF08pj_name/pj_role/pj_duty/pj_introduction/start_date/end_date\uFF09",
5956
+ items: { type: "object" }
5957
+ },
5958
+ awardExperiences: {
5959
+ type: "array",
5960
+ maxItems: 20,
5961
+ description: "\u83B7\u5956\u7ECF\u5386\uFF08award_name/awarded/credentials/award_date\uFF09",
5962
+ items: { type: "object" }
5963
+ },
5964
+ languageAbilities: {
5965
+ type: "array",
5966
+ maxItems: 10,
5967
+ description: "\u8BED\u8A00\u80FD\u529B\uFF08language_type/mastery/ls_ability/literacy\uFF09",
5968
+ items: { type: "object" }
5969
+ }
5970
+ }, { required: ["candidateName", "telephone"] }),
5971
+ handler: async (args, env) => {
5972
+ const cache = await requireCache2(env);
5973
+ const plan = await buildImportPlan(env, cache, args);
5974
+ const dup = await checkDuplicateByMobile(env, cache, plan.mobile);
5975
+ if (dup.length > 1) {
5976
+ 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`);
5977
+ }
5978
+ const dupRow = dup[0];
5979
+ const session = await env.host.getSession();
5980
+ const continuation = await env.continuation.issue(
5981
+ {
5982
+ operation: "ehr.recruit.candidate.import.apply",
5983
+ baseUrl: session.baseUrl,
5984
+ profile: session.profile,
5985
+ userId: session.userId,
5986
+ payload: {
5987
+ objId: plan.objId,
5988
+ formId: plan.formId,
5989
+ mainTable: plan.mainTable,
5990
+ details: plan.details,
5991
+ tags: Array.isArray(args.tags) ? args.tags.map(String) : [],
5992
+ resumeFile: str(args.resumeFile),
5993
+ subStats: plan.subStats,
5994
+ warnings: plan.warnings,
5995
+ candidateName: plan.talentName,
5996
+ telephone: plan.mobile
5997
+ }
5998
+ },
5999
+ 10 * 60 * 1e3
6000
+ );
6001
+ return ehrSuccess("ehr.recruit.candidate.import.prepare", {
6002
+ status: "AWAITING_CONFIRMATION",
6003
+ preview: {
6004
+ action: "\u7B80\u5386\u5BFC\u5165\u5EFA\u4EBA\u624D\uFF08dataset/v2/saveFormData \u4E3B\u8868+\u660E\u7EC6 \u2192 \u6279\u6B21 \u2192 \u667A\u80FD\u6807\u7B7E \u2192 \u5E73\u53F0\u6807\u7B7E\uFF09",
6005
+ ...plan.summary,
6006
+ duplicate: dupRow ? `\u5DF2\u5B58\u5728\u540C\u624B\u673A\u53F7\u4EBA\u624D\u300C${dupRow.candidate_name || dupRow.name || ""}\u300D\uFF08id=${dupRow.form_data_id || dupRow.id}\uFF09\u2192 apply \u5C06\u963B\u65AD` : "\u65E0\u91CD\u590D"
6007
+ },
6008
+ willSubmit: {
6009
+ objId: plan.objId,
6010
+ mainTable: plan.mainTable,
6011
+ details: plan.details,
6012
+ tags: Array.isArray(args.tags) ? args.tags : []
6013
+ },
6014
+ continuation,
6015
+ expiresInSeconds: 600
6016
+ });
6017
+ }
6018
+ };
6019
+ var candidateImportApply = {
6020
+ name: "ehr.recruit.candidate.import.apply",
6021
+ resource: "recruit",
6022
+ method: "candidate.import.apply",
6023
+ action: "ehr.recruit.candidate.import.apply",
6024
+ summary: "\u6267\u884C\u7B80\u5386\u5BFC\u5165\u5EFA\u4EBA\u624D\uFF08\u4E3B\u8868+\u660E\u7EC6 \u2192 \u6279\u6B21 \u2192 \u667A\u80FD\u6807\u7B7E \u2192 \u5E73\u53F0\u6807\u7B7E \u2192 \u53CC\u94FE\u63A5\uFF09\uFF1B\u5FC5\u987B\u5148 prepare \u5E76\u7ECF\u7528\u6237\u660E\u786E\u786E\u8BA4",
6025
+ capability: "recruit",
6026
+ risk: WRITE,
6027
+ requiresConfirmation: true,
6028
+ inputSchema: schemaOf(
6029
+ {
6030
+ candidateName: { type: "string", maxLength: 60 },
6031
+ telephone: { type: "string", pattern: "^\\d{6,20}$" },
6032
+ resumeFile: { type: "string" },
6033
+ tags: { type: "array", items: { type: "string" }, maxItems: 20 },
6034
+ confirm: { const: true },
6035
+ continuation: { type: "string", minLength: 1 }
6036
+ },
6037
+ { required: ["candidateName", "telephone", "confirm", "continuation"] }
6038
+ ),
6039
+ handler: async (args, env) => {
6040
+ if (args.confirm !== true) {
6041
+ throw ehrConfirmationError("ehr.recruit.candidate.import.apply", {
6042
+ hint: "\u91CD\u65B0\u8C03\u7528\u65F6\u4F20\u5165 confirm=true \u4E0E prepare \u8FD4\u56DE\u7684 continuation"
6043
+ });
6044
+ }
6045
+ const lease = await env.continuation.verify(args.continuation, "ehr.recruit.candidate.import.apply");
6046
+ const session = await env.host.getSession();
6047
+ if (lease.baseUrl !== session.baseUrl || lease.profile !== session.profile || lease.userId !== session.userId) {
6048
+ throw ehrValidationError("context_mismatch", "\u786E\u8BA4\u4E0A\u4E0B\u6587\u5DF2\u53D8\u5316\uFF08\u79DF\u6237\u6216\u767B\u5F55\u8D26\u53F7\u4E0D\u540C\uFF09\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C prepare");
6049
+ }
6050
+ const payload = lease.payload || {};
6051
+ if (payload.candidateName !== str(args.candidateName) || payload.telephone !== str(args.telephone)) {
6052
+ throw ehrValidationError("target_changed", "\u59D3\u540D\u6216\u624B\u673A\u53F7\u4E0E prepare \u65F6\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C prepare");
6053
+ }
6054
+ const cache = await requireCache2(env);
6055
+ const objId = String(payload.objId);
6056
+ const formId = String(payload.formId);
6057
+ const fields = cache.fields[objId] || {};
6058
+ const mainTable = { ...payload.mainTable || {} };
6059
+ const resumeFieldKey = "original_resume";
6060
+ const resumeFile = str(payload.resumeFile);
6061
+ let uploaded = null;
6062
+ if (resumeFile) {
6063
+ if (!fields[resumeFieldKey]) {
6064
+ throw ehrValidationError("resume_field_missing", "\u4EBA\u624D\u8868\u5355\u65E0\u300C\u539F\u59CB\u7B80\u5386\u300D\u9644\u4EF6\u5B57\u6BB5\uFF08original_resume\uFF09\uFF0C\u65E0\u6CD5\u4E0A\u4F20\u539F\u4EF6");
6065
+ }
6066
+ uploaded = await uploadResumeFile(env, resumeFile, fields[resumeFieldKey], formId);
6067
+ mainTable[resumeFieldKey] = uploaded.fileid;
6068
+ }
6069
+ let talentId;
6070
+ try {
6071
+ talentId = await datasetSaveFormData(env, objId, mainTable, payload.details || {});
6072
+ } catch (error) {
6073
+ if (error?.subtype === "save_failed") throw error;
6074
+ throw ehrPartialError(
6075
+ `\u7B80\u5386\u5EFA\u6863\u5199\u5165\u7ED3\u679C\u4E0D\u786E\u5B9A\uFF08${error?.message || "\u672A\u77E5\u9519\u8BEF"}\uFF09\uFF1A\u8BF7\u5148\u7528\u4EBA\u624D\u5E93\u641C\u7D22\u624B\u673A\u53F7 ${payload.telephone} \u6838\u5BF9\u662F\u5426\u5DF2\u5EFA\u6863\uFF0C\u7981\u6B62\u76F4\u63A5\u91CD\u8BD5`,
6076
+ { candidateName: payload.candidateName, telephone: payload.telephone }
6077
+ );
6078
+ }
6079
+ let batchOk = null;
6080
+ let batchNote;
6081
+ try {
6082
+ await env.host.request({
6083
+ path: `/api/recruit/candidate/initTalentBatch?formDataId=${encodeURIComponent(talentId)}`,
6084
+ method: "POST",
6085
+ operation: "ehr.recruit.candidate.import.apply"
6086
+ });
6087
+ batchOk = true;
6088
+ } catch (error) {
6089
+ batchOk = false;
6090
+ 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`;
6091
+ }
6092
+ let labelMatch = null;
6093
+ try {
6094
+ const r = await env.host.request({
6095
+ path: `/api/recruit/label/matchTalentLabel?talentId=${encodeURIComponent(talentId)}`,
6096
+ method: "POST",
6097
+ body: {},
6098
+ operation: "ehr.recruit.candidate.import.apply"
6099
+ });
6100
+ labelMatch = r?.data === void 0 ? "TRIGGERED" : String(r.data);
6101
+ } catch {
6102
+ labelMatch = "FAILED\uFF08\u4E0D\u5F71\u54CD\u5EFA\u6863\uFF0C\u53EF\u7A0D\u540E\u5728\u4EBA\u624D\u5361\u7247\u624B\u52A8\u89E6\u53D1\uFF09";
6103
+ }
6104
+ let tagResult = { applied: [], skipped: [] };
6105
+ try {
6106
+ tagResult = await applyPlatformTags(env, talentId, Array.isArray(payload.tags) ? payload.tags.map(String) : []);
6107
+ } catch (error) {
6108
+ tagResult = { applied: [], skipped: Array.isArray(payload.tags) ? payload.tags.map(String) : [] };
6109
+ }
6110
+ const baseUrl = session.baseUrl.replace(/\/+$/, "");
6111
+ const extra = String(cache.selfUse) === "1" ? "&recruitType=useSelf" : "";
6112
+ const talentCardUrl = cache.ebdDetailPageId ? `${baseUrl}/sp/ebdpage/view/${cache.ebdDetailPageId}?talentId=${talentId}${extra}` : null;
6113
+ const talentListId = cache.lists.talent?.listId;
6114
+ const talentListLocateUrl = talentListId ? `${baseUrl}/sp/ebdfpage/list/${talentListId}?talentId=${talentId}${extra}` : null;
6115
+ const warnings = [...payload.warnings || []];
6116
+ if (batchNote) warnings.push(batchNote);
6117
+ return ehrSuccess("ehr.recruit.candidate.import.apply", {
6118
+ result: "success",
6119
+ talentId,
6120
+ candidateName: payload.candidateName,
6121
+ telephone: payload.telephone,
6122
+ mainTableFields: Object.keys(mainTable).length,
6123
+ detailRows: payload.subStats || void 0,
6124
+ attachment: uploaded ? { name: uploaded.name, size: uploaded.size } : null,
6125
+ batch: { ok: batchOk, note: batchNote },
6126
+ labelMatch,
6127
+ tagsApplied: tagResult.applied,
6128
+ tagsSkipped: tagResult.skipped,
6129
+ talentCardUrl,
6130
+ talentListLocateUrl
6131
+ }, warnings.length ? { warnings } : void 0);
6132
+ }
6133
+ };
6134
+ var recruitCandidateImportOperations = [
6135
+ candidateImportPrepare,
6136
+ candidateImportApply
6137
+ ];
6138
+
4825
6139
  // src/shortcuts/ehr/operations/salary.ts
4826
6140
  function requireMonthRange(value, field) {
4827
6141
  if (!Array.isArray(value) || value.length === 0) {
@@ -5003,7 +6317,10 @@ function buildRegistry() {
5003
6317
  ...hrDataSourceOperations,
5004
6318
  ...hrReportOperations,
5005
6319
  ...recruitOperations,
5006
- ...recruitWriteOperations
6320
+ ...recruitWriteOperations,
6321
+ ...recruitStatsOperations,
6322
+ ...recruitPositionOperations,
6323
+ ...recruitCandidateImportOperations
5007
6324
  ];
5008
6325
  for (const definition of definitions) {
5009
6326
  if (registry.has(definition.name)) {
@@ -5114,7 +6431,7 @@ async function inputFrom(options, extra = {}) {
5114
6431
  if (opts.input) {
5115
6432
  let raw;
5116
6433
  try {
5117
- raw = stripBom(opts.input === "-" ? await readStdin() : await readFile2(String(opts.input), "utf8"));
6434
+ raw = stripBom(opts.input === "-" ? await readStdin() : await readFile3(String(opts.input), "utf8"));
5118
6435
  } catch {
5119
6436
  throw ehrValidationError("input_unreadable", `\u65E0\u6CD5\u8BFB\u53D6 --input\uFF1A${String(opts.input)}`);
5120
6437
  }