weaver-work-cli 0.1.39 → 0.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/chunks/{archive-ENZYBBME.js → archive-J4I6MCKF.js} +2 -2
- package/dist/chunks/{archive-oa-YWLEC5V4.js → archive-oa-BRZKKTO4.js} +2 -2
- package/dist/chunks/{asset-OOV6DWPA.js → asset-L7D4FAOZ.js} +2 -2
- package/dist/chunks/{asset-idle-RE632MY5.js → asset-idle-DADLLV75.js} +2 -2
- package/dist/chunks/{attachment-content-RWN2FCLL.js → attachment-content-K4C7VLWQ.js} +3 -3
- package/dist/chunks/{blog-PEPOJAL7.js → blog-BVGQ7NZL.js} +2 -2
- package/dist/chunks/{caizhilian-WYIQDVPJ.js → caizhilian-SAADNFD6.js} +2 -2
- package/dist/chunks/{calendar-CIARBAOX.js → calendar-Q7NQY5Z2.js} +61 -23
- package/dist/chunks/{chunk-F2EQXUEE.js → chunk-3KTFS4IA.js} +397 -159
- package/dist/chunks/{chunk-G2JSIUWF.js → chunk-4PD5NWAX.js} +89 -0
- package/dist/chunks/{chunk-HJYVHXUQ.js → chunk-4RAE6JXP.js} +1 -1
- package/dist/chunks/{chunk-CQHGNCK2.js → chunk-72CKAUAG.js} +41 -8
- package/dist/chunks/{chunk-K3RUEEOL.js → chunk-APVFJUQH.js} +2 -2
- package/dist/chunks/{chunk-BJGXMFGD.js → chunk-BBKDXQEC.js} +2 -2
- package/dist/chunks/chunk-BIPYLLVS.js +501 -0
- package/dist/chunks/{chunk-GVPJZXJ2.js → chunk-EKWAQSLU.js} +92 -50
- package/dist/chunks/{chunk-XXJOSZTQ.js → chunk-FOAA27U5.js} +381 -57
- package/dist/chunks/{chunk-I2HB737U.js → chunk-G4UR6NXN.js} +2 -2
- package/dist/chunks/{chunk-YS5DPN4T.js → chunk-GEWYMQME.js} +1 -1
- package/dist/chunks/{chunk-P2DAISYW.js → chunk-GHXCDPTM.js} +1 -1
- package/dist/chunks/{chunk-L4N7B7ZD.js → chunk-IZ7S7QUS.js} +4 -4
- package/dist/chunks/{chunk-NLK2RZKG.js → chunk-JPRO6ER2.js} +122 -2214
- package/dist/chunks/{chunk-36DIFZAH.js → chunk-LADPYOWC.js} +2 -2
- package/dist/chunks/chunk-LDZQPUIA.js +1238 -0
- package/dist/chunks/chunk-NKZRQXST.js +2546 -0
- package/dist/chunks/{chunk-EINRJHQ3.js → chunk-QKNKR4RH.js} +3 -3
- package/dist/chunks/{chunk-T7JDJJ7H.js → chunk-R4CHUSKC.js} +126 -19
- package/dist/chunks/{chunk-EZGFHJFW.js → chunk-SA6VBIHY.js} +88 -15
- package/dist/chunks/{chunk-ZXRNHZTE.js → chunk-TTPJJJC4.js} +2 -2
- package/dist/chunks/{chunk-KTCB5R5H.js → chunk-W7BEHOSR.js} +2 -2
- package/dist/chunks/{chunk-MUQUHKPG.js → chunk-YRSZQUD2.js} +2 -2
- package/dist/chunks/{conflict-DRMDOL56.js → conflict-F2OMZRTO.js} +2 -2
- package/dist/chunks/{contract-73MERZWN.js → contract-5BETHRSG.js} +2 -2
- package/dist/chunks/{cowork-HNGBPD7A.js → cowork-3MN6EQQO.js} +27 -10
- package/dist/chunks/create-flow-5FQNNMVO.js +55 -0
- package/dist/chunks/{ebuilder-form-OY6DVNRK.js → ebuilder-form-SBHOYBUD.js} +3 -3
- package/dist/chunks/{ehr-RAQNEQO7.js → ehr-KMOJULKG.js} +432 -1234
- package/dist/chunks/env-LB7NBL2O.js +19 -0
- package/dist/chunks/{esb-6XV7C7YV.js → esb-W2PJCFCX.js} +4 -4
- package/dist/chunks/factory-6ZFQXQ46.js +30 -0
- package/dist/chunks/{fna-XTSRB7G7.js → fna-ZQPCUV4R.js} +2 -2
- package/dist/chunks/group-chat-2HHAJN46.js +20 -0
- package/dist/chunks/{host-YMBXWFWX.js → host-4CBHGOHT.js} +3 -3
- package/dist/chunks/{host-A7J6GDUP.js → host-ATKVNZAT.js} +3 -3
- package/dist/chunks/{host-3F5UM5GN.js → host-BQH22NTQ.js} +3 -3
- package/dist/chunks/{host-YAQAPYNP.js → host-X2CSLFM6.js} +3 -3
- package/dist/chunks/{host-VGPVP7YF.js → host-YTODKMSL.js} +3 -3
- package/dist/chunks/{hrm-TLHKC545.js → hrm-FFM4UJFS.js} +4 -4
- package/dist/chunks/{im-YOUCFMUK.js → im-VEHKDQPB.js} +8 -8
- package/dist/chunks/{invoice-WNBS2B4C.js → invoice-LVIKSIIF.js} +3 -3
- package/dist/chunks/{jiuchuanhui-XABWS5RN.js → jiuchuanhui-X6DINO5P.js} +2 -2
- package/dist/chunks/{lazy-dispatch-NLZ5UQQI.js → lazy-dispatch-KSWTN5L6.js} +4 -4
- package/dist/chunks/{mail-LRYMBFNL.js → mail-VDKRGUD3.js} +9 -4
- package/dist/chunks/{meeting-ESXL6MFI.js → meeting-CLC6HP3Z.js} +12 -10
- package/dist/chunks/{okr-EOV65SSF.js → okr-CHG4OO3U.js} +2 -2
- package/dist/chunks/{plan-YFPVKW6Y.js → plan-XWR75667.js} +2 -2
- package/dist/chunks/{project-MDVZUYQW.js → project-5BXOOKMG.js} +40 -23
- package/dist/chunks/recruit-stage-DGMFNNDE.js +4507 -0
- package/dist/chunks/{registry-VT5Y2HLA.js → registry-7FFOXIAW.js} +4 -4
- package/dist/chunks/{registry-QCLZEDAP.js → registry-BKBZPBAJ.js} +10 -8
- package/dist/chunks/{registry-YX4CSEMD.js → registry-RHMW6ANW.js} +2 -2
- package/dist/chunks/{registry-NYPYMZXG.js → registry-Z43434VG.js} +1 -1
- package/dist/chunks/{room-recommend-L3MWH4JW.js → room-recommend-62H7ZQQ6.js} +4 -4
- package/dist/chunks/{schedule-UASAVDVD.js → schedule-FNXKH3DQ.js} +4 -4
- package/dist/chunks/{workflow-KDGIUCNW.js → workflow-ADHKCAJK.js} +4 -4
- package/dist/index.js +33 -28
- package/dist/internal/e10/auth/commands.js +110 -56
- package/dist/internal/e10/auth/messages.js +6 -0
- package/dist/internal/e10/auth/session.js +28 -1
- package/dist/internal/e10/auth/xiaoe.js +315 -48
- package/dist/internal/e10/auth-recovery.js +36 -5
- package/dist/internal/e10/client.js +13 -0
- package/dist/internal/e10/context.js +61 -9
- package/dist/internal/e10/index.js +1 -1
- package/dist/internal/e10/network-error.js +93 -0
- package/dist/internal/e10/request-runtime.js +15 -0
- package/dist/shortcuts/calendar/operations/read.js +31 -22
- package/dist/shortcuts/calendar/operations/write.js +45 -1
- package/dist/shortcuts/cowork/operations/discuss-write.js +5 -1
- package/dist/shortcuts/cowork/operations/item-write.js +5 -3
- 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/ehr/operations/attend-write.js +43 -1
- 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 +134 -59
- package/dist/shortcuts/ehr/operations/recruit-write.js +262 -17
- package/dist/shortcuts/ehr/operations/recruit.js +10 -4
- package/dist/shortcuts/ehr/operations/shared.js +47 -0
- package/dist/shortcuts/hrm/operations/org.js +118 -0
- package/dist/shortcuts/index.js +5 -0
- package/dist/shortcuts/mail/operations/mail-send.js +8 -2
- package/dist/shortcuts/meeting/operations/eb/catalog.js +9 -2
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +461 -127
- package/dist/shortcuts/meeting/operations/eb/crm-search.js +87 -5
- package/dist/shortcuts/meeting/operations/eb/factory.js +33 -0
- package/dist/shortcuts/meeting/operations/eb/flow-memory.js +49 -0
- package/dist/shortcuts/meeting/operations/eb/group-chat.js +605 -0
- package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
- package/dist/shortcuts/meeting/operations/eb/person-cache.js +96 -0
- package/dist/shortcuts/meeting/operations/eb/room-recommend.js +44 -6
- package/dist/shortcuts/meeting/operations/eb/room-summary.js +2 -0
- package/dist/shortcuts/meeting/operations/eb/usage-cache.js +99 -0
- package/dist/shortcuts/meeting/operations/from-group-chat.js +35 -12
- 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/recruit-stage/context.js +119 -0
- package/dist/shortcuts/recruit-stage/continuation.js +73 -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 +630 -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 +1198 -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/operations/form.js +21 -2
- package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +52 -1
- package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +4 -2
- package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +19 -11
- 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 +78 -10
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +3 -1
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +17 -11
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +16 -40
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +3 -2
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +52 -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 +53 -16
- package/docs/e10-auth.md +22 -1
- package/docs/operation-manual.md +8 -0
- package/package.json +1 -1
- package/scripts/install-skill-to-xiaoe.mjs +105 -0
- package/scripts/mcp-meeting.mjs +234 -0
- package/scripts/package-skill.mjs +4 -2
- package/skill-template/business-info.json +24 -0
- package/skill-template/domains/recruit-stage.md +29 -0
- package/skills/weaver-e10-calendar/SKILL.md +2 -1
- package/skills/weaver-e10-calendar/references/calendar-write.md +54 -0
- package/skills/weaver-e10-cowork/SKILL.md +1 -1
- package/skills/weaver-e10-cowork/references/cowork-write.md +3 -1
- package/skills/weaver-e10-jucailin/SKILL.md +1 -1
- package/skills/weaver-e10-jucailin/references/attend-write.md +8 -0
- 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 +2 -2
- package/skills/weaver-e10-jucailin/references/recruit-read.md +1 -1
- package/skills/weaver-e10-jucailin/references/recruit-write.md +4 -3
- package/skills/weaver-e10-mail/SKILL.md +1 -1
- package/skills/weaver-e10-mail/references/mail-send.md +2 -0
- package/skills/weaver-e10-meeting/SKILL.md +119 -26
- package/skills/weaver-e10-meeting/references/meeting-minute-file.md +25 -11
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-recruit-stage/SKILL.md +226 -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/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 +73 -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 +438 -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 +2 -2
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -2
- package/skills/weaver-e10-shijingran/SKILL.md +3 -2
- package/skills/weaver-e10-shijingran/references/operations.md +3 -1
- package/skills/weaver-e10-shijingran/references/source-manifest.json +41 -40
- package/skills/weaver-e10-shijingran/references/write-operations.md +2 -0
- package/skills/weaver-e10-workflow/SKILL.md +7 -1
- package/skills/weaver-e10-workflow/aigroup/default.json +23 -9
- package/skills/weaver-e10-workflow/references/todo-smart-convert.md +37 -21
- package/skills/weaver-e10-workflow/tools/todo-summary.cjs +135 -43
- package/skills/weaver-e10-yepiaotong/SKILL.md +9 -10
- package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +6 -3
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { rsSuccess, schemaOf } from './types.js';
|
|
2
|
+
import { rsFail, rsValidationError, rsConfirmationError } from '../errors.js';
|
|
3
|
+
import { objIdOf, formIdOf, saveFormData, runEsb, getDsOptions, cachedDsMap, getFormButtonsForData, findSubmitButton, analyzeButtonActions, getFormDataRecord, toSubmitDetails, randHex32, } from '../shared.js';
|
|
4
|
+
// InterviewStatusEnum.INVALID(已失效):系统反馈按钮(填写/编辑/代评价)均要求非此状态
|
|
5
|
+
const INVALID_STATUS_ID = '841052191228968977';
|
|
6
|
+
const R = (v) => String(v ?? '').replace(/^<p>|<\/p>$/g, '').trim();
|
|
7
|
+
async function locateTalentId(host, cache, args) {
|
|
8
|
+
if (args.talentId)
|
|
9
|
+
return String(args.talentId);
|
|
10
|
+
if (!args.mobile)
|
|
11
|
+
throw rsValidationError('locate_required', '请提供 talentId 或 mobile');
|
|
12
|
+
const listId = cache.forms?.talent_pool?.listId;
|
|
13
|
+
if (!listId)
|
|
14
|
+
throw rsFail('环境缓存缺少人才列表 listId');
|
|
15
|
+
const poolObjId = cache.forms.talent_pool.objId;
|
|
16
|
+
const r = await host.request({
|
|
17
|
+
path: '/api/ebuilder/form/list/getListDatas?passid=' + poolObjId + '&cid=' + poolObjId,
|
|
18
|
+
body: {
|
|
19
|
+
listId, cid: poolObjId, passid: poolObjId,
|
|
20
|
+
searchParamData: JSON.stringify({ searchParamList: [{ key: 'field_mobile', value: args.mobile, operator: '=' }] }),
|
|
21
|
+
current: 1, pageSize: 10, tableuid: randHex32(),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
const rows = r?.data?.rows || r?.data || [];
|
|
25
|
+
if (!rows.length)
|
|
26
|
+
throw rsFail('未找到手机号为 ' + args.mobile + ' 的候选人');
|
|
27
|
+
if (rows.length > 1)
|
|
28
|
+
throw rsFail('该手机号命中多条候选人,请提供 talentId 精确定位');
|
|
29
|
+
return String(rows[0].talentId || rows[0].form_data_id);
|
|
30
|
+
}
|
|
31
|
+
async function getBatchId(host, talentId) {
|
|
32
|
+
const rk = 'randomkey_' + randHex32().slice(0, 6);
|
|
33
|
+
const r = await host.request({ path: '/api/recruit/candidate/getCandidateCardPrepareQuery?talentId=' + talentId + '&' + rk + '=' + rk.split('_')[1], method: 'GET' });
|
|
34
|
+
return r?.data?.batchId;
|
|
35
|
+
}
|
|
36
|
+
async function listInterviewFeedback(host, talentId, batchId) {
|
|
37
|
+
const r = await host.request({
|
|
38
|
+
path: '/api/recruit/candidate/getCandidateCardInterviewInfo', method: 'POST',
|
|
39
|
+
body: { talentId, batchId, current: 1, pageSize: 20 },
|
|
40
|
+
});
|
|
41
|
+
const list = r?.data?.list || [];
|
|
42
|
+
const out = [];
|
|
43
|
+
for (let i = 0; i < list.length; i++) {
|
|
44
|
+
const it = list[i];
|
|
45
|
+
const fbs = it?.interviewFeedBack || [];
|
|
46
|
+
if (fbs.length) {
|
|
47
|
+
const fb = fbs[0];
|
|
48
|
+
out.push({
|
|
49
|
+
index: i,
|
|
50
|
+
planId: (fb.interviewPlanId || [])[0]?.id,
|
|
51
|
+
planName: (fb.interviewPlanId || [])[0]?.name || (it.subject || ''),
|
|
52
|
+
interviewer: (fb.interviewer || []).map((x) => x.name).join('、') || (it.interviewer || ''),
|
|
53
|
+
feedbackId: fb.id || fb.formDataId,
|
|
54
|
+
result: (fb.result || []).map((x) => x.name).join('、') || '',
|
|
55
|
+
judgement: R(fb.judgement),
|
|
56
|
+
unionStatusId: (it.unionStatus || [])[0]?.id || '',
|
|
57
|
+
unionStatusName: (it.unionStatus || [])[0]?.name || '',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
async function buildTarget(env, args) {
|
|
64
|
+
const host = env.host;
|
|
65
|
+
const session = await host.getSession();
|
|
66
|
+
const cache = await env.recruitContext();
|
|
67
|
+
const baseUrl = session.baseUrl.replace(/\/+$/, '');
|
|
68
|
+
const talentId = await locateTalentId(host, cache, args);
|
|
69
|
+
const batchId = await getBatchId(host, talentId);
|
|
70
|
+
const list = await listInterviewFeedback(host, talentId, batchId);
|
|
71
|
+
if (!list.length)
|
|
72
|
+
throw rsFail('该候选人没有可编辑的面试反馈记录');
|
|
73
|
+
let target;
|
|
74
|
+
if (args.feedbackId) {
|
|
75
|
+
target = list.find((x) => String(x.feedbackId) === String(args.feedbackId));
|
|
76
|
+
if (!target)
|
|
77
|
+
throw rsFail('未找到反馈记录 id=' + args.feedbackId + ',可选:' + list.map((x) => x.feedbackId).join(','));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const idx = args.planIndex !== undefined ? Number(args.planIndex) : 0;
|
|
81
|
+
if (args.planIndex !== undefined && !list[idx])
|
|
82
|
+
throw rsFail('planIndex 超出范围,共 ' + list.length + ' 场含反馈');
|
|
83
|
+
target = list[idx];
|
|
84
|
+
}
|
|
85
|
+
if (target.unionStatusId === INVALID_STATUS_ID) {
|
|
86
|
+
throw rsFail('该面试状态为「已失效」,不允许填写/编辑面试反馈(与系统按钮条件一致:已失效不显示反馈按钮)。面试=' + target.planName + ',反馈记录Id=' + target.feedbackId);
|
|
87
|
+
}
|
|
88
|
+
const objId = objIdOf(cache, 'interview_detail');
|
|
89
|
+
const formId = formIdOf(cache, 'interview_detail');
|
|
90
|
+
const fields = cache.fields?.[objId] || {};
|
|
91
|
+
const resultFid = fields.result;
|
|
92
|
+
const judgementFid = fields.judgement;
|
|
93
|
+
if (!resultFid || !judgementFid)
|
|
94
|
+
throw rsFail('环境缓存缺少面试反馈的 result/judgement 字段映射');
|
|
95
|
+
return { host, baseUrl, appId: cache.appId, cache, talentId, batchId, list, target, objId, formId, fields, resultFid, judgementFid };
|
|
96
|
+
}
|
|
97
|
+
async function prepare(args, env) {
|
|
98
|
+
if (!args.result && args.comment === undefined) {
|
|
99
|
+
throw rsValidationError('result_or_comment_required', '请提供 result(通过/不通过/待定)和/或 comment(评价)');
|
|
100
|
+
}
|
|
101
|
+
const t = await buildTarget(env, args);
|
|
102
|
+
// 反馈记录当前数据(updateTimeStamp + 全字段集)
|
|
103
|
+
const rec = await getFormDataRecord(t.host, t.formId, String(t.target.feedbackId), t.objId);
|
|
104
|
+
if (!rec.id)
|
|
105
|
+
throw rsFail('无法获取反馈记录数据(formData 为空),可能无权限或记录不存在');
|
|
106
|
+
// 粗定位提交按钮 + 精核验
|
|
107
|
+
const btns = await getFormButtonsForData(t.host, t.objId, t.formId, String(t.target.feedbackId));
|
|
108
|
+
const submitBtn = findSubmitButton(btns);
|
|
109
|
+
const analysis = submitBtn ? analyzeButtonActions(submitBtn) : null;
|
|
110
|
+
// result optionId
|
|
111
|
+
let newOptionId = null;
|
|
112
|
+
if (args.result) {
|
|
113
|
+
const opts = cachedDsMap(t.cache, t.objId, 'result') || await getDsOptions(t.host, t.objId, t.resultFid, t.appId);
|
|
114
|
+
newOptionId = opts[String(args.result)];
|
|
115
|
+
if (!newOptionId)
|
|
116
|
+
throw rsFail('未识别的面试结果「' + args.result + '」,可选:' + Object.keys(opts).join('/'));
|
|
117
|
+
}
|
|
118
|
+
const newComment = args.comment !== undefined ? String(args.comment) : t.target.judgement;
|
|
119
|
+
const newResultName = args.result ? String(args.result) : t.target.result;
|
|
120
|
+
const willSubmit = { feedbackId: String(t.target.feedbackId), result: newResultName, comment: newComment };
|
|
121
|
+
const { createHash } = await import('node:crypto');
|
|
122
|
+
const fp = createHash('sha256').update(JSON.stringify(willSubmit)).digest('hex');
|
|
123
|
+
const continuation = await env.continuation.issue({ operation: 'recruit-stage.interview.feedback.edit.apply', fingerprint: fp });
|
|
124
|
+
return rsSuccess('recruit-stage.interview.feedback.edit.prepare', {
|
|
125
|
+
mode: 'prepare',
|
|
126
|
+
talentId: t.talentId,
|
|
127
|
+
batchId: t.batchId,
|
|
128
|
+
面试: t.target.planName,
|
|
129
|
+
面试官: t.target.interviewer,
|
|
130
|
+
反馈记录Id: t.target.feedbackId,
|
|
131
|
+
当前反馈: (t.target.result ? '结果 ' + t.target.result : '') + (t.target.judgement ? ';评价 ' + t.target.judgement : ''),
|
|
132
|
+
新反馈: (args.result ? '结果 ' + newResultName : '') + (args.comment !== undefined ? ';评价 ' + newComment : ''),
|
|
133
|
+
将执行动作: analysis
|
|
134
|
+
? {
|
|
135
|
+
保存反馈表单: 'saveFormData(全字段集 + updateTimeStamp 乐观锁)',
|
|
136
|
+
动作流: analysis.esbIds.length ? analysis.esbIds : '(该按钮无 esb 动作流)',
|
|
137
|
+
招聘API: analysis.jsCalls.map((c) => c.url + (Object.keys(c.data).length ? ' data=' + Object.keys(c.data).join(',') : '')),
|
|
138
|
+
}
|
|
139
|
+
: '⚠️ 未定位到「提交评价」按钮(getFormButtons 未取到保存按钮),仅能执行保存+写过程日志',
|
|
140
|
+
willSubmit,
|
|
141
|
+
}, { continuation, fingerprint: fp, confirmHint: '用户明确确认后,携带 confirm:true 与 continuation 调用 apply' });
|
|
142
|
+
}
|
|
143
|
+
async function apply(args, env) {
|
|
144
|
+
if (args.confirm !== true)
|
|
145
|
+
throw rsConfirmationError('recruit-stage.interview.feedback.edit.apply', { hint: '写操作必须显式 confirm:true' });
|
|
146
|
+
if (!args.continuation)
|
|
147
|
+
throw rsValidationError('continuation_required', '缺少 continuation(来自 prepare 的 meta)');
|
|
148
|
+
const payload = await env.continuation.verify(String(args.continuation), 'recruit-stage.interview.feedback.edit.apply');
|
|
149
|
+
const t = await buildTarget(env, args);
|
|
150
|
+
// 重新取最新 updateTimeStamp(乐观锁)
|
|
151
|
+
const rec2 = await getFormDataRecord(t.host, t.formId, String(t.target.feedbackId), t.objId);
|
|
152
|
+
const details2 = toSubmitDetails(rec2.dataDetails);
|
|
153
|
+
let newOptionId = null;
|
|
154
|
+
if (args.result) {
|
|
155
|
+
const opts = cachedDsMap(t.cache, t.objId, 'result') || await getDsOptions(t.host, t.objId, t.resultFid, t.appId);
|
|
156
|
+
newOptionId = opts[String(args.result)];
|
|
157
|
+
if (!newOptionId)
|
|
158
|
+
throw rsFail('未识别的面试结果「' + args.result + '」,可选:' + Object.keys(opts).join('/'));
|
|
159
|
+
}
|
|
160
|
+
const newComment = args.comment !== undefined ? String(args.comment) : t.target.judgement;
|
|
161
|
+
const newResultName = args.result ? String(args.result) : t.target.result;
|
|
162
|
+
const willSubmit = { feedbackId: String(t.target.feedbackId), result: newResultName, comment: newComment };
|
|
163
|
+
const { createHash } = await import('node:crypto');
|
|
164
|
+
if (payload.fingerprint !== createHash('sha256').update(JSON.stringify(willSubmit)).digest('hex')) {
|
|
165
|
+
throw rsValidationError('fingerprint_mismatch', '待提交内容与 prepare 确认摘要不一致,请重新 prepare 确认后再 apply');
|
|
166
|
+
}
|
|
167
|
+
// 覆盖 result/judgement 字段(原详情缺失则追加,避免整条提交时字段不写入)
|
|
168
|
+
const newDetails = details2.map((x) => {
|
|
169
|
+
const fid = x.formField.id;
|
|
170
|
+
if (fid === t.resultFid && newOptionId)
|
|
171
|
+
return { formField: { id: fid }, dataOptions: [{ optionId: newOptionId, content: newResultName }] };
|
|
172
|
+
if (fid === t.judgementFid)
|
|
173
|
+
return { formField: { id: fid }, dataText: { content: '<p>' + (newComment || '') + '</p>' } };
|
|
174
|
+
return x;
|
|
175
|
+
});
|
|
176
|
+
if (newOptionId && !newDetails.some((x) => x.formField.id === t.resultFid)) {
|
|
177
|
+
newDetails.push({ formField: { id: t.resultFid }, dataOptions: [{ optionId: newOptionId, content: newResultName }] });
|
|
178
|
+
}
|
|
179
|
+
if (args.comment !== undefined && !newDetails.some((x) => x.formField.id === t.judgementFid)) {
|
|
180
|
+
newDetails.push({ formField: { id: t.judgementFid }, dataText: { content: '<p>' + (newComment || '') + '</p>' } });
|
|
181
|
+
}
|
|
182
|
+
// saveFormData(layoutId 用 getFormDataAndLayout 的 formLayout.id,getFormLayout 405 不可靠)
|
|
183
|
+
const savedId = await saveFormData(t.host, {
|
|
184
|
+
appId: t.appId, formId: t.formId, objId: t.objId,
|
|
185
|
+
name: '面试反馈-' + t.target.planName, formDataId: String(t.target.feedbackId),
|
|
186
|
+
updateTimeStamp: rec2.updateTimeStamp, dataDetails: newDetails, rightType: '2',
|
|
187
|
+
layoutId: rec2.formLayoutId,
|
|
188
|
+
extraCustomParam: { secretLevel: '', secretLevelValidity: '' },
|
|
189
|
+
});
|
|
190
|
+
// runEsb 动作流(按钮配置解析)
|
|
191
|
+
const btns = await getFormButtonsForData(t.host, t.objId, t.formId, String(t.target.feedbackId));
|
|
192
|
+
const submitBtn = findSubmitButton(btns);
|
|
193
|
+
const analysis = submitBtn ? analyzeButtonActions(submitBtn) : null;
|
|
194
|
+
let esbRes = null;
|
|
195
|
+
if (analysis?.esbIds?.length) {
|
|
196
|
+
esbRes = await runEsb(t.host, { objId: t.objId, dataIds: String(t.target.feedbackId), esbActionIds: analysis.esbIds.join(',') });
|
|
197
|
+
}
|
|
198
|
+
// addProcessLog 写招聘过程日志(jsCode 里的招聘 API,按标准契约直接调)
|
|
199
|
+
let processRes = null;
|
|
200
|
+
if (analysis?.jsCalls?.length) {
|
|
201
|
+
const call = analysis.jsCalls.find((c) => c.url.includes('/api/recruit/')) || analysis.jsCalls[0];
|
|
202
|
+
processRes = await t.host.request({
|
|
203
|
+
path: call.url, method: (call.method || 'post').toUpperCase(),
|
|
204
|
+
body: {
|
|
205
|
+
talentId: String(t.talentId), batchId: String(t.batchId),
|
|
206
|
+
result: newOptionId || t.target.result, judgement: '<p>' + (newComment || '') + '</p>',
|
|
207
|
+
businessId: String(t.target.feedbackId), processEnum: '20',
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// 回查校验
|
|
212
|
+
const back = await listInterviewFeedback(t.host, t.talentId, t.batchId);
|
|
213
|
+
const backFb = back.find((x) => String(x.feedbackId) === String(t.target.feedbackId));
|
|
214
|
+
const changed = backFb && String(backFb.result).includes(newResultName) && (args.comment === undefined || String(backFb.judgement).includes(newComment));
|
|
215
|
+
// 产物链接:人才卡片(点进去可看该候选人的面试与反馈)+ 面试管理列表
|
|
216
|
+
const cardPageId = String(t.cache.ebdDetailPageId || '');
|
|
217
|
+
const talentExtra = String(t.cache.selfUse) === '1' ? '&recruitType=useSelf' : '';
|
|
218
|
+
const talentCardUrl = cardPageId ? `${t.baseUrl}/sp/ebdpage/view/${cardPageId}?talentId=${t.talentId}${talentExtra}` : null;
|
|
219
|
+
const interviewListId = String((t.cache.lists?.interview?.listId) || '');
|
|
220
|
+
const interviewListUrl = interviewListId ? `${t.baseUrl}/sp/ebdfpage/list/${interviewListId}` : null;
|
|
221
|
+
return rsSuccess('recruit-stage.interview.feedback.edit.apply', {
|
|
222
|
+
mode: 'apply', result: 'success',
|
|
223
|
+
saveFormData: { 成功: !!savedId, 记录Id: savedId },
|
|
224
|
+
runEsb: esbRes ? { 动作流数: analysis?.esbIds.length ?? 0, 返回: esbRes?.successNum ?? esbRes?.status ?? 'ok' } : '无 esb 动作流',
|
|
225
|
+
addProcessLog: processRes ? { 返回: processRes?.data ?? processRes?.status ?? 'ok' } : '无 jsCode 招聘 API',
|
|
226
|
+
回查: backFb ? { 结果: backFb.result, 评价: backFb.judgement, 已生效: changed } : '回查未取到该反馈',
|
|
227
|
+
talentId: t.talentId, batchId: t.batchId, 面试: t.target.planName,
|
|
228
|
+
talentCardUrl,
|
|
229
|
+
interviewListUrl,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
export const interviewFeedbackOperations = [
|
|
233
|
+
{
|
|
234
|
+
name: 'recruit-stage.interview.feedback.edit.prepare',
|
|
235
|
+
resource: 'recruit.interview',
|
|
236
|
+
method: 'prepare',
|
|
237
|
+
action: 'interview.feedback.edit.prepare',
|
|
238
|
+
summary: '面试反馈确认摘要:定位反馈记录(feedbackId 或 planIndex)、按钮动作智能解析(保存+esb+招聘API)、当前/新反馈对照,不写入',
|
|
239
|
+
capability: 'recruit.interview.feedback',
|
|
240
|
+
risk: 'write',
|
|
241
|
+
requiresConfirmation: true,
|
|
242
|
+
inputSchema: schemaOf({
|
|
243
|
+
talentId: { type: 'string', description: '人才 id(与 mobile 二选一)' },
|
|
244
|
+
mobile: { type: 'string', pattern: '^\\d{6,20}$', description: '手机号定位(唯一命中)' },
|
|
245
|
+
feedbackId: { type: 'string', description: '反馈记录 id(最稳;缺省用 planIndex 选第几场)' },
|
|
246
|
+
planIndex: { type: 'integer', minimum: 0, description: '第几场含反馈的面试(默认 0)' },
|
|
247
|
+
result: { type: 'string', description: '面试结果(如 通过/不通过/待定,按环境枚举)' },
|
|
248
|
+
comment: { type: 'string', description: '评价正文' },
|
|
249
|
+
}, {}),
|
|
250
|
+
handler: prepare,
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: 'recruit-stage.interview.feedback.edit.apply',
|
|
254
|
+
resource: 'recruit.interview',
|
|
255
|
+
method: 'apply',
|
|
256
|
+
action: 'interview.feedback.edit.apply',
|
|
257
|
+
summary: '用户确认后提交反馈:saveFormData(乐观锁全字段集)→ runEsb 动作流 → addProcessLog 过程日志 → 回查校验',
|
|
258
|
+
capability: 'recruit.interview.feedback',
|
|
259
|
+
risk: 'write',
|
|
260
|
+
requiresConfirmation: true,
|
|
261
|
+
inputSchema: schemaOf({
|
|
262
|
+
talentId: { type: 'string' }, mobile: { type: 'string', pattern: '^\\d{6,20}$' },
|
|
263
|
+
feedbackId: { type: 'string' }, planIndex: { type: 'integer', minimum: 0 },
|
|
264
|
+
result: { type: 'string' }, comment: { type: 'string' },
|
|
265
|
+
confirm: { type: 'boolean' }, continuation: { type: 'string' },
|
|
266
|
+
}, {}),
|
|
267
|
+
handler: apply,
|
|
268
|
+
},
|
|
269
|
+
];
|