weaver-work-cli 0.1.29 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/dist/chunks/archive-U766FTFH.js +2537 -0
  2. package/dist/chunks/archive-oa-DMWEKX3L.js +1317 -0
  3. package/dist/chunks/asset-TCR7VDEV.js +2109 -0
  4. package/dist/chunks/attachment-content-AQNX5N3S.js +23 -0
  5. package/dist/chunks/blog-EVZWGDVM.js +2157 -0
  6. package/dist/chunks/caizhilian-JOWS6SL5.js +10087 -0
  7. package/dist/chunks/calendar-5WNCV66Q.js +1195 -0
  8. package/dist/chunks/chunk-2DUV44UX.js +15606 -0
  9. package/dist/chunks/chunk-324YPTC2.js +1607 -0
  10. package/dist/chunks/chunk-65QMVWHS.js +231 -0
  11. package/dist/chunks/chunk-6V7ZLF34.js +387 -0
  12. package/dist/chunks/chunk-AC7KJWC5.js +358 -0
  13. package/dist/chunks/chunk-AJ62BGAW.js +29 -0
  14. package/dist/chunks/chunk-BUSC6UAE.js +43 -0
  15. package/dist/chunks/chunk-CVZ2JOSW.js +62 -0
  16. package/dist/chunks/chunk-D4LTJSJD.js +477 -0
  17. package/dist/chunks/chunk-EG6ZU444.js +654 -0
  18. package/dist/chunks/chunk-EZALDOQH.js +383 -0
  19. package/dist/chunks/chunk-FMPJJYYI.js +106 -0
  20. package/dist/chunks/chunk-G2JSIUWF.js +769 -0
  21. package/dist/chunks/chunk-GCG2NMWR.js +703 -0
  22. package/dist/chunks/chunk-GPMFD4RD.js +75 -0
  23. package/dist/chunks/chunk-H3WDBYJW.js +508 -0
  24. package/dist/chunks/chunk-H6XSTSLM.js +2357 -0
  25. package/dist/chunks/chunk-J7RJTZSV.js +5556 -0
  26. package/dist/chunks/chunk-LUSPKZKY.js +53 -0
  27. package/dist/chunks/chunk-NIAJ5ZCG.js +73 -0
  28. package/dist/chunks/chunk-NOODWMCL.js +584 -0
  29. package/dist/chunks/chunk-OOUTBLZ4.js +51 -0
  30. package/dist/chunks/chunk-P26ZWSKU.js +73 -0
  31. package/dist/chunks/chunk-QWY6PGL6.js +3472 -0
  32. package/dist/chunks/chunk-R2IJAK7T.js +464 -0
  33. package/dist/chunks/chunk-R7O5PUWO.js +205 -0
  34. package/dist/chunks/chunk-RCGYCOMG.js +6962 -0
  35. package/dist/chunks/chunk-VZTXRG6X.js +403 -0
  36. package/dist/chunks/chunk-WHD4XMRP.js +161 -0
  37. package/dist/chunks/chunk-YLCYX7LE.js +371 -0
  38. package/dist/chunks/chunk-ZEDWSH32.js +68 -0
  39. package/dist/chunks/continuation-2CKSBLA6.js +11 -0
  40. package/dist/chunks/continuation-HZIZRZBX.js +13 -0
  41. package/dist/chunks/continuation-NLECEJPX.js +11 -0
  42. package/dist/chunks/ebuilder-form-NJ4YK6CC.js +2276 -0
  43. package/dist/chunks/ehr-MMYN36VA.js +5241 -0
  44. package/dist/chunks/employee-SAK2QYXX.js +11 -0
  45. package/dist/chunks/esb-HIWHOU7Q.js +141 -0
  46. package/dist/chunks/fna-KBACQTA5.js +1444 -0
  47. package/dist/chunks/host-3OHS5QUX.js +21 -0
  48. package/dist/chunks/host-IG6KEH5Q.js +19 -0
  49. package/dist/chunks/host-KQL5N4HO.js +23 -0
  50. package/dist/chunks/host-LLSZQCWR.js +17 -0
  51. package/dist/chunks/hrm-LYEC67ZQ.js +244 -0
  52. package/dist/chunks/im-RN2UKAEU.js +252 -0
  53. package/dist/chunks/invoice-EEPHLAF6.js +5179 -0
  54. package/dist/chunks/jiuchuanhui-WVVZYSJE.js +1940 -0
  55. package/dist/chunks/lazy-dispatch-MDOFFL2S.js +28 -0
  56. package/dist/chunks/mail-LNQLTF6L.js +2695 -0
  57. package/dist/chunks/meeting-XSD3BDVQ.js +504 -0
  58. package/dist/chunks/msg-parse-4GSQ2WTO.js +41 -0
  59. package/dist/chunks/okr-TMWTIU2K.js +2482 -0
  60. package/dist/chunks/plan-A2FSFA7T.js +2025 -0
  61. package/dist/chunks/project-T26QLBQF.js +1361 -0
  62. package/dist/chunks/registry-2VLUAESA.js +14 -0
  63. package/dist/chunks/registry-JCFEYXOE.js +18 -0
  64. package/dist/chunks/registry-NYPYMZXG.js +16 -0
  65. package/dist/chunks/registry-Q7OQJ3SU.js +19 -0
  66. package/dist/chunks/registry-ZJMJVWFA.js +19 -0
  67. package/dist/chunks/room-recommend-UR4ZDZS6.js +20 -0
  68. package/dist/chunks/schedule-5L2PM4M6.js +24 -0
  69. package/dist/chunks/state-Y2XBP6YE.js +15 -0
  70. package/dist/chunks/workflow-TR6PG2SL.js +245 -0
  71. package/dist/cmd/argv.js +45 -0
  72. package/dist/cmd/docs/index.js +3 -3
  73. package/dist/cmd/lazy-dispatch.js +20 -0
  74. package/dist/cmd/root.js +2 -4
  75. package/dist/cmd/setup/index.js +2 -2
  76. package/dist/cmd/skills/index.js +2 -2
  77. package/dist/core/package-paths.js +47 -0
  78. package/dist/index.js +163 -7
  79. package/dist/internal/e10/auth/xiaoe.js +3 -3
  80. package/dist/internal/e10/request-runtime.js +18 -62
  81. package/dist/internal/skills/detector.js +2 -2
  82. package/dist/shortcuts/contract/data/performance-catalog.js +1306 -0
  83. package/dist/shortcuts/contract/host.js +91 -35
  84. package/dist/shortcuts/contract/index.js +213 -0
  85. package/dist/shortcuts/contract/manifest.js +928 -0
  86. package/dist/shortcuts/contract/ooxml/docx.js +295 -0
  87. package/dist/shortcuts/contract/ooxml/xlsx.js +221 -0
  88. package/dist/shortcuts/contract/ooxml/zip.js +158 -0
  89. package/dist/shortcuts/contract/operations/assistant.js +1156 -0
  90. package/dist/shortcuts/contract/operations/clause-library.js +522 -0
  91. package/dist/shortcuts/contract/operations/compare.js +193 -0
  92. package/dist/shortcuts/contract/operations/credit-page.js +34 -0
  93. package/dist/shortcuts/contract/operations/flow.js +98 -0
  94. package/dist/shortcuts/contract/operations/page.js +66 -0
  95. package/dist/shortcuts/contract/operations/performance.js +733 -0
  96. package/dist/shortcuts/contract/operations/quote.js +557 -0
  97. package/dist/shortcuts/contract/operations/recommend.js +78 -0
  98. package/dist/shortcuts/contract/operations/registry.js +18 -0
  99. package/dist/shortcuts/contract/operations/review.js +83 -0
  100. package/dist/shortcuts/contract/operations/shared.js +37 -0
  101. package/dist/shortcuts/contract/operations/upload.js +108 -0
  102. package/dist/shortcuts/contract/operations/write.js +3 -2
  103. package/dist/shortcuts/index.js +140 -40
  104. package/dist/shortcuts/invoice/manifest.js +34 -11
  105. package/dist/shortcuts/invoice/operations/reim-auto.js +9 -0
  106. package/dist/shortcuts/invoice/operations/reim.js +81 -5
  107. package/dist/shortcuts/invoice/operations/shared.js +12 -5
  108. package/dist/shortcuts/meeting/host.js +45 -3
  109. package/dist/shortcuts/meeting/invitation/docx.js +442 -0
  110. package/dist/shortcuts/meeting/operations/attachment-content.js +418 -0
  111. package/dist/shortcuts/meeting/operations/attachment.js +442 -0
  112. package/dist/shortcuts/meeting/operations/eb/cache.js +57 -0
  113. package/dist/shortcuts/meeting/operations/eb/catalog.js +343 -28
  114. package/dist/shortcuts/meeting/operations/eb/conflict.js +372 -11
  115. package/dist/shortcuts/meeting/operations/eb/create-flow.js +710 -0
  116. package/dist/shortcuts/meeting/operations/eb/decision.js +47 -1
  117. package/dist/shortcuts/meeting/operations/eb/factory.js +129 -11
  118. package/dist/shortcuts/meeting/operations/eb/index.js +122 -2
  119. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +378 -0
  120. package/dist/shortcuts/meeting/operations/eb/room-summary.js +84 -0
  121. package/dist/shortcuts/meeting/operations/eb/schedule.js +450 -0
  122. package/dist/shortcuts/meeting/operations/from-group-chat.js +576 -0
  123. package/dist/shortcuts/meeting/operations/invitation.js +458 -0
  124. package/dist/shortcuts/meeting/operations/minute-content.js +266 -0
  125. package/dist/shortcuts/meeting/operations/pipeline.js +489 -0
  126. package/dist/shortcuts/meeting/operations/qrcode.js +131 -0
  127. package/dist/shortcuts/meeting/operations/receipts.js +1 -1
  128. package/dist/shortcuts/meeting/operations/registry.js +18 -0
  129. package/dist/shortcuts/meeting/operations/summary.js +276 -0
  130. package/dist/shortcuts/meeting/operations/task-notify.js +325 -0
  131. package/dist/shortcuts/meeting/operations.js +42 -1
  132. package/dist/shortcuts/meeting/qr/encoder.js +427 -0
  133. package/dist/shortcuts/meeting/qr/render.js +103 -0
  134. package/dist/shortcuts/workflow/operations/form.js +17 -5
  135. package/docs/contract.md +39 -4
  136. package/docs/invoice.md +5 -5
  137. package/package.json +3 -2
  138. package/scripts/bundle.mjs +63 -0
  139. package/skill-template/business-info.json +25 -4
  140. package/skill-template/domains/jinchengda.md +16 -3
  141. package/skill-template/domains/yepiaotong.md +1 -1
  142. package/skills/weaver-e10-jinchengda/SKILL.md +111 -31
  143. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +7 -13
  144. package/skills/weaver-e10-jinchengda/references/contract-ai-assistant.md +150 -0
  145. package/skills/weaver-e10-jinchengda/references/contract-ai-review.md +87 -0
  146. package/skills/weaver-e10-jinchengda/references/contract-clause-library-crud.md +111 -0
  147. package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +0 -15
  148. package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +0 -13
  149. package/skills/weaver-e10-jinchengda/references/contract-credit-page.md +50 -0
  150. package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +4 -2
  151. package/skills/weaver-e10-jinchengda/references/contract-doc-compare.md +74 -0
  152. package/skills/weaver-e10-jinchengda/references/contract-flow-launch.md +84 -0
  153. package/skills/weaver-e10-jinchengda/references/contract-performance-query.md +29 -0
  154. package/skills/weaver-e10-jinchengda/references/contract-quote-to-contract.md +28 -0
  155. package/skills/weaver-e10-jinchengda/references/contract-request-status.md +2 -21
  156. package/skills/weaver-e10-jinchengda/references/contract-request-write.md +0 -24
  157. package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +0 -15
  158. package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +0 -17
  159. package/skills/weaver-e10-jinchengda/references/contract-template-recommend.md +70 -0
  160. package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +0 -15
  161. package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +0 -15
  162. package/skills/weaver-e10-jinchengda/references/source-manifest.json +1168 -152
  163. package/skills/weaver-e10-meeting/SKILL.md +493 -17
  164. package/skills/weaver-e10-meeting/references/meeting-change.md +4 -4
  165. package/skills/weaver-e10-meeting/references/meeting-create.md +6 -6
  166. package/skills/weaver-e10-meeting/references/meeting-eb.md +503 -27
  167. package/skills/weaver-e10-meeting/references/meeting-invitation.md +113 -0
  168. package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +6 -6
  169. package/skills/weaver-e10-meeting/references/meeting-minute-file.md +425 -0
  170. package/skills/weaver-e10-meeting/references/meeting-queries.md +4 -4
  171. package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +15 -15
  172. package/skills/weaver-e10-meeting/references/meeting-verification.md +19 -11
  173. package/skills/weaver-e10-meeting.zip +0 -0
  174. package/skills/weaver-e10-yepiaotong/SKILL.md +9 -5
  175. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +1 -1
  176. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +6 -5
  177. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +14 -11
  178. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +97 -56
  179. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +84 -60
  180. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +142 -105
  181. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +53 -194
  182. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +7 -7
  183. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +164 -72
  184. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +6 -0
  185. package/templates/meeting/invitation.docx +0 -0
@@ -0,0 +1,450 @@
1
+ import { meetingSuccess, schemaOf, SCHEMA_DATETIME_MINUTE } from '../types.js';
2
+ import { meetingValidationError } from '../../errors.js';
3
+ import { readCache, writeCache } from './cache.js';
4
+ /**
5
+ * 参会人「日程空闲」检测(EB 版)。
6
+ *
7
+ * 与 `meeting.eb.conflict.member` 的区别(这是两件事,别混):
8
+ * - `conflict.member`:查**会议表**,判断这个人在这段时间是不是已经有**其它会议**(回执表比对);
9
+ * - `schedule.free`(本文件):查**日程应用**,判断这个人在这段时间有没有**任何日程占用**(含外出、出差、任务等)。
10
+ *
11
+ * 数据来源:ESB 动作流(用户 2026-10-28 指定,primary_key=E37BF827FF2D47F6BD1DF928ABD25F69 /
12
+ * esbFlowId=1328057613897310208),入参 `customParams.mainTable = {startDate, endDate, memberIds, mtId?}`。
13
+ * 走 ESB 而不是直连 Java 接口,是为了和其他 ESB 能力保持同一条链路(权限/日志都在 ESB 侧)。
14
+ *
15
+ * ⛔ 必须过滤「会议日程」类型:会议冲突已由 `conflict.member` 判过,日程里再由会议生成的日程
16
+ * 重复计入会导致「同一场会议被报两次冲突」。会议日程的类型 id **每个环境都不同**
17
+ * (188 环境是 1147539996611665922),所以一律**动态解析**,绝不写死:
18
+ * 入参 excludeTypeIds → 环境变量 WEAVER_MEETING_SCHEDULE_TYPE_IDS
19
+ * → 本地缓存 → 调日程类型字典(按 type_name=会议日程 / type_code=HYRC 匹配)
20
+ */
21
+ /** 日程类型字典(日程应用 queryAgendaType)。 */
22
+ const SCHEDULE_TYPE_PATH = '/api/ebuilder/form/formdata/v2/getFormDataList/queryAgendaType';
23
+ /** 用户指定的日程冲突动作流(可在入参里覆盖,避免换环境后失效)。 */
24
+ const DEFAULT_SCHEDULE_FLOW_ID = '1328057613897310208';
25
+ const DEFAULT_SCHEDULE_UNIQUE_INDENT = 'E37BF827FF2D47F6BD1DF928ABD25F69';
26
+ /** 会议日程类型在字典里的识别依据:名称优先,名称被改过时用内置 type_code 兜底。 */
27
+ const DEFAULT_MEETING_TYPE_NAME = '会议日程';
28
+ const MEETING_TYPE_CODE = 'HYRC';
29
+ const TYPE_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
30
+ function splitIds(value) {
31
+ if (value === undefined || value === null)
32
+ return [];
33
+ if (Array.isArray(value)) {
34
+ return value.map((item) => String(item).trim()).filter((item) => item !== '');
35
+ }
36
+ return String(value)
37
+ .split(/[,;,;\s]+/)
38
+ .map((item) => item.trim())
39
+ .filter((item) => item !== '');
40
+ }
41
+ /** 读日程类型字典(一次性全量,用于动态定位「会议日程」类型 id)。 */
42
+ async function fetchScheduleTypeOptions(env) {
43
+ const session = await env.host.getSession();
44
+ const response = await env.host.request({
45
+ method: 'POST',
46
+ path: SCHEDULE_TYPE_PATH,
47
+ operation: 'meeting.eb.schedule.free',
48
+ body: {
49
+ datajson: {
50
+ mainTable: {},
51
+ pageInfo: { pageNo: '1', pageSize: '100' },
52
+ header: { operator: session.userId },
53
+ },
54
+ },
55
+ acceptBusiness: (value) => {
56
+ if (!value || typeof value !== 'object')
57
+ return false;
58
+ if (typeof value.status === 'boolean')
59
+ return value.status;
60
+ return Number(value.code ?? 200) === 200;
61
+ },
62
+ });
63
+ const value = response;
64
+ const dj = value?.datajson ?? value?.data ?? {};
65
+ const raw = dj?.datas ?? dj?.data ?? (Array.isArray(dj) ? dj : []);
66
+ const rows = Array.isArray(raw) ? raw : [];
67
+ return rows
68
+ .map((row) => {
69
+ const main = row?.mainTable ?? row ?? {};
70
+ return {
71
+ id: String(main.id ?? '').trim(),
72
+ name: String(main.type_name ?? main.name ?? '').trim(),
73
+ code: String(main.type_code ?? '').trim().toUpperCase(),
74
+ };
75
+ })
76
+ .filter((item) => item.id !== '');
77
+ }
78
+ /** 解析「会议日程」类型 id(动态,不写死)。 */
79
+ export async function resolveMeetingScheduleTypeIds(env, options = {}) {
80
+ const fromArgs = splitIds(options.excludeTypeIds);
81
+ if (fromArgs.length > 0) {
82
+ return { ids: fromArgs, names: [], source: 'args', warnings: [] };
83
+ }
84
+ const fromEnv = splitIds(process.env.WEAVER_MEETING_SCHEDULE_TYPE_IDS);
85
+ if (fromEnv.length > 0) {
86
+ return { ids: fromEnv, names: [], source: 'env', warnings: [] };
87
+ }
88
+ const cacheKey = `schedule-types-${await scopeSuffix(env)}`;
89
+ if (!options.refresh) {
90
+ const cached = await readCache(env, cacheKey, { ttlMs: TYPE_CACHE_TTL_MS });
91
+ if (cached?.data?.meetingTypeIds?.length) {
92
+ return { ids: cached.data.meetingTypeIds, names: cached.data.names ?? [], source: 'cache', warnings: [] };
93
+ }
94
+ }
95
+ const warnings = [];
96
+ try {
97
+ const typeName = (process.env.WEAVER_MEETING_SCHEDULE_TYPE_NAME || DEFAULT_MEETING_TYPE_NAME).trim();
98
+ const options2 = await fetchScheduleTypeOptions(env);
99
+ const matched = options2.filter((item) => item.name === typeName || item.code === MEETING_TYPE_CODE);
100
+ if (matched.length === 0) {
101
+ warnings.push(`日程类型字典里没找到「${typeName}」(也没有 type_code=${MEETING_TYPE_CODE} 的项)——`
102
+ + '本次不做会议日程过滤,冲突里可能包含会议生成的日程(会与会议冲突重复计)。'
103
+ + '可用入参 excludeTypeIds 或环境变量 WEAVER_MEETING_SCHEDULE_TYPE_IDS 显式指定。');
104
+ return { ids: [], names: [], source: 'none', warnings };
105
+ }
106
+ const ids = matched.map((item) => item.id);
107
+ const names = matched.map((item) => item.name);
108
+ await writeCache(env, cacheKey, { meetingTypeIds: ids, names });
109
+ return { ids, names, source: 'remote', warnings };
110
+ }
111
+ catch (error) {
112
+ warnings.push(`日程类型字典读取失败(${String(error?.message ?? error).slice(0, 80)}):`
113
+ + '本次不做会议日程过滤,请人工判断是否属于重复计入。');
114
+ return { ids: [], names: [], source: 'none', warnings };
115
+ }
116
+ }
117
+ async function scopeSuffix(env) {
118
+ try {
119
+ const session = await env.host.getSession();
120
+ const host = String(session?.baseUrl ?? '').replace(/^https?:\/\//, '').replace(/[^0-9a-zA-Z]+/g, '-');
121
+ return `${host || 'local'}-${String(session?.tenantKey ?? 'unknown')}`;
122
+ }
123
+ catch {
124
+ return 'local-unknown';
125
+ }
126
+ }
127
+ /** 触发 ESB 日程冲突动作流,拿到原始返回(超时/失败不重试,交给调用方判定)。 */
128
+ export async function runScheduleConflictFlow(env, input) {
129
+ const session = await env.host.getSession();
130
+ const employeeId = String(session?.userId ?? '').trim();
131
+ if (!employeeId) {
132
+ throw meetingValidationError('schedule_employee_required', '取不到当前登录用户 id,无法触发 ESB 日程查询');
133
+ }
134
+ const { createEsbHost } = await import('../../../esb/host.js');
135
+ const { createEsbContinuationSigner } = await import('../../../esb/continuation.js');
136
+ const { getEsbOperation } = await import('../../../esb/operations/registry.js');
137
+ const esbEnv = {
138
+ ctx: env.ctx,
139
+ host: createEsbHost(env.ctx),
140
+ continuation: createEsbContinuationSigner(env.ctx),
141
+ };
142
+ const mainTable = {
143
+ startDate: input.startDate,
144
+ endDate: input.endDate,
145
+ memberIds: input.memberIds.join(','),
146
+ };
147
+ if (input.mtId)
148
+ mainTable.mtId = input.mtId;
149
+ // 触发方式二选一:优先用 primary_key(用户 2026-10-28 指定的唯一值触发),
150
+ // 也支持显式传 flowId 走普通触发(同一动作流,换环境时更省事)。
151
+ const explicitFlowId = String(input.flowId ?? '').trim() || process.env.WEAVER_MEETING_SCHEDULE_FLOW_ID || '';
152
+ const uniqueIndent = String(input.uniqueIndent ?? '').trim()
153
+ || process.env.WEAVER_MEETING_SCHEDULE_FLOW_KEY
154
+ || DEFAULT_SCHEDULE_UNIQUE_INDENT;
155
+ const resolvedTarget = explicitFlowId
156
+ ? { esbFlowId: explicitFlowId }
157
+ : (uniqueIndent ? { uniqueIndent } : { esbFlowId: DEFAULT_SCHEDULE_FLOW_ID });
158
+ const prepareOp = getEsbOperation('esb.trigger.prepare');
159
+ const applyOp = getEsbOperation('esb.trigger.apply');
160
+ const pre = await prepareOp.handler({ employeeId, ...resolvedTarget, customParams: { mainTable } }, esbEnv);
161
+ const applied = await applyOp.handler({ continuation: pre?.data?.continuation, confirm: true }, esbEnv);
162
+ const actionData = applied?.data?.actionData ?? {};
163
+ const main = actionData?.responseData?.customData?.mainTable ?? {};
164
+ const rowsFrom = (value) => {
165
+ if (Array.isArray(value))
166
+ return value;
167
+ return [];
168
+ };
169
+ const rows = rowsFrom(main.memberWpConflictData)
170
+ .concat(rowsFrom(main.wpConflictData))
171
+ .concat(rowsFrom(main.conflictList))
172
+ .concat(rowsFrom(main.scheduleConflicts));
173
+ const count = Number(main.memberWpConflictNum ?? main.conflictNum ?? rows.length) || 0;
174
+ const message = String(main.memberWpConflictMsg ?? main.conflictMsg ?? '');
175
+ return {
176
+ rows,
177
+ count,
178
+ message,
179
+ raw: applied?.data ?? applied,
180
+ target: {
181
+ mode: explicitFlowId ? 'esbFlowId' : 'uniqueIndent',
182
+ value: explicitFlowId || uniqueIndent,
183
+ },
184
+ };
185
+ }
186
+ function normalizeTypeId(value) {
187
+ if (value === undefined || value === null)
188
+ return { id: '', name: '' };
189
+ if (Array.isArray(value)) {
190
+ const first = value[0];
191
+ if (first && typeof first === 'object') {
192
+ return { id: String(first.id ?? '').trim(), name: String(first.name ?? '').trim() };
193
+ }
194
+ return { id: String(first ?? '').trim(), name: '' };
195
+ }
196
+ if (typeof value === 'object') {
197
+ const item = value;
198
+ return { id: String(item.id ?? '').trim(), name: String(item.name ?? '').trim() };
199
+ }
200
+ return { id: String(value).trim(), name: '' };
201
+ }
202
+ function normalizeMembers(value) {
203
+ if (Array.isArray(value)) {
204
+ return value
205
+ .map((item) => (item && typeof item === 'object' ? String(item.id ?? '') : String(item ?? '')))
206
+ .map((item) => item.trim())
207
+ .filter((item) => item !== '');
208
+ }
209
+ return splitIds(value);
210
+ }
211
+ function normalizeRows(rows) {
212
+ return rows.map((row) => {
213
+ const main = row?.mainTable ?? row ?? {};
214
+ const type = normalizeTypeId(main.agn_type);
215
+ return {
216
+ scheduleId: String(main.id ?? '').trim(),
217
+ title: String(main.agn_name ?? main.name ?? '').trim(),
218
+ startTime: String(main.start_time ?? '').trim(),
219
+ endTime: String(main.end_time ?? '').trim(),
220
+ typeId: type.id,
221
+ typeName: type.name,
222
+ members: normalizeMembers(main.participants),
223
+ meetingSchedule: false,
224
+ };
225
+ });
226
+ }
227
+ /** 把 ESB 原始返回 + 类型过滤结果整理成「哪些人空闲、哪些人忙」。 */
228
+ export function buildScheduleFreeResult(flow, typeIds, options) {
229
+ const normalized = normalizeRows(flow.rows);
230
+ const wanted = new Set(options.memberIds);
231
+ const excluded = new Set(typeIds);
232
+ let meetingSchedule = 0;
233
+ const kept = [];
234
+ for (const item of normalized) {
235
+ const isMeetingType = item.typeId !== '' && excluded.has(item.typeId);
236
+ if (isMeetingType)
237
+ meetingSchedule += 1;
238
+ if (isMeetingType) {
239
+ kept.push({ ...item, meetingSchedule: true });
240
+ continue;
241
+ }
242
+ kept.push(item);
243
+ }
244
+ const busyMap = new Map();
245
+ for (const item of kept) {
246
+ if (item.meetingSchedule)
247
+ continue;
248
+ for (const memberId of item.members) {
249
+ if (wanted.size > 0 && !wanted.has(memberId))
250
+ continue;
251
+ const list = busyMap.get(memberId) ?? [];
252
+ list.push({
253
+ scheduleId: item.scheduleId,
254
+ title: item.title,
255
+ startTime: item.startTime,
256
+ endTime: item.endTime,
257
+ typeId: item.typeId,
258
+ typeName: item.typeName,
259
+ });
260
+ busyMap.set(memberId, list);
261
+ }
262
+ }
263
+ const busyMembers = [...busyMap.entries()].map(([memberId, items]) => ({ memberId, items }));
264
+ const busySet = new Set(busyMembers.map((item) => item.memberId));
265
+ const freeMembers = options.memberIds.filter((memberId) => !busySet.has(memberId));
266
+ const detailAvailable = normalized.length > 0 || flow.count === 0;
267
+ const verdict = normalized.length === 0 && flow.count === 0
268
+ ? 'free'
269
+ : busyMembers.length > 0
270
+ ? 'busy'
271
+ : 'unknown';
272
+ return {
273
+ verdict,
274
+ counts: {
275
+ requested: options.memberIds.length,
276
+ free: freeMembers.length,
277
+ busy: busyMembers.length,
278
+ },
279
+ freeMembers,
280
+ busyMembers,
281
+ filtered: { total: normalized.length, meetingSchedule },
282
+ meetingScheduleTypes: { ids: typeIds, names: [], source: '' },
283
+ message: flow.message,
284
+ detailAvailable,
285
+ target: flow.target,
286
+ ...(options.includeRaw ? { raw: flow.raw } : {}),
287
+ };
288
+ }
289
+ const ebScheduleFree = {
290
+ name: 'meeting.eb.schedule.free',
291
+ resource: 'eb',
292
+ method: 'meeting.eb.schedule.free',
293
+ action: 'meeting.eb.schedule.free',
294
+ summary: '参会人日程空闲检测(ESB 动作流查日程应用,非会议表):判断人在目标时段有没有其它日程占用;自动过滤「会议日程」类型(类型 id 动态解析,不写死)',
295
+ capability: 'eb.schedule',
296
+ risk: 'read',
297
+ inputSchema: schemaOf({
298
+ startDate: { ...SCHEMA_DATETIME_MINUTE, description: '开始时间(必填,yyyy-MM-dd HH:mm)' },
299
+ endDate: { ...SCHEMA_DATETIME_MINUTE, description: '结束时间(必填,yyyy-MM-dd HH:mm)' },
300
+ memberIds: {
301
+ type: 'array',
302
+ items: { type: 'string' },
303
+ description: '参会人 id 数组(必填;HRM 员工 id,与回执 member_id 同口径)',
304
+ },
305
+ mtId: { type: 'string', description: '变更会议时排除该会议自身生成的日程' },
306
+ excludeTypeIds: {
307
+ type: 'string',
308
+ description: '显式指定要过滤的日程类型 id(逗号分隔)。不传时按日程类型字典动态解析「会议日程」',
309
+ },
310
+ refreshTypes: { type: 'boolean', description: 'true=强制重新拉取日程类型字典(默认命中 7 天缓存)' },
311
+ flowId: { type: 'string', description: '覆盖 ESB 动作流 id(租户不同时用)' },
312
+ uniqueIndent: { type: 'string', description: '覆盖 ESB 动作流 primary_key(默认用内置值,换环境时传)' },
313
+ includeRaw: { type: 'boolean', description: 'true=附带 ESB 原始返回,便于排查口径问题' },
314
+ }, { required: ['startDate', 'endDate', 'memberIds'] }),
315
+ handler: async (args, env) => {
316
+ const startDate = String(args.startDate ?? '').trim();
317
+ const endDate = String(args.endDate ?? '').trim();
318
+ const memberIds = splitIds(args.memberIds);
319
+ if (memberIds.length === 0) {
320
+ throw meetingValidationError('schedule_member_required', 'memberIds 不能为空(参会人 id 数组)');
321
+ }
322
+ const typeResolution = await resolveMeetingScheduleTypeIds(env, {
323
+ excludeTypeIds: args.excludeTypeIds,
324
+ refresh: args.refreshTypes === true,
325
+ });
326
+ const flow = await runScheduleConflictFlow(env, {
327
+ startDate,
328
+ endDate,
329
+ memberIds,
330
+ ...(args.mtId ? { mtId: String(args.mtId) } : {}),
331
+ ...(args.flowId ? { flowId: String(args.flowId) } : {}),
332
+ ...(args.uniqueIndent ? { uniqueIndent: String(args.uniqueIndent) } : {}),
333
+ });
334
+ const result = buildScheduleFreeResult(flow, typeResolution.ids, {
335
+ memberIds,
336
+ includeRaw: args.includeRaw === true,
337
+ });
338
+ result.meetingScheduleTypes.names = typeResolution.names;
339
+ result.meetingScheduleTypes.source = typeResolution.source;
340
+ // 动作流只给「数量 + 一段提示语」时,从提示语里把「时间/日程名/涉及人」捞出来,
341
+ // 否则调用方只能看到 unknown(用户也看不明白到底撞了哪条日程)。
342
+ await applyMessageDetails(env, result, flow, memberIds);
343
+ const warnings = [...typeResolution.warnings];
344
+ if (!result.detailAvailable) {
345
+ warnings.push(`ESB 只返回了冲突数量(${flow.count})与提示语「${flow.message || '空'}」,没有逐条日程明细——`
346
+ + '无法判断是哪几条日程占用;如需明细请让 ESB 动作流输出 scheduleConflicts 列表。');
347
+ }
348
+ return meetingSuccess('meeting.eb.schedule.free', result, warnings.length > 0 ? { warnings } : {});
349
+ },
350
+ };
351
+ export const meetingEbScheduleOperations = [ebScheduleFree];
352
+ export function parseScheduleConflictMessage(message) {
353
+ const text = String(message ?? '');
354
+ if (!text.trim())
355
+ return [];
356
+ const pattern = /(\d{1,2}:\d{2})\s*[~~-]\s*(\d{1,2}:\d{2})[^《]*《([^》]*)》[^【]*【([^】]*)】/g;
357
+ const items = [];
358
+ for (const match of text.matchAll(pattern)) {
359
+ const memberNames = match[4]
360
+ .split(/[、,,;;\s]+/)
361
+ .map((name) => name.trim())
362
+ .filter((name) => name !== '');
363
+ items.push({
364
+ startTime: match[1],
365
+ endTime: match[2],
366
+ title: match[3].trim(),
367
+ memberNames,
368
+ });
369
+ }
370
+ return items;
371
+ }
372
+ /** 姓名 → 员工 id(逐个 keyword 查,人数通常在 10 以内;查不到保持原名,不猜)。 */
373
+ export async function resolveMemberIdsByName(env, names) {
374
+ const map = new Map();
375
+ const unique = [...new Set(names)].filter((name) => name !== '');
376
+ if (unique.length === 0)
377
+ return map;
378
+ try {
379
+ const { hrmEmployeeOperations } = await import('../../../hrm/operations/employee.js');
380
+ const query = hrmEmployeeOperations.find((operation) => operation.name === 'hrm.employee.query');
381
+ if (!query)
382
+ return map;
383
+ for (const name of unique) {
384
+ try {
385
+ const result = await query.handler({ keyword: name, pageSize: 5 }, env);
386
+ const rows = (result?.data?.rows ?? []);
387
+ const hit = rows.find((row) => String(row.name ?? '').trim() === name) ?? rows[0];
388
+ const id = String(hit?.id ?? '').trim();
389
+ if (id)
390
+ map.set(name, id);
391
+ }
392
+ catch {
393
+ // 单个查询失败不影响其它人
394
+ }
395
+ }
396
+ }
397
+ catch {
398
+ // hrm 模块不可用:保持原名
399
+ }
400
+ return map;
401
+ }
402
+ /** 把提示语解析结果并入结果集(仅在没有结构化明细时调用)。 */
403
+ export async function applyMessageDetails(env, result, flow, requestedMemberIds) {
404
+ if (result.detailAvailable)
405
+ return false;
406
+ const parsed = parseScheduleConflictMessage(flow.message);
407
+ if (parsed.length === 0)
408
+ return false;
409
+ const idsByName = await resolveMemberIdsByName(env, parsed.flatMap((item) => item.memberNames));
410
+ const wanted = new Set(requestedMemberIds);
411
+ const busy = new Map();
412
+ const unresolved = [];
413
+ for (const item of parsed) {
414
+ for (const name of item.memberNames) {
415
+ const id = idsByName.get(name) ?? '';
416
+ const key = id || `name:${name}`;
417
+ if (id && wanted.size > 0 && !wanted.has(id))
418
+ continue;
419
+ const entry = busy.get(key) ?? { memberId: id || name, items: [] };
420
+ entry.items.push({
421
+ scheduleId: '',
422
+ title: item.title,
423
+ startTime: item.startTime,
424
+ endTime: item.endTime,
425
+ typeId: '',
426
+ typeName: '',
427
+ });
428
+ busy.set(key, entry);
429
+ if (!id)
430
+ unresolved.push(name);
431
+ }
432
+ }
433
+ if (busy.size === 0)
434
+ return false;
435
+ result.busyMembers = [...busy.values()];
436
+ const busyIds = new Set(result.busyMembers.map((item) => item.memberId));
437
+ result.freeMembers = requestedMemberIds.filter((id) => !busyIds.has(id));
438
+ result.counts = {
439
+ requested: requestedMemberIds.length,
440
+ free: result.freeMembers.length,
441
+ busy: result.busyMembers.length,
442
+ };
443
+ result.verdict = 'busy';
444
+ result.detailAvailable = true;
445
+ result.parsedFrom = 'message';
446
+ if (unresolved.length > 0) {
447
+ result.unresolvedNames = [...new Set(unresolved)];
448
+ }
449
+ return true;
450
+ }