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