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,733 @@
1
+ /**
2
+ * ⑨ 业绩核算:原六菜单链接构造(纯本地)+ 来源台账 List 只读查询(HTTP)。
3
+ *
4
+ * 语义与源脚本 query_link.py / eb-list-api.md 对齐:
5
+ * - 人员/枚举/数值/数值区间条件一律进 needs_ui,由用户在页面补填,AI 不代操作。
6
+ * - combination 路由(合同收款)不透传筛选 query,只出入口地址。
7
+ * - searchPermittedEbMenus 请求体是关键词字符串数组,不是对象。
8
+ * - VIEWPORT/NList 分支未实现(customConfig 需平台 LZW 编码),遇到即报错说明。
9
+ */
10
+ import { PERFORMANCE_CATALOG, CONTRACT_SOURCES, } from '../data/performance-catalog.js';
11
+ import { contractValidationError } from '../errors.js';
12
+ import { requireNonEmptyString } from './shared.js';
13
+ const ORIGIN_PLACEHOLDER = '{baseUrl}';
14
+ const BROWSER_WIDTH = 1000;
15
+ const NULL_OPS = new Set(['isNull', 'isNotNull']);
16
+ function catalog() {
17
+ return PERFORMANCE_CATALOG;
18
+ }
19
+ function findMenu(name) {
20
+ const c = catalog();
21
+ const matches = c.menus.filter((m) => m.name === name || m.aliases.includes(name));
22
+ if (matches.length !== 1) {
23
+ throw contractValidationError('menu_ambiguous', '请选择菜单:' + c.menus.map((m) => m.name).join('、'));
24
+ }
25
+ return matches[0];
26
+ }
27
+ function findField(menu, name) {
28
+ const matches = menu.fields.filter((f) => name === f[0] || name === f[1] || name === 'field_' + f[1]);
29
+ if (matches.length !== 1) {
30
+ throw contractValidationError('field_not_found', `${menu.name}没有唯一字段 ${name}`);
31
+ }
32
+ return matches[0];
33
+ }
34
+ function normalizePeriod(value) {
35
+ const v = String(value).replace(/\s+/gu, '').replace('年', '-').replace(/月/gu, '').replace(/-$/u, '');
36
+ const m = /^(\d{4})(?:-(\d{1,2}))?$/u.exec(v);
37
+ if (!m)
38
+ throw contractValidationError('bad_period', '年月应为 YYYY 或 YYYY-MM;相对日期先按用户时区换算。');
39
+ const year = parseInt(m[1], 10);
40
+ if (m[2]) {
41
+ const month = parseInt(m[2], 10);
42
+ if (month < 1 || month > 12)
43
+ throw contractValidationError('bad_period', '月份超出 1-12。');
44
+ return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}`;
45
+ }
46
+ return String(year);
47
+ }
48
+ function periodCondition(menu, period, fieldName) {
49
+ const p = normalizePeriod(period);
50
+ const name = fieldName ?? menu.default_time;
51
+ if (!name) {
52
+ throw contractValidationError('no_default_time', '渠道订单-商务没有默认业绩年月,请指定 time-field:签订年份、签订月份或签订日期。');
53
+ }
54
+ const field = findField(menu, name);
55
+ if (field[2] === 'date') {
56
+ const year = parseInt(p.slice(0, 4), 10);
57
+ const hasMonth = p.includes('-');
58
+ const month = hasMonth ? parseInt(p.slice(5), 10) : 1;
59
+ const lastDay = hasMonth ? new Date(year, month, 0).getDate() : 31;
60
+ const endMonth = hasMonth ? month : 12;
61
+ const endDay = hasMonth ? lastDay : 31;
62
+ const pad = (n) => String(n).padStart(2, '0');
63
+ return {
64
+ field: name,
65
+ operator: 'between',
66
+ value: [`${year}-${pad(month)}-01`, `${year}-${pad(endMonth)}-${pad(endDay)}`],
67
+ };
68
+ }
69
+ const isYearMonth = name.endsWith('年月') || name === '签订年份' || name === '签订月份';
70
+ if (field[2] !== 'text' || !isYearMonth) {
71
+ throw contractValidationError('bad_time_field', `${name}不是已核对的年月/日期字段。`);
72
+ }
73
+ if (name === '签订年份' && p.includes('-')) {
74
+ throw contractValidationError('bad_time_field', '按月份查询商务订单时使用 time-field「签订月份」。');
75
+ }
76
+ return { field: name, operator: 'startWith', value: p };
77
+ }
78
+ function numberText(value) {
79
+ const s = String(value).replace(/,/gu, '').trim();
80
+ if (!/^-?\d+(\.\d+)?$/u.test(s)) {
81
+ throw contractValidationError('bad_number', `数值应为纯数字,先换算“万”等单位:${String(value)}`);
82
+ }
83
+ return String(parseFloat(s));
84
+ }
85
+ function encodeURIComponent0(v) {
86
+ return encodeURIComponent(v);
87
+ }
88
+ function buildQuery(menu, conditions, origin) {
89
+ const c = catalog();
90
+ const route = `/ebdapp/view/${c.app_id}/${menu.route}/${menu.page_id}-${menu.menu_id}`;
91
+ const params = [
92
+ ['cusMenuId', menu.menu_id],
93
+ ['urlPageTitle', menu.title_param],
94
+ ['appId_' + (menu.app_param_page_id ?? menu.page_id), c.app_id],
95
+ ];
96
+ const filters = [];
97
+ const needsUi = [];
98
+ const warnings = [];
99
+ const seen = new Set();
100
+ for (const condition of conditions) {
101
+ const label = String(condition.field);
102
+ const [fLabel, fid, kind] = findField(menu, label);
103
+ const key = 'field_' + fid;
104
+ if (seen.has(key)) {
105
+ throw contractValidationError('duplicate_condition', `${fLabel}条件重复;先合并,不能通过覆盖丢弃条件。`);
106
+ }
107
+ seen.add(key);
108
+ const op = condition.operator ?? findField(menu, label)[3];
109
+ const allowed = c.operators[kind];
110
+ if (!allowed?.includes(op)) {
111
+ throw contractValidationError('bad_operator', `${fLabel}不支持操作符 ${op}`);
112
+ }
113
+ let value = condition.value;
114
+ if (!NULL_OPS.has(op) && (value === undefined || value === null || value === '' || (Array.isArray(value) && value.length === 0))) {
115
+ throw contractValidationError('missing_value', `${fLabel}缺少筛选值。`);
116
+ }
117
+ const item = { field: fLabel, key, operator: op, value };
118
+ if (NULL_OPS.has(op)) {
119
+ value = '';
120
+ }
121
+ else if (kind.startsWith('person') || kind === 'browser') {
122
+ needsUi.push({ ...item, reason: '需用户在页面检索并选择真实人员/关联对象,核对同名项后搜索;AI不代操作。' });
123
+ continue;
124
+ }
125
+ else if (kind in c.enums) {
126
+ const choices = Array.isArray(value) ? value : [value];
127
+ const mapping = c.enums[kind];
128
+ const ids = [];
129
+ for (const choice of choices) {
130
+ const cs = String(choice);
131
+ if (cs in mapping)
132
+ ids.push(mapping[cs]);
133
+ else if (Object.values(mapping).includes(cs))
134
+ ids.push(cs);
135
+ else
136
+ throw contractValidationError('bad_enum', `${fLabel}没有选项 ${cs}`);
137
+ }
138
+ needsUi.push({ ...item, resolved_ids: ids, reason: '需用户在页面选择枚举项并搜索;枚举URL序列化未实测,AI不代操作。' });
139
+ continue;
140
+ }
141
+ else if (op === 'between') {
142
+ if (!Array.isArray(value) || value.length !== 2) {
143
+ throw contractValidationError('bad_between', `${fLabel}的 between 条件需要两个边界组成的数组。`);
144
+ }
145
+ if (kind === 'date') {
146
+ const [a, b] = value.map((x) => String(x));
147
+ if (!/^\d{4}-\d{2}-\d{2}$/u.test(a) || !/^\d{4}-\d{2}-\d{2}$/u.test(b)) {
148
+ throw contractValidationError('bad_between', `${fLabel}的日期边界应为 YYYY-MM-DD。`);
149
+ }
150
+ if (a > b)
151
+ throw contractValidationError('bad_between', `${fLabel}范围起点大于终点。`);
152
+ value = [a, b];
153
+ }
154
+ else {
155
+ const [a, b] = value.map((x) => numberText(x));
156
+ if (parseFloat(a) > parseFloat(b))
157
+ throw contractValidationError('bad_between', `${fLabel}范围起点大于终点。`);
158
+ needsUi.push({ ...item, value: [a, b], reason: '需用户在页面填写数值范围并搜索;数值URL序列化未实测,AI不代操作。' });
159
+ continue;
160
+ }
161
+ const bounds = value;
162
+ params.push([key + '[0]', String(bounds[0])], [key + '[1]', String(bounds[1])], [key + '_condition_type', op]);
163
+ filters.push({ ...item, value });
164
+ continue;
165
+ }
166
+ else if (kind === 'number') {
167
+ needsUi.push({ ...item, value: numberText(value), reason: '需用户在页面应用数值条件并搜索;数值URL序列化未实测,AI不代操作。' });
168
+ continue;
169
+ }
170
+ else if (typeof value === 'object') {
171
+ throw contractValidationError('bad_value', `${fLabel}需要字符串筛选值。`);
172
+ }
173
+ else {
174
+ value = String(value);
175
+ }
176
+ params.push([key, value], [key + '_condition_type', op]);
177
+ filters.push({ ...item, value });
178
+ }
179
+ if (menu.route === 'combination') {
180
+ for (const f of filters)
181
+ needsUi.push({ ...f, reason: '组合页不透传筛选query;需用户填写该条件并搜索,AI不代操作。' });
182
+ filters.length = 0;
183
+ params.length = 3;
184
+ }
185
+ const defaultTime = menu.default_time;
186
+ if (defaultTime && ![...filters, ...needsUi].some((f) => f.field === defaultTime)) {
187
+ warnings.push(`页面可能默认限制${defaultTime}为当年。请用户核对实际范围;要求全部年份/其他时间口径时,由用户清空默认年月再搜索。`);
188
+ }
189
+ const url = origin + route + '?' + params.map(([k, v]) => `${encodeURIComponent0(k)}=${encodeURIComponent0(v)}`).join('&');
190
+ return {
191
+ menu: menu.name,
192
+ url,
193
+ filters,
194
+ needs_ui: needsUi,
195
+ warnings,
196
+ browsertool: {
197
+ version: '1.0',
198
+ callId: 'performance-query-1',
199
+ toolId: 'open_web',
200
+ arguments: { url, title: menu.name, activate: true, width: BROWSER_WIDTH },
201
+ },
202
+ page_verified: false,
203
+ execution: needsUi.length ? 'manual-page-filter' : 'filtered-link',
204
+ };
205
+ }
206
+ function requireOrigin(input) {
207
+ const raw = String(input.origin ?? '').trim();
208
+ if (!raw) {
209
+ throw contractValidationError('origin_required', `缺少业务域名 origin:传当前环境 baseUrl(与合同办公同一个域名,形如 https://xxx.cn),不要传含 ${ORIGIN_PLACEHOLDER} 的占位值。`);
210
+ }
211
+ if (raw.includes(ORIGIN_PLACEHOLDER)) {
212
+ throw contractValidationError('origin_unresolved', `业务域名未解析:origin 含 ${ORIGIN_PLACEHOLDER} 占位符。`);
213
+ }
214
+ let parsed;
215
+ try {
216
+ parsed = new URL(raw);
217
+ }
218
+ catch {
219
+ throw contractValidationError('bad_origin', 'origin 必须是完整的 http/https 域名。');
220
+ }
221
+ if (!/^https?:$/u.test(parsed.protocol) || !parsed.hostname) {
222
+ throw contractValidationError('bad_origin', 'origin 必须是完整的 http/https 域名。');
223
+ }
224
+ if (parsed.username || (parsed.pathname && parsed.pathname !== '/') || parsed.search || parsed.hash) {
225
+ throw contractValidationError('bad_origin', 'origin 只能是 scheme+host,不能带路径、query 或用户信息。');
226
+ }
227
+ return raw.replace(/\/$/u, '');
228
+ }
229
+ // ---------------------------------------------------------------- 操作定义
230
+ function pageContract(name, summary, props, required) {
231
+ return { name, summary, risk: 'read', inputSchema: { type: 'object', additionalProperties: false, properties: props, required } };
232
+ }
233
+ export const contractPerformanceOperations = [
234
+ {
235
+ name: 'contract.performance.link',
236
+ inputSchema: {
237
+ type: 'object',
238
+ additionalProperties: false,
239
+ properties: {
240
+ menu: { type: 'string' },
241
+ origin: { type: 'string' },
242
+ period: { type: 'string' },
243
+ 'time-field': { type: 'string' },
244
+ conditions: {
245
+ type: 'array',
246
+ items: {
247
+ type: 'object',
248
+ additionalProperties: false,
249
+ properties: { field: { type: 'string' }, operator: { type: 'string' }, value: {} },
250
+ },
251
+ },
252
+ },
253
+ required: ['menu', 'origin'],
254
+ },
255
+ async execute(input) {
256
+ const menu = findMenu(requireNonEmptyString(input.menu, 'menu'));
257
+ const origin = requireOrigin(input);
258
+ const conditions = [];
259
+ if (input.period) {
260
+ conditions.push(periodCondition(menu, String(input.period), input['time-field'] ? String(input['time-field']) : undefined));
261
+ }
262
+ else if (input['time-field']) {
263
+ throw contractValidationError('period_required', 'time-field 需要与 period 同时使用。');
264
+ }
265
+ let conditionList = input.conditions;
266
+ if (typeof conditionList === 'string') {
267
+ try {
268
+ conditionList = JSON.parse(conditionList);
269
+ }
270
+ catch {
271
+ throw contractValidationError('bad_conditions', 'conditions 不是合法 JSON 数组。');
272
+ }
273
+ }
274
+ if (Array.isArray(conditionList)) {
275
+ for (const c of conditionList) {
276
+ conditions.push({ field: String(c.field), operator: c.operator ? String(c.operator) : undefined, value: c.value });
277
+ }
278
+ }
279
+ const built = buildQuery(menu, conditions, origin);
280
+ return {
281
+ status: 'DONE',
282
+ data: built,
283
+ };
284
+ },
285
+ },
286
+ {
287
+ name: 'contract.performance.fields',
288
+ inputSchema: {
289
+ type: 'object',
290
+ additionalProperties: false,
291
+ properties: { menu: { type: 'string' } },
292
+ required: ['menu'],
293
+ },
294
+ async execute(input) {
295
+ const c = catalog();
296
+ const menu = findMenu(requireNonEmptyString(input.menu, 'menu'));
297
+ return {
298
+ status: 'DONE',
299
+ data: {
300
+ menu: menu.name,
301
+ default_time: menu.default_time ?? null,
302
+ fields: menu.fields.map((f) => ({
303
+ label: f[0],
304
+ key: 'field_' + f[1],
305
+ kind: f[2],
306
+ default_operator: f[3],
307
+ operators: c.operators[f[2]] ?? [],
308
+ options: c.enums[f[2]] ?? null,
309
+ })),
310
+ },
311
+ };
312
+ },
313
+ },
314
+ {
315
+ name: 'contract.performance.source.entry',
316
+ inputSchema: {
317
+ type: 'object',
318
+ additionalProperties: false,
319
+ properties: {
320
+ source: { type: 'string', description: '来源名或别名;缺省返回全部三个来源' },
321
+ origin: { type: 'string' },
322
+ },
323
+ required: ['origin'],
324
+ },
325
+ async execute(input) {
326
+ const origin = requireOrigin(input);
327
+ const sources = CONTRACT_SOURCES.sources;
328
+ const wanted = input.source ? String(input.source) : null;
329
+ const out = sources
330
+ .filter((s) => !wanted || s.source === wanted || s.aliases.includes(wanted))
331
+ .map((s) => ({
332
+ source: s.source,
333
+ entry_url: origin + s.list_entry.url,
334
+ card_path: s.card_path,
335
+ obj_id: s.obj_id,
336
+ }));
337
+ if (wanted && out.length === 0) {
338
+ throw contractValidationError('source_not_found', '来源不存在,可选:' + sources.map((s) => s.source).join('、'));
339
+ }
340
+ return { status: 'DONE', data: { sources: out } };
341
+ },
342
+ },
343
+ {
344
+ name: 'contract.performance.menu.search',
345
+ risk: 'read',
346
+ inputSchema: {
347
+ type: 'object',
348
+ additionalProperties: false,
349
+ properties: {
350
+ keywords: { type: 'array', items: { type: 'string' }, description: '关键词数组,每个菜单必须唯一匹配' },
351
+ },
352
+ required: ['keywords'],
353
+ },
354
+ async execute(input, context) {
355
+ let keywordList = input.keywords;
356
+ if (typeof keywordList === 'string') {
357
+ try {
358
+ keywordList = JSON.parse(keywordList);
359
+ }
360
+ catch {
361
+ throw contractValidationError('bad_keywords', 'keywords 不是合法 JSON 数组。');
362
+ }
363
+ }
364
+ if (!Array.isArray(keywordList))
365
+ throw contractValidationError('bad_keywords', 'keywords 必须是字符串数组。');
366
+ const keywords = keywordList.map((k) => requireNonEmptyString(k, 'keywords'));
367
+ const result = (await context.host.contractRequest({
368
+ method: 'POST',
369
+ path: '/api/ebuilder/form/viewlist/searchPermittedEbMenus',
370
+ body: keywords,
371
+ }));
372
+ const menus = Array.isArray(result) ? result : (result.data ?? []);
373
+ return {
374
+ status: 'DONE',
375
+ data: {
376
+ menus: menus.map((m) => ({
377
+ name: m.menuName ?? m.name ?? null,
378
+ pageType: m.pageType ?? null,
379
+ pageId: m.pageId ?? null,
380
+ menuUrl: m.menuUrl ?? null,
381
+ appId: m.appId ?? null,
382
+ objId: m.objId ?? null,
383
+ })),
384
+ note: 'pageType=SEARCH 的 pageId 即 listId;VIEWPORT 的 pageId 是 hostPageId(本 CLI 未实现 VIEWPORT 查询)。',
385
+ },
386
+ };
387
+ },
388
+ },
389
+ {
390
+ name: 'contract.performance.list.init',
391
+ risk: 'read',
392
+ inputSchema: {
393
+ type: 'object',
394
+ additionalProperties: false,
395
+ properties: {
396
+ apiPrefix: { type: 'string', description: '/api/ebuilder 或 /api/ebuilder{appId}' },
397
+ listId: { type: 'string' },
398
+ expectedObjId: { type: 'string', description: '来源配置里已确认的表单 ID,传入则校验' },
399
+ },
400
+ required: ['apiPrefix', 'listId'],
401
+ },
402
+ async execute(input, context) {
403
+ const prefix = requireApiPrefix(input.apiPrefix);
404
+ const listId = requireNonEmptyString(input.listId, 'listId');
405
+ const result = (await context.host.contractRequest({
406
+ method: 'POST',
407
+ path: `${prefix}/form/list/listInit`,
408
+ body: { listId },
409
+ }));
410
+ const objId = result?.data?.objid ?? result?.data?.objId ?? null;
411
+ if (objId == null) {
412
+ throw contractValidationError('list_init_failed', 'listInit 未返回 objId,不能继续查询。');
413
+ }
414
+ if (input.expectedObjId && String(objId) !== String(input.expectedObjId)) {
415
+ throw contractValidationError('obj_id_mismatch', `listInit 的 objId ${objId} 与来源配置 ${input.expectedObjId} 不一致,停止查询。`);
416
+ }
417
+ return { status: 'DONE', data: { objId: String(objId), apiPrefix: prefix, listId } };
418
+ },
419
+ },
420
+ {
421
+ name: 'contract.performance.list.filters',
422
+ risk: 'read',
423
+ inputSchema: {
424
+ type: 'object',
425
+ additionalProperties: false,
426
+ properties: {
427
+ apiPrefix: { type: 'string' },
428
+ listId: { type: 'string' },
429
+ appId: { type: 'string' },
430
+ objId: { type: 'string' },
431
+ includeFields: { type: 'boolean', description: '是否同时读取列映射 getListFields' },
432
+ },
433
+ required: ['apiPrefix', 'listId', 'appId', 'objId'],
434
+ },
435
+ async execute(input, context) {
436
+ const prefix = requireApiPrefix(input.apiPrefix);
437
+ const listId = requireNonEmptyString(input.listId, 'listId');
438
+ const appId = requireNonEmptyString(input.appId, 'appId');
439
+ const objId = requireNonEmptyString(input.objId, 'objId');
440
+ const query = { apid: appId, tpaid: listId, objId, viewType: 'tableView', isVirtualForm: 'false' };
441
+ const filters = (await context.host.contractRequest({
442
+ method: 'GET',
443
+ path: `${prefix}/form/listFilter/getSearchFilters/${listId}`,
444
+ query,
445
+ }));
446
+ let fields = null;
447
+ if (input.includeFields) {
448
+ fields = await context.host.contractRequest({
449
+ method: 'GET',
450
+ path: `${prefix}/form/list/getListFields/${listId}`,
451
+ query: { ...query, clientType: 'PC' },
452
+ });
453
+ }
454
+ return { status: 'DONE', data: { filters: filters?.data ?? filters, fields } };
455
+ },
456
+ },
457
+ {
458
+ name: 'contract.performance.list.query',
459
+ risk: 'read',
460
+ inputSchema: {
461
+ type: 'object',
462
+ additionalProperties: false,
463
+ properties: {
464
+ apiPrefix: { type: 'string' },
465
+ listId: { type: 'string' },
466
+ appId: { type: 'string' },
467
+ objId: { type: 'string' },
468
+ searchFieldId: { type: 'string', description: '真实合同号字段 ID' },
469
+ searchOperator: { type: 'string', description: '默认 eq;仅支持 eq/like/startWith' },
470
+ searchValue: { type: 'string', description: '完整合同号或关键词' },
471
+ searchParamJson: {
472
+ type: 'string',
473
+ description: '完整 searchParamData JSON 字符串;提供时忽略 searchFieldId/Operator/Value',
474
+ },
475
+ pageSize: { type: 'number' },
476
+ maxPages: { type: 'number', description: '最多翻页数,默认 10;到达上限且仍有数据会提示缩小范围' },
477
+ },
478
+ required: ['apiPrefix', 'listId', 'appId', 'objId'],
479
+ },
480
+ async execute(input, context) {
481
+ const prefix = requireApiPrefix(input.apiPrefix);
482
+ const listId = requireNonEmptyString(input.listId, 'listId');
483
+ const appId = requireNonEmptyString(input.appId, 'appId');
484
+ const objId = requireNonEmptyString(input.objId, 'objId');
485
+ const pageSize = input.pageSize ? Number(input.pageSize) : 200;
486
+ const maxPages = input.maxPages ? Number(input.maxPages) : 10;
487
+ let searchParamData;
488
+ if (input.searchParamJson) {
489
+ try {
490
+ searchParamData = JSON.parse(String(input.searchParamJson));
491
+ }
492
+ catch {
493
+ throw contractValidationError('bad_search_param', 'searchParamJson 不是合法 JSON。');
494
+ }
495
+ }
496
+ else {
497
+ const fieldId = requireNonEmptyString(input.searchFieldId, 'searchFieldId');
498
+ const value = requireNonEmptyString(input.searchValue, 'searchValue');
499
+ const op = input.searchOperator ? String(input.searchOperator) : 'eq';
500
+ if (!['eq', 'like', 'startWith'].includes(op)) {
501
+ throw contractValidationError('bad_operator', '本操作仅支持 eq/like/startWith;其他条件请在页面完成。');
502
+ }
503
+ const key = 'field_' + fieldId;
504
+ searchParamData = {
505
+ searchType: '2',
506
+ type: 'and',
507
+ groupFields: {},
508
+ commonSearchFields: {
509
+ [key]: [{ [key]: value, [key + '_condition_type']: op }],
510
+ },
511
+ };
512
+ }
513
+ const tableuid = `wb-${Date.now()}-${Math.floor(Math.random() * 1e8)}`;
514
+ const body = {
515
+ current: 1,
516
+ pageSize,
517
+ requiredMatch: 1,
518
+ tableuid,
519
+ listId,
520
+ searchParamData,
521
+ isFollow: 0,
522
+ isShareToMe: 0,
523
+ filterCondition: {},
524
+ terminal: 'PC',
525
+ };
526
+ const rows = [];
527
+ let total = null;
528
+ let page = 1;
529
+ for (; page <= maxPages; page++) {
530
+ body.current = page;
531
+ const data = (await context.host.contractRequest({
532
+ method: 'POST',
533
+ path: `${prefix}/form/list/getListDatas?passid=${objId}&cid=${objId}`,
534
+ body,
535
+ }));
536
+ const display = data?.data?.displayData;
537
+ const batch = Array.isArray(display) ? display : Array.isArray(data?.data?.rows) ? data.data.rows : [];
538
+ rows.push(...batch);
539
+ const count = (await context.host.contractRequest({
540
+ method: 'POST',
541
+ path: `${prefix}/form/list/getListDataCount?passid=${objId}&cid=${objId}`,
542
+ body,
543
+ }));
544
+ total = Number(count?.data?.total ?? 0);
545
+ if (rows.length >= (total ?? 0))
546
+ break;
547
+ }
548
+ const truncated = total !== null && rows.length < total;
549
+ return {
550
+ status: 'DONE',
551
+ data: {
552
+ context: { objId, appId, listId, apiPrefix: prefix },
553
+ searchParamData,
554
+ rows,
555
+ pagination: { total, fetched: rows.length, pageSize, truncated, maxPages },
556
+ warning: truncated
557
+ ? `真实总数 ${total} 超过本次取回 ${rows.length} 条(maxPages=${maxPages})。请缩小范围后重查,不能把部分结果当全量。`
558
+ : null,
559
+ },
560
+ };
561
+ },
562
+ },
563
+ {
564
+ name: 'contract.performance.receipt.query',
565
+ risk: 'read',
566
+ inputSchema: {
567
+ type: 'object',
568
+ additionalProperties: false,
569
+ properties: {
570
+ origin: { type: 'string' },
571
+ listId: { type: 'string', description: '收款 List 的 listId;缺省用 catalog 里「合同收款」菜单的 list_id' },
572
+ apiPrefix: { type: 'string' },
573
+ appId: { type: 'string' },
574
+ objId: { type: 'string' },
575
+ contractNumbers: { type: 'array', items: { type: 'string' }, description: '完整合同号列表' },
576
+ pageSize: { type: 'number' },
577
+ },
578
+ required: ['origin', 'contractNumbers'],
579
+ },
580
+ async execute(input, context) {
581
+ const origin = requireOrigin(input);
582
+ const c = catalog();
583
+ const menu = findMenu('合同收款');
584
+ const listId = input.listId ? String(input.listId) : menu.list_id;
585
+ if (!listId) {
586
+ throw contractValidationError('list_id_required', 'catalog 未记录「合同收款」的 listId,请传 listId。');
587
+ }
588
+ let numberList = input.contractNumbers;
589
+ if (typeof numberList === 'string') {
590
+ try {
591
+ numberList = JSON.parse(numberList);
592
+ }
593
+ catch {
594
+ throw contractValidationError('bad_contract_numbers', 'contractNumbers 不是合法 JSON 数组。');
595
+ }
596
+ }
597
+ if (!Array.isArray(numberList))
598
+ throw contractValidationError('bad_contract_numbers', 'contractNumbers 必须是字符串数组。');
599
+ const numbers = numberList.map((n) => requireNonEmptyString(n, 'contractNumbers'));
600
+ // 先 init 拿真实运行上下文
601
+ const init = (await context.host.contractRequest({
602
+ method: 'POST',
603
+ path: `/api/ebuilder/form/list/listInit`,
604
+ body: { listId },
605
+ }));
606
+ const objId = String(init?.data?.objid ?? init?.data?.objId ?? '');
607
+ if (!objId)
608
+ throw contractValidationError('list_init_failed', '收款 List listInit 未返回 objId。');
609
+ const appId = input.appId ? String(input.appId) : String(c.app_id);
610
+ const prefix = input.apiPrefix ? requireApiPrefix(input.apiPrefix) : '/api/ebuilder';
611
+ const fieldId = menu.fields.find((f) => f[0] === '合同号')?.[1];
612
+ if (!fieldId)
613
+ throw contractValidationError('field_missing', 'catalog 中「合同收款」没有合同号字段。');
614
+ const pageSize = input.pageSize ? Number(input.pageSize) : 200;
615
+ const results = [];
616
+ for (const number of numbers) {
617
+ const searchParamData = {
618
+ searchType: '2',
619
+ type: 'and',
620
+ groupFields: {},
621
+ commonSearchFields: {
622
+ ['field_' + fieldId]: [{ ['field_' + fieldId]: number, ['field_' + fieldId + '_condition_type']: 'eq' }],
623
+ },
624
+ };
625
+ const tableuid = `wb-${Date.now()}-${Math.floor(Math.random() * 1e8)}`;
626
+ const body = {
627
+ current: 1, pageSize, requiredMatch: 1, tableuid, listId, searchParamData,
628
+ isFollow: 0, isShareToMe: 0, filterCondition: {}, terminal: 'PC',
629
+ };
630
+ try {
631
+ const data = (await context.host.contractRequest({
632
+ method: 'POST',
633
+ path: `${prefix}/form/list/getListDatas?passid=${objId}&cid=${objId}`,
634
+ body,
635
+ }));
636
+ const count = (await context.host.contractRequest({
637
+ method: 'POST',
638
+ path: `${prefix}/form/list/getListDataCount?passid=${objId}&cid=${objId}`,
639
+ body,
640
+ }));
641
+ const display = data?.data?.displayData;
642
+ const rows = Array.isArray(display) ? display : Array.isArray(data?.data?.rows) ? data.data.rows : [];
643
+ const total = Number(count?.data?.total ?? 0);
644
+ results.push({
645
+ contract_number: number,
646
+ status: rows.length ? 'receipt_records_found' : 'no_accessible_receipt',
647
+ record_count: rows.length,
648
+ total,
649
+ rows,
650
+ message: rows.length ? '当前账户查询到收款记录;有记录不代表已收齐。' : '当前账户范围内未查到该合同号的收款记录。',
651
+ });
652
+ }
653
+ catch (error) {
654
+ results.push({
655
+ contract_number: number,
656
+ status: 'query_failed',
657
+ stage: 'receipt_ledger_query',
658
+ message: error instanceof Error ? error.message : String(error),
659
+ });
660
+ }
661
+ }
662
+ const failed = results.filter((r) => r.status === 'query_failed').length;
663
+ return {
664
+ status: 'DONE',
665
+ data: {
666
+ results,
667
+ entry_url: origin + `/ebdapp/view/${c.app_id}/${menu.route}/${menu.page_id}-${menu.menu_id}`,
668
+ status_summary: failed === results.length ? 'failed' : failed ? 'partial' : 'success',
669
+ note: '组合页仅是 UI 容器;数据来自其底层 List。收款时间范围如需限定,请在页面或后续条件中明确,前轮合同年份不自动变为收款年份。',
670
+ },
671
+ };
672
+ },
673
+ },
674
+ ];
675
+ export function performanceContractContracts() {
676
+ return [
677
+ pageContract('contract.performance.link', '构造原六业绩菜单的过滤链接(纯本地)', {
678
+ menu: { type: 'string' },
679
+ origin: { type: 'string' },
680
+ period: { type: 'string' },
681
+ 'time-field': { type: 'string' },
682
+ conditions: { type: 'array', items: { type: 'object' } },
683
+ }, ['menu', 'origin']),
684
+ pageContract('contract.performance.fields', '列出业绩菜单筛选字段(纯本地)', { menu: { type: 'string' } }, ['menu']),
685
+ pageContract('contract.performance.source.entry', '三类合同来源台账入口地址(纯本地)', {
686
+ source: { type: 'string' },
687
+ origin: { type: 'string' },
688
+ }, ['origin']),
689
+ pageContract('contract.performance.menu.search', '按关键词搜索有权限的 EB 菜单', {
690
+ keywords: { type: 'array', items: { type: 'string' } },
691
+ }, ['keywords']),
692
+ pageContract('contract.performance.list.init', 'List 初始化并校验 objId', {
693
+ apiPrefix: { type: 'string' },
694
+ listId: { type: 'string' },
695
+ expectedObjId: { type: 'string' },
696
+ }, ['apiPrefix', 'listId']),
697
+ pageContract('contract.performance.list.filters', '读取搜索配置与列映射', {
698
+ apiPrefix: { type: 'string' },
699
+ listId: { type: 'string' },
700
+ appId: { type: 'string' },
701
+ objId: { type: 'string' },
702
+ includeFields: { type: 'boolean' },
703
+ }, ['apiPrefix', 'listId', 'appId', 'objId']),
704
+ pageContract('contract.performance.list.query', '按精确条件查询来源台账(全页)', {
705
+ apiPrefix: { type: 'string' },
706
+ listId: { type: 'string' },
707
+ appId: { type: 'string' },
708
+ objId: { type: 'string' },
709
+ searchFieldId: { type: 'string' },
710
+ searchOperator: { type: 'string' },
711
+ searchValue: { type: 'string' },
712
+ searchParamJson: { type: 'string' },
713
+ pageSize: { type: 'number' },
714
+ maxPages: { type: 'number' },
715
+ }, ['apiPrefix', 'listId', 'appId', 'objId']),
716
+ pageContract('contract.performance.receipt.query', '按合同号查合同收款台账', {
717
+ origin: { type: 'string' },
718
+ listId: { type: 'string' },
719
+ apiPrefix: { type: 'string' },
720
+ appId: { type: 'string' },
721
+ objId: { type: 'string' },
722
+ contractNumbers: { type: 'array', items: { type: 'string' } },
723
+ pageSize: { type: 'number' },
724
+ }, ['origin', 'contractNumbers']),
725
+ ];
726
+ }
727
+ function requireApiPrefix(value) {
728
+ const raw = requireNonEmptyString(value, 'apiPrefix');
729
+ if (raw !== '/api/ebuilder' && !/^\/api\/ebuilder\/?\d+$/u.test(raw) && !/^\/api\/ebuilder\d+$/u.test(raw)) {
730
+ throw contractValidationError('bad_api_prefix', `apiPrefix 只能是 /api/ebuilder 或 /api/ebuilder{appId},收到:${raw}`);
731
+ }
732
+ return raw;
733
+ }