weaver-work-cli 0.1.8 → 0.1.21

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 (271) hide show
  1. package/README.md +25 -17
  2. package/dist/cmd/action.js +76 -0
  3. package/dist/cmd/index.js +2 -0
  4. package/dist/cmd/refs/index.js +103 -0
  5. package/dist/cmd/register.js +31 -0
  6. package/dist/cmd/root.js +1 -0
  7. package/dist/cmd/setup/index.js +25 -5
  8. package/dist/cmd/skills/index.js +37 -5
  9. package/dist/core/connector.js +54 -0
  10. package/dist/core/context.js +2 -1
  11. package/dist/core/input.js +48 -0
  12. package/dist/core/output.js +298 -3
  13. package/dist/core/refs.js +435 -0
  14. package/dist/core/state.js +26 -0
  15. package/dist/internal/e10/auth/commands.js +34 -19
  16. package/dist/internal/e10/auth/session.js +74 -2
  17. package/dist/internal/e10/auth/xiaoe.js +359 -94
  18. package/dist/internal/e10/context.js +18 -6
  19. package/dist/internal/e10/request-runtime.js +8 -1
  20. package/dist/internal/skills/install.js +148 -28
  21. package/dist/internal/skills/reader.js +12 -9
  22. package/dist/shortcuts/blog/continuation.js +74 -0
  23. package/dist/shortcuts/blog/errors.js +60 -0
  24. package/dist/shortcuts/blog/host.js +175 -0
  25. package/dist/shortcuts/blog/index.js +324 -0
  26. package/dist/shortcuts/blog/manifest.js +415 -0
  27. package/dist/shortcuts/blog/operations/comment.js +125 -0
  28. package/dist/shortcuts/blog/operations/comments.js +31 -0
  29. package/dist/shortcuts/blog/operations/edit.js +131 -0
  30. package/dist/shortcuts/blog/operations/export.js +46 -0
  31. package/dist/shortcuts/blog/operations/like.js +80 -0
  32. package/dist/shortcuts/blog/operations/publish.js +157 -0
  33. package/dist/shortcuts/blog/operations/query-user.js +58 -0
  34. package/dist/shortcuts/blog/operations/query.js +96 -0
  35. package/dist/shortcuts/blog/operations/registry.js +54 -0
  36. package/dist/shortcuts/blog/operations/remind.js +176 -0
  37. package/dist/shortcuts/blog/operations/shared.js +330 -0
  38. package/dist/shortcuts/blog/operations/types.js +16 -0
  39. package/dist/shortcuts/blog/operations.js +9 -0
  40. package/dist/shortcuts/caizhilian/continuation.js +70 -0
  41. package/dist/shortcuts/caizhilian/errors.js +53 -0
  42. package/dist/shortcuts/caizhilian/host.js +190 -0
  43. package/dist/shortcuts/caizhilian/index.js +186 -0
  44. package/dist/shortcuts/caizhilian/manifest.js +42 -0
  45. package/dist/shortcuts/caizhilian/operations/api.js +801 -0
  46. package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
  47. package/dist/shortcuts/caizhilian/operations/files.js +306 -0
  48. package/dist/shortcuts/caizhilian/operations/form.js +170 -0
  49. package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
  50. package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
  51. package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +1227 -0
  52. package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
  53. package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
  54. package/dist/shortcuts/caizhilian/operations/handlers/folder.js +1024 -0
  55. package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
  56. package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
  57. package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
  58. package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
  59. package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
  60. package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
  61. package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
  62. package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
  63. package/dist/shortcuts/caizhilian/operations/specs.js +698 -0
  64. package/dist/shortcuts/caizhilian/operations/types.js +57 -0
  65. package/dist/shortcuts/caizhilian/operations.js +21 -0
  66. package/dist/shortcuts/contract/continuation.js +70 -0
  67. package/dist/shortcuts/contract/errors.js +62 -0
  68. package/dist/shortcuts/contract/host.js +205 -0
  69. package/dist/shortcuts/contract/index.js +318 -0
  70. package/dist/shortcuts/contract/manifest.js +596 -0
  71. package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
  72. package/dist/shortcuts/contract/operations/form-info.js +371 -0
  73. package/dist/shortcuts/contract/operations/read.js +118 -0
  74. package/dist/shortcuts/contract/operations/registry.js +43 -0
  75. package/dist/shortcuts/contract/operations/request.js +300 -0
  76. package/dist/shortcuts/contract/operations/shared.js +133 -0
  77. package/dist/shortcuts/contract/operations/subject.js +157 -0
  78. package/dist/shortcuts/contract/operations/template.js +81 -0
  79. package/dist/shortcuts/contract/operations/types.js +7 -0
  80. package/dist/shortcuts/contract/operations/workflow.js +52 -0
  81. package/dist/shortcuts/contract/operations/write.js +131 -0
  82. package/dist/shortcuts/contract/operations.js +8 -0
  83. package/dist/shortcuts/hrm/index.js +83 -2
  84. package/dist/shortcuts/hrm/manifest.js +16 -9
  85. package/dist/shortcuts/hrm/operations/employee.js +160 -30
  86. package/dist/shortcuts/hrm/operations/org.js +17 -3
  87. package/dist/shortcuts/hrm/operations/position.js +18 -2
  88. package/dist/shortcuts/hrm/operations/registry.js +2 -0
  89. package/dist/shortcuts/hrm/operations/resolve.js +449 -0
  90. package/dist/shortcuts/index.js +5 -1
  91. package/dist/shortcuts/invoice/index.js +74 -36
  92. package/dist/shortcuts/invoice/manifest.js +674 -4
  93. package/dist/shortcuts/invoice/markdown.js +172 -0
  94. package/dist/shortcuts/invoice/operations/add.js +3 -3
  95. package/dist/shortcuts/invoice/operations/delete.js +2 -2
  96. package/dist/shortcuts/invoice/operations/get.js +10 -2
  97. package/dist/shortcuts/invoice/operations/import.js +2 -2
  98. package/dist/shortcuts/invoice/operations/registry.js +8 -1
  99. package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
  100. package/dist/shortcuts/invoice/operations/reim.js +85 -2
  101. package/dist/shortcuts/invoice/operations/shared.js +75 -8
  102. package/dist/shortcuts/invoice/operations/update.js +2 -2
  103. package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
  104. package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
  105. package/dist/shortcuts/invoice/projection.js +328 -0
  106. package/dist/shortcuts/meeting/host.js +18 -1
  107. package/dist/shortcuts/meeting/manifest.js +30 -18
  108. package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
  109. package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
  110. package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
  111. package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
  112. package/dist/shortcuts/meeting/operations/env.js +8 -1
  113. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  114. package/dist/shortcuts/meeting/operations/signs.js +216 -2
  115. package/dist/shortcuts/workflow/endpoints.js +2 -1
  116. package/dist/shortcuts/workflow/errors.js +8 -2
  117. package/dist/shortcuts/workflow/host.js +11 -4
  118. package/dist/shortcuts/workflow/operations/form.js +1016 -590
  119. package/dist/shortcuts/workflow/operations/read.js +913 -231
  120. package/dist/shortcuts/workflow/operations/shared.js +21 -0
  121. package/dist/shortcuts/workflow/operations/write.js +750 -52
  122. package/dist/shortcuts/workflow/operations.js +36 -10
  123. package/docs/_catalog.md +6 -0
  124. package/docs/agent-invoice.md +5 -1
  125. package/docs/agent-io-channel-design.md +468 -0
  126. package/docs/agent-skill-install.md +9 -4
  127. package/docs/blog.md +123 -0
  128. package/docs/caizhilian.md +304 -0
  129. package/docs/contract.md +91 -0
  130. package/docs/e10-auth.md +4 -2
  131. package/docs/hrm.md +53 -11
  132. package/docs/invoice.md +5 -1
  133. package/docs/meeting.md +1 -1
  134. package/docs/operation-manual.md +15 -3
  135. package/docs/skill-review-2026-09-19.md +2 -2
  136. package/package.json +1 -1
  137. package/scripts/package-skill.mjs +32 -20
  138. package/scripts/xiaoe_env.py +118 -0
  139. package/skill-template/business-info.json +113 -14
  140. package/skill-template/domains/blog.md +24 -0
  141. package/skill-template/domains/caizhilian.md +22 -0
  142. package/skill-template/domains/hrm.md +2 -1
  143. package/skill-template/domains/jinchengda.md +23 -0
  144. package/skill-template/domains/meeting.md +1 -1
  145. package/skill-template/domains/workflow.md +1 -1
  146. package/skill-template/skill-template.md +1 -1
  147. package/skills/weaver-e10-blog/SKILL.md +111 -0
  148. package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
  149. package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
  150. package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
  151. package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
  152. package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
  153. package/skills/weaver-e10-blog/references/blog-export.md +42 -0
  154. package/skills/weaver-e10-blog/references/blog-like.md +50 -0
  155. package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
  156. package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
  157. package/skills/weaver-e10-blog/references/blog-query.md +49 -0
  158. package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
  159. package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
  160. package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
  161. package/skills/weaver-e10-caizhilian/SKILL.md +217 -0
  162. package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
  163. package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
  164. package/skills/weaver-e10-caizhilian/references/document.md +111 -0
  165. package/skills/weaver-e10-caizhilian/references/file-collection.md +173 -0
  166. package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
  167. package/skills/weaver-e10-caizhilian/references/files.md +120 -0
  168. package/skills/weaver-e10-caizhilian/references/folder.md +122 -0
  169. package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
  170. package/skills/weaver-e10-caizhilian/references/setting-attach.md +440 -0
  171. package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
  172. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
  173. package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
  174. package/skills/weaver-e10-caizhilian/references/source-manifest.json +5694 -0
  175. package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
  176. package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
  177. package/skills/weaver-e10-calendar/SKILL.md +4 -3
  178. package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
  179. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
  180. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
  181. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
  182. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
  183. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
  184. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
  185. package/skills/weaver-e10-esb/SKILL.md +2 -2
  186. package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
  187. package/skills/weaver-e10-hrm/SKILL.md +104 -9
  188. package/skills/weaver-e10-hrm/references/employee.md +89 -15
  189. package/skills/weaver-e10-hrm/references/org.md +114 -5
  190. package/skills/weaver-e10-hrm/references/position.md +14 -7
  191. package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
  192. package/skills/weaver-e10-jinchengda/SKILL.md +167 -0
  193. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
  194. package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
  195. package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
  196. package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
  197. package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
  198. package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
  199. package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
  200. package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
  201. package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
  202. package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
  203. package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
  204. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
  205. package/skills/weaver-e10-jucailin/SKILL.md +4 -3
  206. package/skills/weaver-e10-mail/SKILL.md +4 -11
  207. package/skills/weaver-e10-meeting/SKILL.md +66 -32
  208. package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
  209. package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
  210. package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
  211. package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
  212. package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
  213. package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
  214. package/skills/weaver-e10-okr/SKILL.md +6 -11
  215. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  216. package/skills/weaver-e10-plan/SKILL.md +3 -3
  217. package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
  218. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
  219. package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
  220. package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
  221. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
  222. package/skills/weaver-e10-shared/SKILL.md +42 -34
  223. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +16 -43
  224. package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
  225. package/skills/weaver-e10-shared/references/non-interactive-environment.md +21 -14
  226. package/skills/weaver-e10-shijingran/SKILL.md +2 -1
  227. package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
  228. package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
  229. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
  230. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
  231. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
  232. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
  233. package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +1 -1
  234. package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
  235. package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
  236. package/skills/weaver-e10-workflow/SKILL.md +41 -46
  237. package/skills/weaver-e10-workflow/references/source-manifest.json +39 -75
  238. package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
  239. package/skills/weaver-e10-workflow/references/workflow-create.md +83 -109
  240. package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
  241. package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
  242. package/skills/weaver-e10-workflow/references/workflow-query.md +40 -27
  243. package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
  244. package/skills/weaver-e10-workflow/references/workflow-view.md +3 -4
  245. package/skills/weaver-e10-yepiaotong/SKILL.md +74 -39
  246. package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
  247. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
  248. package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
  249. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
  250. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
  251. package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
  252. package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
  253. package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
  254. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +89 -9
  255. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
  256. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
  257. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
  258. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
  259. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
  260. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
  261. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
  262. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
  263. package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
  264. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
  265. package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
  266. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
  267. package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
  268. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
  269. package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
  270. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
  271. package/scripts/sync-connector-skills.mjs +0 -172
@@ -0,0 +1,389 @@
1
+ import { meetingConflictError } from '../../errors.js';
2
+ import { meetingSuccess, SCHEMA_DATETIME_MINUTE, SCHEMA_ID } from '../types.js';
3
+ import { assertDateTimeMinute, toIdCsv } from '../shared.js';
4
+ import { EB_BASE, ebBusinessSuccess, extractEbRows, queryBody } from './factory.js';
5
+ /**
6
+ * 会议 EB 版冲突检测(eBuilder formdata)。
7
+ *
8
+ * 为什么存在(2026-09-20):EB 版建会此前**没有任何冲突检测** —— factory 的 prepare 只做字段
9
+ * 白名单 + 必填校验,于是重复占用同一间会议室、参会人在同一时段撞会都不会被拦住。
10
+ * 本文件补齐该项能力,并直接采用参考资料定型后的「2 次请求」方案(**无 N+1**):
11
+ *
12
+ * 会议室:query_meeting_baseset(策略)→ query_roomusage_list(该会议室时段内占用,1 次)
13
+ * 参会人:query_meeting_baseset(策略)→ query_mtDetail_list(候选会议,1 次)
14
+ * → query_meeting_receipt_list?mt_id=<多值批量>(1~k 次:分批 + 分页)→ 本地比对(0 请求)
15
+ *
16
+ * 口径与参考资料一致,不可随意改:
17
+ * - 参会人以**回执表 member_id** 为准(会议表 hrm_members 是渲染文本,不可靠);
18
+ * - 只统计 `member_type=1`(参会人员);参会客户(2)/其他人员(3) 不算冲突;
19
+ * - 候选会议只取 `mt_status ∈ {1审批中,2未开始,8已结束,9进行中}`;
20
+ * 占用视图与回执只对有效状态生成,故无需再额外过滤;
21
+ * - 命中后**严格按应用设置**处理,CLI 不额外加码:
22
+ * - `1` = **禁止提交** → 抛 `conflict_rejected`,怎么都不允许提交(不询问、不新建);
23
+ * - `0` = **仅提醒** → 只写入 warnings(如实告知冲突、建议换时间/换地点),
24
+ * **不阻止提交**:用户了解冲突后仍要求新建时,可继续 prepare → apply 完成建会。
25
+ *
26
+ * 失败语义与标准版(create.ts 的 runMandatoryConflictChecks)对齐:
27
+ * - 应用设置读取失败:非致命,按「开启检测 + 仅提醒」继续,并在 warnings 中如实说明;
28
+ * - 检测请求本身失败:视为冲突 → 拒绝建会(`conflict_check_failed`),宁可拦住不可漏检。
29
+ */
30
+ /** 候选会议的有效状态:1审批中 / 2未开始 / 8已结束 / 9进行中。 */
31
+ const ACTIVE_MT_STATUS = '1,2,8,9';
32
+ /** 列表单页条数(EB 侧 pageSize 上限 200,尽量少翻页)。 */
33
+ const PAGE_SIZE = 200;
34
+ /** 回执批量查询每批携带的会议 id 数(避免 URL 过长)。 */
35
+ const MT_ID_BATCH = 50;
36
+ /** 防御性上限:单个列表最多翻页数(200 × 50 = 1 万条)。 */
37
+ const MAX_PAGES = 50;
38
+ function errorText(error) {
39
+ return error instanceof Error ? error.message : String(error);
40
+ }
41
+ /** 单页 EB 列表查询(list,不计数;筛选走 URL query,body 带 datajson)。 */
42
+ async function requestEbList(env, interfacePk, query, operation) {
43
+ const session = await env.host.getSession();
44
+ const response = await env.host.request({
45
+ method: 'POST',
46
+ path: `${EB_BASE}/getFormDataList/${interfacePk}`,
47
+ operation,
48
+ query,
49
+ body: queryBody(session.userId),
50
+ acceptBusiness: ebBusinessSuccess,
51
+ });
52
+ // 行数据可能是 { mainTable: {...} } 包裹,统一摊平(与 member-conflict.mjs 同口径)。
53
+ return extractEbRows(response).rows.map((row) => (row?.mainTable ? row.mainTable : row));
54
+ }
55
+ /** 分页拉全一个 EB 列表(返回累计请求数,便于在结果里如实报告开销)。 */
56
+ async function listAllEbRows(env, interfacePk, baseQuery, operation) {
57
+ const rows = [];
58
+ let requests = 0;
59
+ for (let pageNo = 1; pageNo <= MAX_PAGES; pageNo += 1) {
60
+ const page = await requestEbList(env, interfacePk, { ...baseQuery, pageNo, pageSize: PAGE_SIZE }, operation);
61
+ requests += 1;
62
+ rows.push(...page);
63
+ if (page.length < PAGE_SIZE)
64
+ break; // 末页
65
+ }
66
+ return { rows, requests };
67
+ }
68
+ /** 读取应用设置(query_meeting_baseset,返回 1 条,多条取第一条)中的冲突策略。 */
69
+ export async function fetchEbConflictPolicy(env, operation) {
70
+ try {
71
+ const rows = await requestEbList(env, 'query_meeting_baseset', { pageNo: 1, pageSize: 5 }, operation);
72
+ const base = rows[0] ?? {};
73
+ return {
74
+ roomChk: String(base.room_conflict_chk ?? ''),
75
+ roomAction: String(base.room_conflict ?? ''),
76
+ memberChk: String(base.member_conflict_chk ?? ''),
77
+ memberAction: String(base.member_conflict ?? ''),
78
+ };
79
+ }
80
+ catch (error) {
81
+ return {
82
+ roomChk: '',
83
+ roomAction: '',
84
+ memberChk: '',
85
+ memberAction: '',
86
+ error: errorText(error),
87
+ };
88
+ }
89
+ }
90
+ /** 会议室冲突:查占用视图(query_roomusage_list),时间重叠即冲突。 */
91
+ async function checkRoomConflict(roomId, begin, end, excludeMtId, env, operation) {
92
+ const { rows, requests } = await listAllEbRows(env, 'query_roomusage_list',
93
+ // 重叠判定:占用开始 < 我的结束(enddatetime 算子 lt)且 占用结束 > 我的开始(begindatetime 算子 gt)
94
+ { address: roomId, enddatetime: end, begindatetime: begin }, operation);
95
+ const conflicts = rows
96
+ .map((row) => ({
97
+ mtId: String(row.mt_id ?? ''),
98
+ begin: String(row.startdate ?? ''),
99
+ end: String(row.enddate ?? ''),
100
+ }))
101
+ .filter((item) => item.mtId && item.mtId !== excludeMtId);
102
+ return { conflicts, requests };
103
+ }
104
+ /**
105
+ * 参会人冲突:候选会议(时间范围 + 有效状态)→ 回执按 mt_id **多值批量**拉取 → 本地求交集。
106
+ * 共 2~k 次请求(候选 1 次 + 回执 1~k 次),**不逐场会议查回执**(旧做法=N+1)。
107
+ */
108
+ async function collectMemberConflicts(members, begin, end, excludeMtId, env, operation) {
109
+ const wanted = new Set(members);
110
+ // 占位符语义(见 catalog 的 queryParams 注释):
111
+ // `enddatetime` ↔ 会议「开始时间」,算子 lt → 传「我的结束时间」
112
+ // `begindatetime` ↔ 会议「结束时间」,算子 gt → 传「我的开始时间」
113
+ // 两者传反会变成「整段包含」关系(会议完全包住我的时段),造成大量漏检。
114
+ const candidate = await listAllEbRows(env, 'query_mtDetail_list', { begindatetime: begin, enddatetime: end, mt_status: ACTIVE_MT_STATUS }, operation);
115
+ const candidates = candidate.rows
116
+ .map((row) => ({
117
+ id: String(row.id ?? ''),
118
+ name: String(row.mt_name ?? row.name ?? ''),
119
+ begin: String(row.begin_datetime ?? ''),
120
+ end: String(row.end_datetime ?? ''),
121
+ }))
122
+ .filter((item) => item.id && item.id !== excludeMtId);
123
+ const byId = new Map(candidates.map((item) => [item.id, item]));
124
+ const byMeeting = new Map();
125
+ let receiptCalls = 0;
126
+ let receipts = 0;
127
+ for (let index = 0; index < candidates.length; index += MT_ID_BATCH) {
128
+ const csv = candidates.slice(index, index + MT_ID_BATCH).map((item) => item.id).join(',');
129
+ const page = await listAllEbRows(env, 'query_meeting_receipt_list', { mt_id: csv }, operation);
130
+ receiptCalls += page.requests;
131
+ receipts += page.rows.length;
132
+ for (const row of page.rows) {
133
+ const meetingId = String(row.mt_id ?? '');
134
+ const memberId = String(row.member_id ?? '');
135
+ const memberType = String(row.member_type ?? '');
136
+ if (!meetingId || !memberId)
137
+ continue;
138
+ if (memberType && memberType !== '1')
139
+ continue; // 仅“参会人员”
140
+ if (!wanted.has(memberId))
141
+ continue; // 只关心本次参会人
142
+ if (!byMeeting.has(meetingId))
143
+ byMeeting.set(meetingId, new Set());
144
+ byMeeting.get(meetingId).add(memberId);
145
+ }
146
+ }
147
+ const conflicts = [...byMeeting.entries()].map(([meetingId, ids]) => {
148
+ const meeting = byId.get(meetingId);
149
+ return {
150
+ meetingId,
151
+ name: meeting?.name ?? '',
152
+ begin: meeting?.begin ?? '',
153
+ end: meeting?.end ?? '',
154
+ conflictedMembers: [...ids],
155
+ };
156
+ });
157
+ conflicts.sort((left, right) => left.begin.localeCompare(right.begin));
158
+ return {
159
+ conflicts,
160
+ listCalls: candidate.requests,
161
+ receiptCalls,
162
+ candidates: candidates.length,
163
+ receipts,
164
+ };
165
+ }
166
+ /**
167
+ * EB 版冲突检测主流程(建会/变更 prepare 阶段调用)。
168
+ *
169
+ * 命中「禁止提交」直接抛 `conflict_rejected`;命中「仅提醒」写入 warnings;
170
+ * 检测请求失败抛 `conflict_check_failed`(与标准版一致:宁可拦住不可漏检)。
171
+ * `throwOnReject=false` 时改为把禁止项写入 warnings(供只查不建的 operation 使用)。
172
+ */
173
+ export async function runEbConflictCheck(input, env, operation, options = {}) {
174
+ const throwOnReject = options.throwOnReject !== false;
175
+ const begin = assertDateTimeMinute(input.beginDatetime, 'begin_datetime');
176
+ const end = assertDateTimeMinute(input.endDatetime, 'end_datetime');
177
+ const warnings = [];
178
+ const policy = await fetchEbConflictPolicy(env, operation);
179
+ const outcome = { warnings, policy };
180
+ if (policy.error) {
181
+ warnings.push(`应用设置(query_meeting_baseset)读取失败,冲突策略按「开启检测 + 仅提醒」处理:${policy.error}`);
182
+ }
183
+ /**
184
+ * 命中冲突的处置(**fail-fast**):
185
+ * - 禁止提交 → **立即抛错并结束**,**不再继续后续检测** —— 结论已定局为拒绝,
186
+ * 继续检测只会多花请求,也不会改变结果;
187
+ * - 仅提醒 → 记入 warnings,**不阻止提交**(用户了解冲突后坚持新建即可继续)。
188
+ * `throwOnReject=false`(只查不建的 operation)时,禁止项改为写入 warnings 而不抛错。
189
+ */
190
+ const handleHit = (action, message, details) => {
191
+ if (action === '1') {
192
+ const text = `${message};应用设置禁止提交(action=1),已拒绝,请更换时间或地点后重试`;
193
+ if (throwOnReject)
194
+ throw meetingConflictError('conflict_rejected', text, details);
195
+ warnings.push(text);
196
+ return;
197
+ }
198
+ warnings.push(`${message};应用设置仅提醒(action=0):请先把冲突如实告知用户,并建议更换时间或地点;`
199
+ + '若用户了解冲突后仍要求新建,可以继续提交完成建会(仅提醒不阻止提交)');
200
+ };
201
+ // ---- 1) 会议室冲突 ----
202
+ if (input.address) {
203
+ if (policy.roomChk === '0') {
204
+ outcome.room = { skipped: true, hasConflict: false, conflicts: [], requests: 0 };
205
+ warnings.push('应用设置 room_conflict_chk=0(关闭),已跳过会议室冲突检测');
206
+ }
207
+ else {
208
+ let result;
209
+ try {
210
+ result = await checkRoomConflict(input.address, begin, end, input.excludeMtId, env, operation);
211
+ }
212
+ catch (error) {
213
+ throw meetingConflictError('conflict_check_failed', `会议室冲突检测失败(视为冲突,拒绝建会):${errorText(error)}`);
214
+ }
215
+ const hasConflict = result.conflicts.length > 0;
216
+ outcome.room = { skipped: false, hasConflict, conflicts: result.conflicts, requests: result.requests };
217
+ if (hasConflict) {
218
+ const detail = result.conflicts
219
+ .map((item) => `${item.mtId}${item.begin ? `(${item.begin}~${item.end})` : ''}`)
220
+ .join('、');
221
+ handleHit(policy.roomAction, `会议室冲突:会议室 ${input.address} 在 ${begin} ~ ${end} 已被占用(占用会议 ${detail})`, { address: input.address, conflicts: result.conflicts, policy: policy.roomAction });
222
+ }
223
+ }
224
+ }
225
+ // ---- 2) 参会人员冲突 ----
226
+ const members = toIdCsv(input.hrmMembers, 'hrm_members');
227
+ if (policy.memberChk === '0') {
228
+ outcome.member = {
229
+ skipped: true, hasConflict: false, conflicts: [], listCalls: 0, receiptCalls: 0, candidates: 0, receipts: 0,
230
+ };
231
+ warnings.push('应用设置 member_conflict_chk=0(关闭),已跳过参会人员冲突检测');
232
+ }
233
+ else if (members) {
234
+ let result;
235
+ try {
236
+ result = await collectMemberConflicts(members.split(','), begin, end, input.excludeMtId, env, operation);
237
+ }
238
+ catch (error) {
239
+ throw meetingConflictError('conflict_check_failed', `参会人员冲突检测失败(视为冲突,拒绝建会):${errorText(error)}`);
240
+ }
241
+ const hasConflict = result.conflicts.length > 0;
242
+ outcome.member = {
243
+ skipped: false,
244
+ hasConflict,
245
+ conflicts: result.conflicts,
246
+ listCalls: result.listCalls,
247
+ receiptCalls: result.receiptCalls,
248
+ candidates: result.candidates,
249
+ receipts: result.receipts,
250
+ };
251
+ if (hasConflict) {
252
+ const detail = result.conflicts
253
+ .map((item) => `${item.name || item.meetingId}(${item.begin}~${item.end}) 冲突人 ${item.conflictedMembers.join(',')}`)
254
+ .join(';');
255
+ handleHit(policy.memberAction, `参会人员冲突:${result.conflicts.length} 场会议与本次参会人时间重叠 —— ${detail}`, { conflicts: result.conflicts, policy: policy.memberAction });
256
+ }
257
+ if (input.orgMembers) {
258
+ warnings.push('org_members(参会部门)未展开为员工名单,本次人员冲突检测仅覆盖 hrm_members 显式指定的人员');
259
+ }
260
+ }
261
+ else if (input.orgMembers) {
262
+ warnings.push('仅提供 org_members(参会部门)而未提供 hrm_members:CLI 不展开部门成员,已跳过参会人员冲突检测');
263
+ }
264
+ return outcome;
265
+ }
266
+ /**
267
+ * 建会/变更 prepare 的前置钩子:EB 版补上强制冲突检测。
268
+ * 仅在拿到起止时间、且传了 address / hrm_members 时才发请求(无目标即无冲突可言)。
269
+ */
270
+ export const ebMeetingWritePreflight = async ({ endpoint, payload, args, env }) => {
271
+ if (endpoint.interfacePk !== 'create_meeting' && endpoint.interfacePk !== 'change_meeting')
272
+ return [];
273
+ const begin = payload.begin_datetime;
274
+ const end = payload.end_datetime;
275
+ if (typeof begin !== 'string' || typeof end !== 'string' || !begin || !end)
276
+ return [];
277
+ const address = typeof payload.address === 'string' && payload.address ? payload.address : undefined;
278
+ const hrmMembers = typeof payload.hrm_members === 'string' && payload.hrm_members ? payload.hrm_members : undefined;
279
+ if (!address && !hrmMembers)
280
+ return [];
281
+ const outcome = await runEbConflictCheck({
282
+ beginDatetime: begin,
283
+ endDatetime: end,
284
+ ...(address ? { address } : {}),
285
+ ...(hrmMembers ? { hrmMembers } : {}),
286
+ ...(typeof payload.org_members === 'string' && payload.org_members ? { orgMembers: payload.org_members } : {}),
287
+ ...(typeof args.id === 'string' || typeof args.id === 'number' ? { excludeMtId: String(args.id) } : {}),
288
+ }, env, endpoint.operation === 'meeting.eb.meeting.change' ? 'meeting.eb.meeting.change.prepare' : 'meeting.eb.meeting.create.prepare');
289
+ return outcome.warnings;
290
+ };
291
+ /** 只查不建:会议室冲突检测 operation。 */
292
+ const ebConflictRoom = {
293
+ name: 'meeting.eb.conflict.room',
294
+ resource: 'eb',
295
+ method: 'meeting.eb.conflict.room',
296
+ action: 'meeting.eb.conflict.room',
297
+ summary: 'EB 版会议室冲突检测(query_roomusage_list 查该会议室时段内占用):按应用设置 room_conflict_chk/room_conflict 给出 verdict',
298
+ capability: 'eb.conflict',
299
+ risk: 'read',
300
+ inputSchema: {
301
+ type: 'object',
302
+ additionalProperties: false,
303
+ properties: {
304
+ beginDatetime: { ...SCHEMA_DATETIME_MINUTE, description: '开始时间(必填,yyyy-MM-dd HH:mm)' },
305
+ endDatetime: { ...SCHEMA_DATETIME_MINUTE, description: '结束时间(必填,yyyy-MM-dd HH:mm)' },
306
+ address: { ...SCHEMA_ID, description: '会议室 id(必填)' },
307
+ mtId: { ...SCHEMA_ID, description: '变更会议时排除自身' },
308
+ },
309
+ required: ['beginDatetime', 'endDatetime', 'address'],
310
+ },
311
+ handler: async (args, env) => {
312
+ const outcome = await runEbConflictCheck({
313
+ beginDatetime: args.beginDatetime,
314
+ endDatetime: args.endDatetime,
315
+ address: String(args.address),
316
+ ...(args.mtId !== undefined ? { excludeMtId: String(args.mtId) } : {}),
317
+ }, env, 'meeting.eb.conflict.room', { throwOnReject: false });
318
+ const room = outcome.room ?? { skipped: true, hasConflict: false, conflicts: [], requests: 0 };
319
+ const verdict = room.skipped
320
+ ? 'skipped'
321
+ : !room.hasConflict
322
+ ? 'ok'
323
+ : outcome.policy.roomAction === '1'
324
+ ? 'rejected'
325
+ : 'warn';
326
+ return meetingSuccess('meeting.eb.conflict.room', {
327
+ verdict,
328
+ hasConflict: room.hasConflict,
329
+ skipped: room.skipped,
330
+ conflicts: room.conflicts,
331
+ policy: { chk: outcome.policy.roomChk, action: outcome.policy.roomAction },
332
+ requests: 1 + room.requests,
333
+ }, outcome.warnings.length > 0 ? { warnings: outcome.warnings } : {});
334
+ },
335
+ };
336
+ /** 只查不建:参会人员冲突检测 operation(2 次请求方案,无 N+1)。 */
337
+ const ebConflictMember = {
338
+ name: 'meeting.eb.conflict.member',
339
+ resource: 'eb',
340
+ method: 'meeting.eb.conflict.member',
341
+ action: 'meeting.eb.conflict.member',
342
+ summary: 'EB 版参会人员冲突检测(query_mtDetail_list 候选 → query_meeting_receipt_list?mt_id=多值批量 → 本地比对;2 次请求,无 N+1):按 member_conflict_chk/member_conflict 给出 verdict',
343
+ capability: 'eb.conflict',
344
+ risk: 'read',
345
+ inputSchema: {
346
+ type: 'object',
347
+ additionalProperties: false,
348
+ properties: {
349
+ beginDatetime: { ...SCHEMA_DATETIME_MINUTE, description: '开始时间(必填,yyyy-MM-dd HH:mm)' },
350
+ endDatetime: { ...SCHEMA_DATETIME_MINUTE, description: '结束时间(必填,yyyy-MM-dd HH:mm)' },
351
+ hrmMembers: { type: 'string', description: '参会人员 id,逗号分隔字符串(必填)' },
352
+ mtId: { ...SCHEMA_ID, description: '变更会议时排除自身' },
353
+ },
354
+ required: ['beginDatetime', 'endDatetime', 'hrmMembers'],
355
+ },
356
+ handler: async (args, env) => {
357
+ const outcome = await runEbConflictCheck({
358
+ beginDatetime: args.beginDatetime,
359
+ endDatetime: args.endDatetime,
360
+ hrmMembers: String(args.hrmMembers),
361
+ ...(args.mtId !== undefined ? { excludeMtId: String(args.mtId) } : {}),
362
+ }, env, 'meeting.eb.conflict.member', { throwOnReject: false });
363
+ const member = outcome.member ?? {
364
+ skipped: true, hasConflict: false, conflicts: [], listCalls: 0, receiptCalls: 0, candidates: 0, receipts: 0,
365
+ };
366
+ const verdict = member.skipped
367
+ ? 'skipped'
368
+ : !member.hasConflict
369
+ ? 'ok'
370
+ : outcome.policy.memberAction === '1'
371
+ ? 'rejected'
372
+ : 'warn';
373
+ return meetingSuccess('meeting.eb.conflict.member', {
374
+ verdict,
375
+ hasConflict: member.hasConflict,
376
+ skipped: member.skipped,
377
+ conflicts: member.conflicts,
378
+ policy: { chk: outcome.policy.memberChk, action: outcome.policy.memberAction },
379
+ cost: {
380
+ baseSetCalls: 1,
381
+ listCalls: member.listCalls,
382
+ receiptCalls: member.receiptCalls,
383
+ candidates: member.candidates,
384
+ receipts: member.receipts,
385
+ },
386
+ }, outcome.warnings.length > 0 ? { warnings: outcome.warnings } : {});
387
+ },
388
+ };
389
+ export const meetingEbConflictOperations = [ebConflictRoom, ebConflictMember];