@wukongcrm/mcp-server 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +969 -97
- package/dist/fc-entry.d.ts +1 -0
- package/dist/fc-entry.js +7 -0
- package/dist/http-entry.d.ts +1 -0
- package/dist/http-entry.js +51 -0
- package/dist/http.d.ts +47 -0
- package/dist/http.js +550 -0
- package/dist/modules.js +4 -4
- package/dist/nocode.d.ts +16 -0
- package/dist/nocode.js +1086 -0
- package/dist/oauth-state.d.ts +94 -0
- package/dist/oauth-state.js +310 -0
- package/dist/oauth.d.ts +59 -0
- package/dist/oauth.js +446 -0
- package/dist/server.d.ts +12 -1
- package/dist/server.js +240 -19
- package/dist/tools.js +3897 -540
- package/package.json +13 -15
package/dist/tools.js
CHANGED
|
@@ -1,5 +1,337 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { CrmClient } from "./client.js";
|
|
2
3
|
import { CRM_MODULES, ensureWritableModule, getModuleByKey } from "./modules.js";
|
|
4
|
+
const FINANCE_MODULES = [
|
|
5
|
+
{
|
|
6
|
+
key: "certificate",
|
|
7
|
+
name: "凭证",
|
|
8
|
+
basePath: "/financeCertificate",
|
|
9
|
+
supportedOperations: ["列表查询", "详情", "汇总", "新增", "编辑", "审核", "冲销", "删除", "凭证整理", "域内固定路径写入"],
|
|
10
|
+
unsupportedOperations: ["文件流下载", "multipart 文件上传代理"]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
key: "ledger",
|
|
14
|
+
name: "账簿",
|
|
15
|
+
basePath: "/financeCertificate",
|
|
16
|
+
supportedOperations: ["明细账", "总账", "科目余额表", "多栏账", "核算项目账", "数量金额账"],
|
|
17
|
+
unsupportedOperations: ["导出", "打印"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
key: "report",
|
|
21
|
+
name: "报表",
|
|
22
|
+
basePath: "/financeReport",
|
|
23
|
+
supportedOperations: ["资产负债表", "利润表", "现金流量表", "平衡检查", "公式编辑", "扩展数据编辑", "域内固定路径写入"],
|
|
24
|
+
unsupportedOperations: ["文件流导出"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: "subject",
|
|
28
|
+
name: "科目",
|
|
29
|
+
basePath: "/financeSubject",
|
|
30
|
+
supportedOperations: ["列表查询", "新增", "编辑", "删除", "启用禁用", "域内固定路径写入"],
|
|
31
|
+
unsupportedOperations: ["文件流导入导出"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "voucherWord",
|
|
35
|
+
name: "凭证字",
|
|
36
|
+
basePath: "/financeVoucher",
|
|
37
|
+
supportedOperations: ["列表查询", "新增", "编辑", "删除", "排序", "域内固定路径写入"],
|
|
38
|
+
unsupportedOperations: []
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "currency",
|
|
42
|
+
name: "币别",
|
|
43
|
+
basePath: "/financeCurrency",
|
|
44
|
+
supportedOperations: ["列表查询", "新增", "编辑", "删除", "域内固定路径写入"],
|
|
45
|
+
unsupportedOperations: []
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: "dashboard",
|
|
49
|
+
name: "财务仪表盘",
|
|
50
|
+
basePath: "/financeDashboard",
|
|
51
|
+
supportedOperations: ["利润表统计", "配置查询", "指标列表", "指标统计", "配置修改", "域内固定路径写入"],
|
|
52
|
+
unsupportedOperations: []
|
|
53
|
+
}
|
|
54
|
+
];
|
|
55
|
+
const FINANCE_LEDGER_QUERIES = [
|
|
56
|
+
{
|
|
57
|
+
key: "detail",
|
|
58
|
+
name: "明细账",
|
|
59
|
+
targetPath: "/financeCertificate/queryDetailAccount",
|
|
60
|
+
aliases: ["detail", "subLedger", "detailedLedger", "明细账"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: "general",
|
|
64
|
+
name: "总账",
|
|
65
|
+
targetPath: "/financeCertificate/queryGeneralLedger",
|
|
66
|
+
aliases: ["general", "generalLedger", "allLedger", "总账"]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: "balance",
|
|
70
|
+
name: "科目余额表",
|
|
71
|
+
targetPath: "/financeCertificate/queryDetailBalanceAccount",
|
|
72
|
+
aliases: ["balance", "accountBalance", "subjectBalance", "科目余额表", "余额表"]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: "multiColumn",
|
|
76
|
+
name: "多栏账",
|
|
77
|
+
targetPath: "/financeCertificate/queryDiversification",
|
|
78
|
+
aliases: ["multiColumn", "diversification", "multi", "多栏账"]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
key: "itemsDetail",
|
|
82
|
+
name: "核算项目明细账",
|
|
83
|
+
targetPath: "/financeCertificate/queryItemsDetailAccount",
|
|
84
|
+
aliases: ["itemsDetail", "auxiliaryDetail", "accountingProjectDetail", "核算项目明细账"]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "itemsBalance",
|
|
88
|
+
name: "核算项目余额表",
|
|
89
|
+
targetPath: "/financeCertificate/queryItemsDetailBalanceAccount",
|
|
90
|
+
aliases: ["itemsBalance", "auxiliaryBalance", "accountingProjectBalance", "核算项目余额表"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "amountDetail",
|
|
94
|
+
name: "数量金额明细账",
|
|
95
|
+
targetPath: "/financeCertificate/queryAmountDetailAccount",
|
|
96
|
+
aliases: ["amountDetail", "numberMoney", "quantityAmountDetail", "数量金额明细账"]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
key: "amountGeneral",
|
|
100
|
+
name: "数量金额总账",
|
|
101
|
+
targetPath: "/financeCertificate/queryAmountDetailUpAccount",
|
|
102
|
+
aliases: ["amountGeneral", "totalNumberMoney", "quantityAmountGeneral", "数量金额总账"]
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
const FINANCE_REPORT_QUERIES = [
|
|
106
|
+
{
|
|
107
|
+
key: "balanceSheet",
|
|
108
|
+
name: "资产负债表",
|
|
109
|
+
targetPath: "/financeReport/balanceSheetReport",
|
|
110
|
+
aliases: ["balanceSheet", "balance", "资产负债表"]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
key: "incomeStatement",
|
|
114
|
+
name: "利润表",
|
|
115
|
+
targetPath: "/financeReport/incomeStatementReport",
|
|
116
|
+
aliases: ["incomeStatement", "profit", "income", "利润表"]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
key: "cashFlow",
|
|
120
|
+
name: "现金流量表",
|
|
121
|
+
targetPath: "/financeReport/cashFlowStatementReport",
|
|
122
|
+
aliases: ["cashFlow", "cash", "现金流量表"]
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
const FINANCE_DASHBOARD_QUERIES = [
|
|
126
|
+
{
|
|
127
|
+
key: "incomeStatement",
|
|
128
|
+
name: "利润表统计",
|
|
129
|
+
targetPath: "/financeDashboard/incomeStatement",
|
|
130
|
+
aliases: ["incomeStatement", "income", "利润表统计"]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
key: "config",
|
|
134
|
+
name: "仪表盘配置",
|
|
135
|
+
targetPath: "/financeDashboard/config",
|
|
136
|
+
aliases: ["config", "dashboardConfig", "配置"]
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
key: "indicator",
|
|
140
|
+
name: "指标列表",
|
|
141
|
+
targetPath: "/financeDashboard/indicator",
|
|
142
|
+
aliases: ["indicator", "indicators", "指标"]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
key: "statistics",
|
|
146
|
+
name: "指标统计",
|
|
147
|
+
targetPath: "/financeDashboard/statistics",
|
|
148
|
+
aliases: ["statistics", "stat", "stats", "指标统计"]
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "fieldLanguage",
|
|
152
|
+
name: "字段语言包",
|
|
153
|
+
targetPath: "/financeDashboard/getAllFieldLanguageRel",
|
|
154
|
+
aliases: ["fieldLanguage", "language", "fieldRel", "字段语言包"]
|
|
155
|
+
}
|
|
156
|
+
];
|
|
157
|
+
const JXC_MODULES = [
|
|
158
|
+
{
|
|
159
|
+
key: "product",
|
|
160
|
+
name: "产品",
|
|
161
|
+
label: 1,
|
|
162
|
+
basePath: "/jxcProduct",
|
|
163
|
+
primaryKey: "productId",
|
|
164
|
+
aliases: [1, "1", "product", "jxcProduct", "产品"]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
key: "supplier",
|
|
168
|
+
name: "供应商",
|
|
169
|
+
label: 8,
|
|
170
|
+
basePath: "/jxcSupplier",
|
|
171
|
+
primaryKey: "supplierId",
|
|
172
|
+
aliases: [8, "8", "supplier", "jxcSupplier", "供应商"]
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
key: "warehouse",
|
|
176
|
+
name: "仓库",
|
|
177
|
+
label: 13,
|
|
178
|
+
basePath: "/jxcWarehouse",
|
|
179
|
+
primaryKey: "warehouseId",
|
|
180
|
+
aliases: [13, "13", "warehouse", "jxcWarehouse", "仓库"]
|
|
181
|
+
}
|
|
182
|
+
];
|
|
183
|
+
const OA_EXAMINE_LIST_ENDPOINTS = [
|
|
184
|
+
{
|
|
185
|
+
key: "upcomingTodo",
|
|
186
|
+
name: "待办-待我审批",
|
|
187
|
+
targetPath: "/examineSuperExamine/todo/me",
|
|
188
|
+
aliases: ["upcoming", "upcoming.todo", "upcoming0", "todo", "todoMe", "待办", "待我审批"]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
key: "upcomingCopy",
|
|
192
|
+
name: "待办-抄送给我",
|
|
193
|
+
targetPath: "/examineSuperExamine/copy/me",
|
|
194
|
+
aliases: ["upcoming.copy", "upcoming1", "copy", "copyMe", "ccMe", "抄送给我"]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
key: "trackMe",
|
|
198
|
+
name: "跟踪-我申请的",
|
|
199
|
+
targetPath: "/examineSuperExamine/track/me",
|
|
200
|
+
aliases: ["track", "track.me", "track0", "trackMe", "myApplication", "跟踪", "我申请的"]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
key: "trackDo",
|
|
204
|
+
name: "跟踪-经我审批",
|
|
205
|
+
targetPath: "/examineSuperExamine/track/do",
|
|
206
|
+
aliases: ["track.do", "track1", "trackDo", "approvedByMe", "经我审批"]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
key: "trackCopy",
|
|
210
|
+
name: "跟踪-抄送给我",
|
|
211
|
+
targetPath: "/examineSuperExamine/track/copy",
|
|
212
|
+
aliases: ["track.copy", "track2", "trackCopy", "trackCcMe", "跟踪抄送"]
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
key: "archiveMe",
|
|
216
|
+
name: "归档-我的申请",
|
|
217
|
+
targetPath: "/examineSuperExamine/end/me",
|
|
218
|
+
aliases: ["archive", "archive.me", "archive0", "end.me", "endMe", "归档", "归档我的申请"]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
key: "archiveAudit",
|
|
222
|
+
name: "归档-经我审批",
|
|
223
|
+
targetPath: "/examineSuperExamine/end/audit",
|
|
224
|
+
aliases: ["archive.audit", "archive1", "end.audit", "endAudit", "归档经我审批"]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
key: "archiveFollow",
|
|
228
|
+
name: "归档-我的关注",
|
|
229
|
+
targetPath: "/examineSuperExamine/end/follow",
|
|
230
|
+
aliases: ["archive.follow", "archive2", "end.follow", "endFollow", "我的关注"]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
key: "archiveTovoid",
|
|
234
|
+
name: "归档-作废",
|
|
235
|
+
targetPath: "/examineSuperExamine/end/tovoid",
|
|
236
|
+
aliases: ["archive.tovoid", "archive3", "end.tovoid", "endTovoid", "void", "作废"]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
key: "archiveCopy",
|
|
240
|
+
name: "归档-抄送给我",
|
|
241
|
+
targetPath: "/examineSuperExamine/end/copy",
|
|
242
|
+
aliases: ["archive.copy", "archive4", "end.copy", "endCopy", "归档抄送"]
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
key: "draft",
|
|
246
|
+
name: "草稿",
|
|
247
|
+
targetPath: "/oaSuperExamine/draft",
|
|
248
|
+
aliases: ["draft", "草稿"]
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
key: "all",
|
|
252
|
+
name: "全部审批",
|
|
253
|
+
targetPath: "/examine/examineSuperExamine/examine/queryAll",
|
|
254
|
+
aliases: ["all", "all0", "全部审批"]
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
key: "legacyWaiting",
|
|
258
|
+
name: "旧版 OA 待审列表",
|
|
259
|
+
targetPath: "/examineWaiting/queryOaExamineList",
|
|
260
|
+
aliases: ["legacyWaiting", "waiting", "oaWaiting", "oldWaiting", "旧版待办"]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
key: "legacyRecord",
|
|
264
|
+
name: "旧版审批记录列表",
|
|
265
|
+
targetPath: "/examineRecord/queryExamineRecordList",
|
|
266
|
+
aliases: ["legacyRecord", "record", "records", "examineRecord", "oldRecord", "旧版记录"]
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
key: "myInitiate",
|
|
270
|
+
name: "我发起的审批",
|
|
271
|
+
targetPath: "/oaExamine/myInitiate",
|
|
272
|
+
aliases: ["myInitiate", "myCreate", "initiated", "我发起", "我发起的审批"]
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
key: "myOaExamine",
|
|
276
|
+
name: "我审批的",
|
|
277
|
+
targetPath: "/oaExamine/myOaExamine",
|
|
278
|
+
aliases: ["myOaExamine", "myExamine", "myAudit", "我审批", "我审批的"]
|
|
279
|
+
}
|
|
280
|
+
];
|
|
281
|
+
const CRM_DOCUMENT_WRITE_DEFINITIONS = [
|
|
282
|
+
{
|
|
283
|
+
key: "contract",
|
|
284
|
+
name: "合同",
|
|
285
|
+
basePath: "/crmContract",
|
|
286
|
+
primaryKey: "contractId",
|
|
287
|
+
requiresApprovalFlow: true
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
key: "receivables",
|
|
291
|
+
name: "回款",
|
|
292
|
+
basePath: "/crmReceivables",
|
|
293
|
+
primaryKey: "receivablesId",
|
|
294
|
+
requiresApprovalFlow: true
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
key: "receivablesPlan",
|
|
298
|
+
name: "回款计划",
|
|
299
|
+
basePath: "/crmReceivablesPlan",
|
|
300
|
+
primaryKey: "receivablesPlanId",
|
|
301
|
+
requiresApprovalFlow: false
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
key: "invoice",
|
|
305
|
+
name: "发票",
|
|
306
|
+
basePath: "/crmInvoice",
|
|
307
|
+
primaryKey: "invoiceId",
|
|
308
|
+
requiresApprovalFlow: true
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
key: "quotation",
|
|
312
|
+
name: "报价单",
|
|
313
|
+
basePath: "/crmQuotation",
|
|
314
|
+
primaryKey: "quotationId",
|
|
315
|
+
requiresApprovalFlow: true
|
|
316
|
+
}
|
|
317
|
+
];
|
|
318
|
+
const WRITE_DOMAINS = {
|
|
319
|
+
hrm: {
|
|
320
|
+
key: "hrm",
|
|
321
|
+
name: "人力资源 HRM",
|
|
322
|
+
allowedPrefixes: ["/hrm"]
|
|
323
|
+
},
|
|
324
|
+
jxc: {
|
|
325
|
+
key: "jxc",
|
|
326
|
+
name: "进销存 JXC",
|
|
327
|
+
allowedPrefixes: ["/jxc"]
|
|
328
|
+
},
|
|
329
|
+
finance: {
|
|
330
|
+
key: "finance",
|
|
331
|
+
name: "独立财务 FM",
|
|
332
|
+
allowedPrefixes: ["/finance"]
|
|
333
|
+
}
|
|
334
|
+
};
|
|
3
335
|
export function createToolHandlers(config = {}) {
|
|
4
336
|
const client = new CrmClient(config);
|
|
5
337
|
return {
|
|
@@ -37,7 +369,7 @@ export function createToolHandlers(config = {}) {
|
|
|
37
369
|
crm_search_records: async (args) => {
|
|
38
370
|
const moduleDefinition = getModuleByKey(args.module);
|
|
39
371
|
const poolContext = isPoolModule(moduleDefinition) ? await resolvePoolContext(client, moduleDefinition, args) : undefined;
|
|
40
|
-
const searchList =
|
|
372
|
+
const searchList = await buildResolvedSearchList(client, args, moduleDefinition);
|
|
41
373
|
const body = {
|
|
42
374
|
page: args.page ?? 1,
|
|
43
375
|
limit: args.limit ?? 15,
|
|
@@ -121,6 +453,89 @@ export function createToolHandlers(config = {}) {
|
|
|
121
453
|
data: await client.post(`${fileModule.basePath}/queryFileList`, undefined, cleanQuery({ id: args.id, fileType: args.fileType }))
|
|
122
454
|
};
|
|
123
455
|
},
|
|
456
|
+
crm_search_products: async (args) => {
|
|
457
|
+
const targetPath = "/crmProduct/queryPageList";
|
|
458
|
+
return {
|
|
459
|
+
module: "product",
|
|
460
|
+
moduleName: "产品",
|
|
461
|
+
readonly: true,
|
|
462
|
+
targetPath,
|
|
463
|
+
data: await client.post(targetPath, await buildCrmProductSearchBody(client, args, false))
|
|
464
|
+
};
|
|
465
|
+
},
|
|
466
|
+
crm_search_sale_products: async (args) => {
|
|
467
|
+
const targetPath = "/crmProduct/querySaleProductPageList";
|
|
468
|
+
return {
|
|
469
|
+
module: "product",
|
|
470
|
+
moduleName: "上架产品",
|
|
471
|
+
readonly: true,
|
|
472
|
+
targetPath,
|
|
473
|
+
data: await client.post(targetPath, await buildCrmProductSearchBody(client, args, true))
|
|
474
|
+
};
|
|
475
|
+
},
|
|
476
|
+
crm_get_product: async (args) => {
|
|
477
|
+
const productId = requireCrmProductId(args, "查询 CRM 产品详情请提供 productId 或 id。");
|
|
478
|
+
const targetPath = `/crmProduct/queryById/${productId}`;
|
|
479
|
+
return {
|
|
480
|
+
module: "product",
|
|
481
|
+
moduleName: "产品",
|
|
482
|
+
readonly: true,
|
|
483
|
+
targetPath,
|
|
484
|
+
data: await client.post(targetPath)
|
|
485
|
+
};
|
|
486
|
+
},
|
|
487
|
+
crm_get_product_information: async (args) => {
|
|
488
|
+
const productId = requireCrmProductId(args, "查询 CRM 产品详情页字段请提供 productId 或 id。");
|
|
489
|
+
const targetPath = `/crmProduct/information/${productId}`;
|
|
490
|
+
return {
|
|
491
|
+
module: "product",
|
|
492
|
+
moduleName: "产品",
|
|
493
|
+
readonly: true,
|
|
494
|
+
targetPath,
|
|
495
|
+
data: await client.post(targetPath)
|
|
496
|
+
};
|
|
497
|
+
},
|
|
498
|
+
crm_get_product_schema: async (args) => {
|
|
499
|
+
const productId = args.productId ?? args.id;
|
|
500
|
+
const targetPath = productId ? `/crmProduct/field/${productId}` : "/crmProduct/field";
|
|
501
|
+
return {
|
|
502
|
+
module: "product",
|
|
503
|
+
moduleName: "产品",
|
|
504
|
+
readonly: true,
|
|
505
|
+
targetPath,
|
|
506
|
+
data: await client.post(targetPath, undefined, cleanQuery({ type: args.type }))
|
|
507
|
+
};
|
|
508
|
+
},
|
|
509
|
+
crm_list_product_files: async (args) => {
|
|
510
|
+
const targetPath = "/crmProduct/queryFileList";
|
|
511
|
+
return {
|
|
512
|
+
module: "product",
|
|
513
|
+
moduleName: "产品",
|
|
514
|
+
readonly: true,
|
|
515
|
+
targetPath,
|
|
516
|
+
data: await client.post(targetPath, undefined, cleanQuery({ id: requireCrmProductId(args, "查询 CRM 产品附件请提供 productId 或 id。"), fileType: args.fileType }))
|
|
517
|
+
};
|
|
518
|
+
},
|
|
519
|
+
crm_get_product_num: async (args) => {
|
|
520
|
+
const targetPath = "/crmProduct/num";
|
|
521
|
+
return {
|
|
522
|
+
module: "product",
|
|
523
|
+
moduleName: "产品",
|
|
524
|
+
readonly: true,
|
|
525
|
+
targetPath,
|
|
526
|
+
data: await client.post(targetPath, undefined, cleanQuery({ id: requireCrmProductId(args, "查询 CRM 产品数量请提供 productId 或 id。") }))
|
|
527
|
+
};
|
|
528
|
+
},
|
|
529
|
+
crm_get_simple_products: async (args) => {
|
|
530
|
+
const targetPath = "/crmProduct/querySimpleEntity";
|
|
531
|
+
return {
|
|
532
|
+
module: "product",
|
|
533
|
+
moduleName: "产品",
|
|
534
|
+
readonly: true,
|
|
535
|
+
targetPath,
|
|
536
|
+
data: await client.post(targetPath, requireIdArray(args.productIds ?? args.ids, "查询 CRM 简要产品请提供 productIds 或 ids 数组。"))
|
|
537
|
+
};
|
|
538
|
+
},
|
|
124
539
|
crm_list_users: async (args) => ({
|
|
125
540
|
data: await client.post("/adminUser/queryUserList", cleanObject({
|
|
126
541
|
page: args.page ?? 1,
|
|
@@ -134,10 +549,19 @@ export function createToolHandlers(config = {}) {
|
|
|
134
549
|
}),
|
|
135
550
|
crm_find_user_id: async (args) => {
|
|
136
551
|
const mode = args.mode ?? "realname";
|
|
137
|
-
const
|
|
552
|
+
const userName = args.userName ?? args.name;
|
|
553
|
+
if (isBlank(userName)) {
|
|
554
|
+
throw new Error("请提供 userName 或 name。");
|
|
555
|
+
}
|
|
556
|
+
if (mode === "realname") {
|
|
557
|
+
return {
|
|
558
|
+
mode,
|
|
559
|
+
data: await resolveUserIdByNickname(client, String(userName), new Map(), args.companyId)
|
|
560
|
+
};
|
|
561
|
+
}
|
|
138
562
|
return {
|
|
139
563
|
mode,
|
|
140
|
-
data: await client.post(
|
|
564
|
+
data: await client.post("/adminUser/getIdByUserName", undefined, { userName, companyId: args.companyId })
|
|
141
565
|
};
|
|
142
566
|
},
|
|
143
567
|
crm_list_departments: async (args) => ({
|
|
@@ -146,656 +570,3589 @@ export function createToolHandlers(config = {}) {
|
|
|
146
570
|
crm_find_dept_id: async (args) => ({
|
|
147
571
|
data: await client.post("/adminDept/getIdByDeptName", undefined, { deptName: args.deptName ?? args.name, companyId: args.companyId })
|
|
148
572
|
}),
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
573
|
+
crm_list_address_book_contacts: async (args) => {
|
|
574
|
+
const targetPath = "/adminUser/queryListName";
|
|
575
|
+
return {
|
|
576
|
+
module: "addressBook",
|
|
577
|
+
moduleName: "通讯录",
|
|
578
|
+
readonly: true,
|
|
579
|
+
targetPath,
|
|
580
|
+
data: await client.post(targetPath, buildAddressBookListBody(args))
|
|
581
|
+
};
|
|
582
|
+
},
|
|
583
|
+
crm_get_address_book_contact: async (args) => {
|
|
584
|
+
const targetPath = "/adminUser/queryUserInfo";
|
|
585
|
+
return {
|
|
586
|
+
module: "addressBook",
|
|
587
|
+
moduleName: "通讯录",
|
|
588
|
+
readonly: true,
|
|
589
|
+
targetPath,
|
|
590
|
+
data: await client.post(targetPath, undefined, cleanQuery({ userId: requireAddressBookUserId(args, "查询通讯录联系人详情请提供 userId 或 id。") }))
|
|
591
|
+
};
|
|
592
|
+
},
|
|
593
|
+
crm_list_address_book_departments: async (args) => {
|
|
594
|
+
const targetPath = "/adminDept/queryDeptTree";
|
|
595
|
+
return {
|
|
596
|
+
module: "addressBook",
|
|
597
|
+
moduleName: "通讯录",
|
|
598
|
+
readonly: true,
|
|
599
|
+
targetPath,
|
|
600
|
+
data: await client.post(targetPath, buildAddressBookDepartmentBody(args))
|
|
601
|
+
};
|
|
602
|
+
},
|
|
603
|
+
crm_list_address_book_auth_departments: async (args) => {
|
|
604
|
+
const targetPath = "/adminDept/queryDeptByAuth";
|
|
605
|
+
return {
|
|
606
|
+
module: "addressBook",
|
|
607
|
+
moduleName: "通讯录",
|
|
608
|
+
readonly: true,
|
|
609
|
+
targetPath,
|
|
610
|
+
data: await client.post(targetPath, cleanObject(args ?? {}))
|
|
611
|
+
};
|
|
612
|
+
},
|
|
613
|
+
crm_list_address_book_dept_user_ids: async (args) => {
|
|
614
|
+
const targetPath = "/adminUser/queryUserByDeptIds";
|
|
615
|
+
const body = addressBookDeptIds(args);
|
|
616
|
+
return {
|
|
617
|
+
module: "addressBook",
|
|
618
|
+
moduleName: "通讯录",
|
|
619
|
+
readonly: true,
|
|
170
620
|
targetPath,
|
|
171
|
-
resolvedRecord,
|
|
172
621
|
body,
|
|
173
|
-
data,
|
|
174
|
-
|
|
175
|
-
});
|
|
622
|
+
data: await client.post(targetPath, body)
|
|
623
|
+
};
|
|
176
624
|
},
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
const targetPath = `${moduleDefinition.basePath}/add`;
|
|
181
|
-
if (!args.confirm) {
|
|
182
|
-
return previewWrite({ ...args, operation: "create", targetPath });
|
|
183
|
-
}
|
|
625
|
+
crm_list_address_book_dept_users: async (args) => {
|
|
626
|
+
const targetPath = "/adminUser/queryListByDeptIds";
|
|
627
|
+
const body = addressBookDeptIds(args);
|
|
184
628
|
return {
|
|
185
|
-
module:
|
|
629
|
+
module: "addressBook",
|
|
630
|
+
moduleName: "通讯录",
|
|
631
|
+
readonly: true,
|
|
186
632
|
targetPath,
|
|
187
|
-
|
|
633
|
+
body,
|
|
634
|
+
data: await client.post(targetPath, body)
|
|
188
635
|
};
|
|
189
636
|
},
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
ensureWritableModule(moduleDefinition);
|
|
193
|
-
const targetPath = `${moduleDefinition.basePath}/updateInformation`;
|
|
194
|
-
if (!args.confirm) {
|
|
195
|
-
return previewWrite({ ...args, operation: "updateInformation", targetPath });
|
|
196
|
-
}
|
|
197
|
-
const body = await buildUpdateInformationBody(client, moduleDefinition, args);
|
|
198
|
-
const data = await client.post(targetPath, body);
|
|
199
|
-
const verification = args.verify === false ? undefined : await verifyInformationUpdate(client, moduleDefinition, body);
|
|
637
|
+
crm_list_address_book_auth_users: async () => {
|
|
638
|
+
const targetPath = "/adminUser/queryAuthUserList";
|
|
200
639
|
return {
|
|
201
|
-
module:
|
|
640
|
+
module: "addressBook",
|
|
641
|
+
moduleName: "通讯录",
|
|
642
|
+
readonly: true,
|
|
643
|
+
targetPath,
|
|
644
|
+
data: await client.post(targetPath)
|
|
645
|
+
};
|
|
646
|
+
},
|
|
647
|
+
crm_get_address_book_user_dept_role_info: async (args) => {
|
|
648
|
+
const targetPath = "/adminUser/queryUserDeptOrRoleInfo";
|
|
649
|
+
const body = buildAddressBookUserDeptRoleBody(args);
|
|
650
|
+
return {
|
|
651
|
+
module: "addressBook",
|
|
652
|
+
moduleName: "通讯录",
|
|
653
|
+
readonly: true,
|
|
202
654
|
targetPath,
|
|
203
655
|
body,
|
|
204
|
-
data,
|
|
205
|
-
verification
|
|
656
|
+
data: await client.post(targetPath, body)
|
|
206
657
|
};
|
|
207
658
|
},
|
|
208
|
-
|
|
209
|
-
const targetPath = "/
|
|
210
|
-
if (!args.confirm) {
|
|
211
|
-
return previewWrite({ ...args, operation: "addFollowup", targetPath });
|
|
212
|
-
}
|
|
659
|
+
crm_get_address_book_organization: async (args) => {
|
|
660
|
+
const targetPath = "/adminUser/queryOrganizationInfo";
|
|
213
661
|
return {
|
|
214
|
-
module: "
|
|
662
|
+
module: "addressBook",
|
|
663
|
+
moduleName: "通讯录",
|
|
664
|
+
readonly: true,
|
|
215
665
|
targetPath,
|
|
216
|
-
data: await client.post(targetPath,
|
|
666
|
+
data: await client.post(targetPath, undefined, cleanQuery({ companyId: args.companyId }))
|
|
217
667
|
};
|
|
218
668
|
},
|
|
219
|
-
|
|
220
|
-
const targetPath =
|
|
669
|
+
crm_get_address_book_user_count: async (args) => {
|
|
670
|
+
const targetPath = "/adminUser/queryUserNumInfo";
|
|
671
|
+
return {
|
|
672
|
+
module: "addressBook",
|
|
673
|
+
moduleName: "通讯录",
|
|
674
|
+
readonly: true,
|
|
675
|
+
targetPath,
|
|
676
|
+
data: await client.post(targetPath, undefined, cleanQuery({ username: requireAddressBookUsername(args) }))
|
|
677
|
+
};
|
|
678
|
+
},
|
|
679
|
+
crm_toggle_address_book_attention: async (args) => {
|
|
680
|
+
const targetPath = "/adminUser/attention";
|
|
681
|
+
const body = buildAddressBookAttentionBody(args);
|
|
221
682
|
if (!args.confirm) {
|
|
222
|
-
return
|
|
683
|
+
return previewOaWrite("toggleAddressBookAttention", "addressBook", "通讯录", targetPath, body);
|
|
223
684
|
}
|
|
224
|
-
const body = args.useInformationUpdate
|
|
225
|
-
? cleanObject({ id: args.id, label: 19, list: args.list ?? args.fields ?? [], batchId: args.batchId })
|
|
226
|
-
: buildFollowupSaveBody(args);
|
|
227
685
|
return {
|
|
228
|
-
module: "
|
|
686
|
+
module: "addressBook",
|
|
687
|
+
moduleName: "通讯录",
|
|
229
688
|
targetPath,
|
|
230
|
-
|
|
689
|
+
body,
|
|
690
|
+
data: await client.post(targetPath, undefined, cleanQuery(body))
|
|
231
691
|
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
692
|
+
},
|
|
693
|
+
crm_list_hrm_employees: async (args) => {
|
|
694
|
+
const targetPath = "/hrmEmployee/queryPageList";
|
|
695
|
+
return {
|
|
696
|
+
module: "hrmEmployee",
|
|
697
|
+
readonly: true,
|
|
698
|
+
targetPath,
|
|
699
|
+
data: await client.post(targetPath, buildHrmEmployeeListBody(args))
|
|
700
|
+
};
|
|
701
|
+
},
|
|
702
|
+
crm_get_hrm_employee: async (args) => {
|
|
703
|
+
const employeeId = requireHrmEmployeeId(args);
|
|
704
|
+
const targetPath = `/hrmEmployee/queryById/${employeeId}`;
|
|
705
|
+
return {
|
|
706
|
+
module: "hrmEmployee",
|
|
707
|
+
readonly: true,
|
|
708
|
+
targetPath,
|
|
709
|
+
data: await client.post(targetPath)
|
|
710
|
+
};
|
|
711
|
+
},
|
|
712
|
+
crm_list_hrm_employee_fields: async () => {
|
|
713
|
+
const targetPath = "/hrmEmployeeField/queryListHeads";
|
|
714
|
+
return {
|
|
715
|
+
module: "hrmEmployee",
|
|
716
|
+
readonly: true,
|
|
717
|
+
targetPath,
|
|
718
|
+
data: await client.post(targetPath)
|
|
719
|
+
};
|
|
720
|
+
},
|
|
721
|
+
crm_list_hrm_departments: async (args) => {
|
|
722
|
+
const targetPath = "/hrmDept/queryTreeList";
|
|
723
|
+
return {
|
|
724
|
+
module: "hrmDept",
|
|
725
|
+
readonly: true,
|
|
726
|
+
targetPath,
|
|
727
|
+
data: await client.post(targetPath, buildHrmDepartmentListBody(args))
|
|
728
|
+
};
|
|
729
|
+
},
|
|
730
|
+
crm_get_hrm_department: async (args) => {
|
|
731
|
+
const deptId = requireHrmDeptId(args);
|
|
732
|
+
const targetPath = `/hrmDept/queryById/${deptId}`;
|
|
733
|
+
return {
|
|
734
|
+
module: "hrmDept",
|
|
735
|
+
readonly: true,
|
|
736
|
+
targetPath,
|
|
737
|
+
data: await client.post(targetPath)
|
|
738
|
+
};
|
|
739
|
+
},
|
|
740
|
+
crm_list_hrm_department_employees: async (args) => {
|
|
741
|
+
const targetPath = "/hrmDept/queryEmployeeByDeptId";
|
|
742
|
+
return {
|
|
743
|
+
module: "hrmDept",
|
|
744
|
+
readonly: true,
|
|
745
|
+
targetPath,
|
|
746
|
+
data: await client.post(targetPath, buildHrmDepartmentEmployeesBody(args))
|
|
747
|
+
};
|
|
748
|
+
},
|
|
749
|
+
crm_get_hrm_employee_post: async (args) => {
|
|
750
|
+
const employeeId = requireHrmEmployeeId(args);
|
|
751
|
+
const targetPath = `/hrmEmployeePost/postInformation/${employeeId}`;
|
|
752
|
+
return {
|
|
753
|
+
module: "hrmEmployeePost",
|
|
754
|
+
readonly: true,
|
|
755
|
+
targetPath,
|
|
756
|
+
data: await client.post(targetPath)
|
|
757
|
+
};
|
|
758
|
+
},
|
|
759
|
+
crm_list_hrm_attendance_month_records: async (args) => {
|
|
760
|
+
const targetPath = "/hrmAttendanceEmpMonthRecord/queryAttendanceEmpMonthRecordPageList";
|
|
761
|
+
return {
|
|
762
|
+
module: "hrmAttendance",
|
|
763
|
+
readonly: true,
|
|
764
|
+
targetPath,
|
|
765
|
+
data: await client.post(targetPath, buildHrmAttendanceMonthRecordBody(args))
|
|
766
|
+
};
|
|
767
|
+
},
|
|
768
|
+
crm_get_hrm_attendance_daily_detail: async (args) => {
|
|
769
|
+
const targetPath = "/hrmAttendanceClock/queryAttendanceDailyDetail";
|
|
770
|
+
return {
|
|
771
|
+
module: "hrmAttendance",
|
|
772
|
+
readonly: true,
|
|
773
|
+
targetPath,
|
|
774
|
+
data: await client.post(targetPath, buildHrmAttendanceDailyDetailBody(args))
|
|
775
|
+
};
|
|
776
|
+
},
|
|
777
|
+
crm_list_hrm_leave_records: async (args) => {
|
|
778
|
+
const targetPath = "/hrmEmployeeLeaveRecord/queryLeaveRecordPageList";
|
|
779
|
+
return {
|
|
780
|
+
module: "hrmLeaveRecord",
|
|
781
|
+
readonly: true,
|
|
782
|
+
targetPath,
|
|
783
|
+
data: await client.post(targetPath, buildHrmLeaveRecordBody(args))
|
|
784
|
+
};
|
|
785
|
+
},
|
|
786
|
+
crm_list_hrm_leave_types: async () => {
|
|
787
|
+
const targetPath = "/hrmEmployeeLeaveRecord/queryLeaveTypeList";
|
|
788
|
+
return {
|
|
789
|
+
module: "hrmLeaveRecord",
|
|
790
|
+
readonly: true,
|
|
791
|
+
targetPath,
|
|
792
|
+
data: await client.post(targetPath)
|
|
793
|
+
};
|
|
794
|
+
},
|
|
795
|
+
crm_list_finance_modules: () => ({
|
|
796
|
+
readonly: true,
|
|
797
|
+
modules: FINANCE_MODULES
|
|
798
|
+
}),
|
|
799
|
+
crm_search_finance_vouchers: async (args) => {
|
|
800
|
+
const targetPath = "/financeCertificate/queryPageList";
|
|
801
|
+
return {
|
|
802
|
+
module: "financeCertificate",
|
|
803
|
+
moduleName: "凭证",
|
|
804
|
+
readonly: true,
|
|
805
|
+
targetPath,
|
|
806
|
+
data: await client.post(targetPath, buildFinanceSearchBody(args, { page: args.page ?? 1, limit: args.limit ?? 15, pageType: args.pageType ?? 1 }))
|
|
807
|
+
};
|
|
808
|
+
},
|
|
809
|
+
crm_get_finance_voucher: async (args) => {
|
|
810
|
+
const targetPath = "/financeCertificate/queryById";
|
|
811
|
+
return {
|
|
812
|
+
module: "financeCertificate",
|
|
813
|
+
moduleName: "凭证",
|
|
814
|
+
readonly: true,
|
|
815
|
+
targetPath,
|
|
816
|
+
data: await client.post(targetPath, cleanObject({ certificateId: requireFinanceCertificateId(args), accountId: args.accountId }))
|
|
817
|
+
};
|
|
818
|
+
},
|
|
819
|
+
crm_summarize_finance_vouchers: async (args) => {
|
|
820
|
+
const targetPath = "/financeCertificate/summary";
|
|
821
|
+
return {
|
|
822
|
+
module: "financeCertificate",
|
|
823
|
+
moduleName: "凭证汇总",
|
|
824
|
+
readonly: true,
|
|
825
|
+
targetPath,
|
|
826
|
+
data: await client.post(targetPath, buildFinanceBody(args))
|
|
827
|
+
};
|
|
828
|
+
},
|
|
829
|
+
crm_query_finance_ledger: async (args) => {
|
|
830
|
+
const ledgerDefinition = getFinanceEndpointByKey(args.ledger ?? args.type ?? args.kind, FINANCE_LEDGER_QUERIES, "财务账簿");
|
|
831
|
+
return {
|
|
832
|
+
module: "financeLedger",
|
|
833
|
+
moduleName: ledgerDefinition.name,
|
|
834
|
+
ledger: ledgerDefinition.key,
|
|
835
|
+
readonly: true,
|
|
836
|
+
targetPath: ledgerDefinition.targetPath,
|
|
837
|
+
data: await client.post(ledgerDefinition.targetPath, buildFinanceBody(args, ["ledger", "type", "kind"]))
|
|
838
|
+
};
|
|
839
|
+
},
|
|
840
|
+
crm_query_finance_report: async (args) => {
|
|
841
|
+
const reportDefinition = getFinanceEndpointByKey(args.report ?? args.type ?? args.kind, FINANCE_REPORT_QUERIES, "财务报表");
|
|
842
|
+
const targetPath = args.checkBalance || args.balanceCheck ? `${reportDefinition.targetPath}/balanceCheck` : reportDefinition.targetPath;
|
|
843
|
+
return {
|
|
844
|
+
module: "financeReport",
|
|
845
|
+
moduleName: reportDefinition.name,
|
|
846
|
+
report: reportDefinition.key,
|
|
847
|
+
balanceCheck: Boolean(args.checkBalance || args.balanceCheck),
|
|
848
|
+
readonly: true,
|
|
849
|
+
targetPath,
|
|
850
|
+
data: await client.post(targetPath, buildFinanceBody(args, ["report", "type", "kind", "checkBalance", "balanceCheck"]))
|
|
851
|
+
};
|
|
852
|
+
},
|
|
853
|
+
crm_list_finance_subjects: async (args) => {
|
|
854
|
+
const targetPath = "/financeSubject/list";
|
|
855
|
+
return {
|
|
856
|
+
module: "financeSubject",
|
|
857
|
+
moduleName: "科目",
|
|
858
|
+
readonly: true,
|
|
859
|
+
targetPath,
|
|
860
|
+
data: await client.post(targetPath, buildFinanceBody(args))
|
|
861
|
+
};
|
|
862
|
+
},
|
|
863
|
+
crm_list_finance_currencies: async (args) => {
|
|
864
|
+
const targetPath = args.accountId ? "/financeCurrency/queryListByAccountId" : "/financeCurrency/queryAllList";
|
|
865
|
+
return {
|
|
866
|
+
module: "financeCurrency",
|
|
867
|
+
moduleName: "币别",
|
|
868
|
+
readonly: true,
|
|
869
|
+
targetPath,
|
|
870
|
+
data: await client.post(targetPath, buildFinanceBody(args))
|
|
871
|
+
};
|
|
872
|
+
},
|
|
873
|
+
crm_list_finance_voucher_words: async (args) => {
|
|
874
|
+
const targetPath = "/financeVoucher/queryList";
|
|
875
|
+
return {
|
|
876
|
+
module: "financeVoucher",
|
|
877
|
+
moduleName: "凭证字",
|
|
878
|
+
readonly: true,
|
|
879
|
+
targetPath,
|
|
880
|
+
data: await client.post(targetPath, buildFinanceBody(args))
|
|
881
|
+
};
|
|
882
|
+
},
|
|
883
|
+
crm_query_finance_dashboard: async (args) => {
|
|
884
|
+
const dashboardDefinition = getFinanceEndpointByKey(args.query ?? args.type ?? args.kind, FINANCE_DASHBOARD_QUERIES, "财务仪表盘");
|
|
885
|
+
return {
|
|
886
|
+
module: "financeDashboard",
|
|
887
|
+
moduleName: dashboardDefinition.name,
|
|
888
|
+
query: dashboardDefinition.key,
|
|
889
|
+
readonly: true,
|
|
890
|
+
targetPath: dashboardDefinition.targetPath,
|
|
891
|
+
data: await client.post(dashboardDefinition.targetPath, buildFinanceBody(args, ["query", "type", "kind"]))
|
|
892
|
+
};
|
|
893
|
+
},
|
|
894
|
+
crm_write_hrm: async (args) => executeDomainWrite(client, WRITE_DOMAINS.hrm, args),
|
|
895
|
+
crm_create_hrm_record: async (args) => executeDomainWrite(client, WRITE_DOMAINS.hrm, {
|
|
896
|
+
targetPath: args.targetPath ?? args.path ?? args.endpoint ?? buildDomainTargetPath(args.module ?? "hrmEmployee", args.operation ?? args.action ?? "addEmployee"),
|
|
897
|
+
body: explicitWriteBody(args),
|
|
898
|
+
query: args.query,
|
|
899
|
+
confirm: args.confirm
|
|
900
|
+
}),
|
|
901
|
+
crm_update_hrm_record: async (args) => executeDomainWrite(client, WRITE_DOMAINS.hrm, {
|
|
902
|
+
targetPath: args.targetPath ?? args.path ?? args.endpoint ?? buildDomainTargetPath(args.module ?? "hrmEmployee", args.operation ?? args.action ?? "updateInformation"),
|
|
903
|
+
body: explicitWriteBody(args),
|
|
904
|
+
query: args.query,
|
|
905
|
+
confirm: args.confirm
|
|
906
|
+
}),
|
|
907
|
+
crm_list_jxc_modules: () => ({
|
|
908
|
+
readonly: true,
|
|
909
|
+
modules: JXC_MODULES.map(({ aliases: _aliases, ...moduleDefinition }) => moduleDefinition)
|
|
910
|
+
}),
|
|
911
|
+
crm_search_jxc_records: async (args) => {
|
|
912
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
913
|
+
const targetPath = `${moduleDefinition.basePath}/queryPageList`;
|
|
914
|
+
return {
|
|
915
|
+
module: moduleDefinition.key,
|
|
916
|
+
moduleName: moduleDefinition.name,
|
|
917
|
+
readonly: true,
|
|
918
|
+
targetPath,
|
|
919
|
+
data: await client.post(targetPath, buildJxcSearchBody(args, moduleDefinition))
|
|
920
|
+
};
|
|
921
|
+
},
|
|
922
|
+
crm_get_jxc_record: async (args) => {
|
|
923
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
924
|
+
const id = requireJxcId(args, "查询 JXC 记录详情请提供 id。");
|
|
925
|
+
const targetPath = `${moduleDefinition.basePath}/queryById/${id}`;
|
|
926
|
+
return {
|
|
927
|
+
module: moduleDefinition.key,
|
|
928
|
+
moduleName: moduleDefinition.name,
|
|
929
|
+
readonly: true,
|
|
930
|
+
targetPath,
|
|
931
|
+
data: await client.post(targetPath)
|
|
932
|
+
};
|
|
933
|
+
},
|
|
934
|
+
crm_get_jxc_record_information: async (args) => {
|
|
935
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
936
|
+
const id = requireJxcId(args, "查询 JXC 详情页字段请提供 id。");
|
|
937
|
+
const targetPath = `${moduleDefinition.basePath}/information/${id}`;
|
|
938
|
+
return {
|
|
939
|
+
module: moduleDefinition.key,
|
|
940
|
+
moduleName: moduleDefinition.name,
|
|
941
|
+
readonly: true,
|
|
942
|
+
targetPath,
|
|
943
|
+
data: await client.post(targetPath)
|
|
944
|
+
};
|
|
945
|
+
},
|
|
946
|
+
crm_get_jxc_module_schema: async (args) => {
|
|
947
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
948
|
+
const id = args.id ?? args.recordId;
|
|
949
|
+
const targetPath = id ? `${moduleDefinition.basePath}/field/${id}` : `${moduleDefinition.basePath}/field`;
|
|
950
|
+
return {
|
|
951
|
+
module: moduleDefinition.key,
|
|
952
|
+
moduleName: moduleDefinition.name,
|
|
953
|
+
readonly: true,
|
|
954
|
+
targetPath,
|
|
955
|
+
data: await client.post(targetPath, undefined, cleanQuery({ type: args.type }))
|
|
956
|
+
};
|
|
957
|
+
},
|
|
958
|
+
crm_list_jxc_files: async (args) => {
|
|
959
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
960
|
+
const targetPath = `${moduleDefinition.basePath}/queryFileList`;
|
|
961
|
+
return {
|
|
962
|
+
module: moduleDefinition.key,
|
|
963
|
+
moduleName: moduleDefinition.name,
|
|
964
|
+
readonly: true,
|
|
965
|
+
targetPath,
|
|
966
|
+
data: await client.post(targetPath, undefined, cleanQuery({ id: requireJxcId(args, "查询 JXC 附件请提供 id。"), fileType: args.fileType }))
|
|
967
|
+
};
|
|
968
|
+
},
|
|
969
|
+
crm_list_jxc_stock_products: async (args) => {
|
|
970
|
+
const targetPath = "/jxcWarehouseProduct/queryPageList";
|
|
971
|
+
return {
|
|
972
|
+
module: "warehouseProduct",
|
|
973
|
+
moduleName: "产品库存",
|
|
974
|
+
readonly: true,
|
|
975
|
+
targetPath,
|
|
976
|
+
data: await client.post(targetPath, buildJxcStockProductsBody(args))
|
|
977
|
+
};
|
|
978
|
+
},
|
|
979
|
+
crm_get_jxc_product_inventory: async (args) => {
|
|
980
|
+
const productId = requireJxcProductId(args);
|
|
981
|
+
const targetPath = `/jxcProduct/queryProductInventory/${productId}`;
|
|
982
|
+
return {
|
|
983
|
+
module: "productInventory",
|
|
984
|
+
moduleName: "产品库存",
|
|
985
|
+
readonly: true,
|
|
986
|
+
targetPath,
|
|
987
|
+
data: await client.post(targetPath, undefined, cleanQuery({ warehouseId: args.warehouseId }))
|
|
988
|
+
};
|
|
989
|
+
},
|
|
990
|
+
crm_list_jxc_stock_movements: async (args) => {
|
|
991
|
+
const targetPath = "/jxcDetailed/queryPageList";
|
|
992
|
+
return {
|
|
993
|
+
module: "detailed",
|
|
994
|
+
moduleName: "出入库明细",
|
|
995
|
+
readonly: true,
|
|
996
|
+
targetPath,
|
|
997
|
+
data: await client.post(targetPath, buildJxcStockMovementsBody(args))
|
|
998
|
+
};
|
|
999
|
+
},
|
|
1000
|
+
crm_get_jxc_warehouse_product_stock: async (args) => {
|
|
1001
|
+
const targetPath = "/jxcWarehouse/queryProductWarehouseNumBatch";
|
|
1002
|
+
return {
|
|
1003
|
+
module: "warehouseProductStock",
|
|
1004
|
+
moduleName: "仓库产品库存数量",
|
|
1005
|
+
readonly: true,
|
|
1006
|
+
targetPath,
|
|
1007
|
+
data: await client.post(targetPath, requireWarehouseProductIdsMap(args))
|
|
1008
|
+
};
|
|
1009
|
+
},
|
|
1010
|
+
crm_get_jxc_warehouse_names: async (args) => {
|
|
1011
|
+
const targetPath = "/jxcWarehouse/queryWarehouseNameByIds";
|
|
1012
|
+
return {
|
|
1013
|
+
module: "warehouse",
|
|
1014
|
+
moduleName: "仓库",
|
|
1015
|
+
readonly: true,
|
|
1016
|
+
targetPath,
|
|
1017
|
+
data: await client.post(targetPath, requireWarehouseIds(args))
|
|
1018
|
+
};
|
|
1019
|
+
},
|
|
1020
|
+
crm_write_jxc: async (args) => executeDomainWrite(client, WRITE_DOMAINS.jxc, args),
|
|
1021
|
+
crm_create_jxc_record: async (args) => {
|
|
1022
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
1023
|
+
const targetPath = `${moduleDefinition.basePath}/add`;
|
|
1024
|
+
const body = buildJxcRecordSaveBody(args, moduleDefinition, false);
|
|
1025
|
+
if (!args.confirm) {
|
|
1026
|
+
return previewDomainWrite(WRITE_DOMAINS.jxc, targetPath, body, args.query);
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
module: moduleDefinition.key,
|
|
1030
|
+
moduleName: moduleDefinition.name,
|
|
1031
|
+
targetPath,
|
|
1032
|
+
body,
|
|
1033
|
+
data: await client.post(targetPath, body, cleanQuery(args.query ?? {}))
|
|
1034
|
+
};
|
|
1035
|
+
},
|
|
1036
|
+
crm_update_jxc_record: async (args) => {
|
|
1037
|
+
const moduleDefinition = getJxcModuleByKey(args.module);
|
|
1038
|
+
const targetPath = args.useInformationUpdate ? `${moduleDefinition.basePath}/updateInformation` : `${moduleDefinition.basePath}/update`;
|
|
1039
|
+
const body = args.useInformationUpdate
|
|
1040
|
+
? buildJxcUpdateInformationBody(args, moduleDefinition)
|
|
1041
|
+
: buildJxcRecordSaveBody(args, moduleDefinition, true);
|
|
1042
|
+
if (!args.confirm) {
|
|
1043
|
+
return previewDomainWrite(WRITE_DOMAINS.jxc, targetPath, body, args.query);
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
module: moduleDefinition.key,
|
|
1047
|
+
moduleName: moduleDefinition.name,
|
|
1048
|
+
targetPath,
|
|
1049
|
+
body,
|
|
1050
|
+
data: await client.post(targetPath, body, cleanQuery(args.query ?? {}))
|
|
1051
|
+
};
|
|
1052
|
+
},
|
|
1053
|
+
crm_write_finance: async (args) => executeDomainWrite(client, WRITE_DOMAINS.finance, args),
|
|
1054
|
+
crm_create_finance_voucher: async (args) => {
|
|
1055
|
+
const targetPath = "/financeCertificate/add";
|
|
1056
|
+
const body = buildFinanceVoucherWriteBody(args, false);
|
|
1057
|
+
if (!args.confirm) {
|
|
1058
|
+
return previewDomainWrite(WRITE_DOMAINS.finance, targetPath, body, args.query);
|
|
1059
|
+
}
|
|
1060
|
+
return {
|
|
1061
|
+
module: "financeCertificate",
|
|
1062
|
+
moduleName: "凭证",
|
|
1063
|
+
targetPath,
|
|
1064
|
+
body,
|
|
1065
|
+
data: await client.post(targetPath, body, cleanQuery(args.query ?? {}))
|
|
1066
|
+
};
|
|
1067
|
+
},
|
|
1068
|
+
crm_update_finance_voucher: async (args) => {
|
|
1069
|
+
const targetPath = "/financeCertificate/update";
|
|
1070
|
+
const body = buildFinanceVoucherWriteBody(args, true);
|
|
1071
|
+
if (!args.confirm) {
|
|
1072
|
+
return previewDomainWrite(WRITE_DOMAINS.finance, targetPath, body, args.query);
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
module: "financeCertificate",
|
|
1076
|
+
moduleName: "凭证",
|
|
1077
|
+
targetPath,
|
|
1078
|
+
body,
|
|
1079
|
+
data: await client.post(targetPath, body, cleanQuery(args.query ?? {}))
|
|
1080
|
+
};
|
|
1081
|
+
},
|
|
1082
|
+
crm_list_calendar_events: async (args) => ({
|
|
1083
|
+
module: "calendarEvent",
|
|
1084
|
+
targetPath: "/oaEvent/queryList",
|
|
1085
|
+
data: await client.post("/oaEvent/queryList", buildCalendarListBody(args))
|
|
1086
|
+
}),
|
|
1087
|
+
crm_get_calendar_event: async (args) => ({
|
|
1088
|
+
module: "calendarEvent",
|
|
1089
|
+
targetPath: "/oaEvent/queryById",
|
|
1090
|
+
data: await client.post("/oaEvent/queryById", buildCalendarDetailBody(args))
|
|
1091
|
+
}),
|
|
1092
|
+
crm_list_oa_announcements: async (args) => {
|
|
1093
|
+
const targetPath = "/oaAnnouncement/queryList";
|
|
1094
|
+
const query = buildOaAnnouncementListQuery(args);
|
|
1095
|
+
return {
|
|
1096
|
+
module: "oaAnnouncement",
|
|
1097
|
+
moduleName: "公告",
|
|
1098
|
+
readonly: true,
|
|
1099
|
+
targetPath,
|
|
1100
|
+
query,
|
|
1101
|
+
data: await client.post(targetPath, undefined, query)
|
|
1102
|
+
};
|
|
1103
|
+
},
|
|
1104
|
+
crm_get_oa_announcement: async (args) => {
|
|
1105
|
+
const announcementId = requireOaAnnouncementId(args, "查询 OA 公告请提供 announcementId 或 id。");
|
|
1106
|
+
const targetPath = `/oaAnnouncement/queryById/${announcementId}`;
|
|
1107
|
+
return {
|
|
1108
|
+
module: "oaAnnouncement",
|
|
1109
|
+
moduleName: "公告",
|
|
1110
|
+
readonly: true,
|
|
1111
|
+
targetPath,
|
|
1112
|
+
data: await client.post(targetPath)
|
|
1113
|
+
};
|
|
1114
|
+
},
|
|
1115
|
+
crm_create_oa_announcement: async (args) => executeOaAnnouncementWrite(client, args, false),
|
|
1116
|
+
crm_update_oa_announcement: async (args) => executeOaAnnouncementWrite(client, args, true),
|
|
1117
|
+
crm_delete_oa_announcement: async (args) => {
|
|
1118
|
+
const announcementId = requireOaAnnouncementId(args, "删除 OA 公告请提供 announcementId 或 id。");
|
|
1119
|
+
const targetPath = `/oaAnnouncement/delete/${announcementId}`;
|
|
1120
|
+
if (!args.confirm) {
|
|
1121
|
+
return previewOaWrite("delete", "oaAnnouncement", "公告", targetPath, {});
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
module: "oaAnnouncement",
|
|
1125
|
+
moduleName: "公告",
|
|
1126
|
+
targetPath,
|
|
1127
|
+
data: await client.post(targetPath)
|
|
1128
|
+
};
|
|
1129
|
+
},
|
|
1130
|
+
crm_mark_oa_announcement_read: async (args) => {
|
|
1131
|
+
const targetPath = "/oaAnnouncement/readAnnouncement";
|
|
1132
|
+
const body = buildOaAnnouncementReadBody(args);
|
|
1133
|
+
if (!args.confirm) {
|
|
1134
|
+
return previewOaWrite("read", "oaAnnouncement", "公告", targetPath, body);
|
|
1135
|
+
}
|
|
1136
|
+
return {
|
|
1137
|
+
module: "oaAnnouncement",
|
|
1138
|
+
moduleName: "公告",
|
|
1139
|
+
targetPath,
|
|
1140
|
+
body,
|
|
1141
|
+
data: await client.post(targetPath, body)
|
|
1142
|
+
};
|
|
1143
|
+
},
|
|
1144
|
+
crm_list_oa_logs: async (args) => {
|
|
1145
|
+
const targetPath = "/oaLog/queryList";
|
|
1146
|
+
return {
|
|
1147
|
+
module: "oaLog",
|
|
1148
|
+
moduleName: "日志",
|
|
1149
|
+
readonly: true,
|
|
1150
|
+
targetPath,
|
|
1151
|
+
data: await client.post(targetPath, buildOaLogListBody(args))
|
|
1152
|
+
};
|
|
1153
|
+
},
|
|
1154
|
+
crm_get_oa_log: async (args) => {
|
|
1155
|
+
const targetPath = "/oaLog/queryById";
|
|
1156
|
+
const query = { logId: requireOaLogId(args, "查询 OA 日志请提供 logId 或 id。") };
|
|
1157
|
+
return {
|
|
1158
|
+
module: "oaLog",
|
|
1159
|
+
moduleName: "日志",
|
|
1160
|
+
readonly: true,
|
|
1161
|
+
targetPath,
|
|
1162
|
+
query,
|
|
1163
|
+
data: await client.post(targetPath, undefined, query)
|
|
1164
|
+
};
|
|
1165
|
+
},
|
|
1166
|
+
crm_get_oa_log_information: async (args) => {
|
|
1167
|
+
const logId = requireOaLogId(args, "查询 OA 日志详情页字段请提供 logId 或 id。");
|
|
1168
|
+
const targetPath = `/oaLog/information/${logId}`;
|
|
1169
|
+
return {
|
|
1170
|
+
module: "oaLog",
|
|
1171
|
+
moduleName: "日志",
|
|
1172
|
+
readonly: true,
|
|
1173
|
+
targetPath,
|
|
1174
|
+
data: await client.post(targetPath)
|
|
1175
|
+
};
|
|
1176
|
+
},
|
|
1177
|
+
crm_list_oa_log_templates: async (args) => {
|
|
1178
|
+
const targetPath = "/oaLogTemplate/queryPartList";
|
|
1179
|
+
return {
|
|
1180
|
+
module: "oaLogTemplate",
|
|
1181
|
+
moduleName: "日志模板",
|
|
1182
|
+
readonly: true,
|
|
1183
|
+
targetPath,
|
|
1184
|
+
data: await client.post(targetPath, buildOaLogTemplateListBody(args))
|
|
1185
|
+
};
|
|
1186
|
+
},
|
|
1187
|
+
crm_get_oa_log_fields: async (args) => {
|
|
1188
|
+
const logId = args.logId ?? args.id;
|
|
1189
|
+
const targetPath = logId ? `/oaLogTemplateField/field/${logId}` : "/oaLogTemplateField/field";
|
|
1190
|
+
return {
|
|
1191
|
+
module: "oaLog",
|
|
1192
|
+
moduleName: "日志字段",
|
|
1193
|
+
readonly: true,
|
|
1194
|
+
targetPath,
|
|
1195
|
+
data: await client.post(targetPath, undefined, buildOaLogFieldQuery(args))
|
|
1196
|
+
};
|
|
1197
|
+
},
|
|
1198
|
+
crm_get_oa_log_welcome: async () => {
|
|
1199
|
+
const targetPath = "/oaLog/getLogWelcomeSpeech";
|
|
1200
|
+
return {
|
|
1201
|
+
module: "oaLog",
|
|
1202
|
+
moduleName: "日志欢迎语",
|
|
1203
|
+
readonly: true,
|
|
1204
|
+
targetPath,
|
|
1205
|
+
data: await client.post(targetPath)
|
|
1206
|
+
};
|
|
1207
|
+
},
|
|
1208
|
+
crm_get_oa_log_bulletin: async () => {
|
|
1209
|
+
const targetPath = "/oaLog/queryLogBulletin";
|
|
1210
|
+
return {
|
|
1211
|
+
module: "oaLog",
|
|
1212
|
+
moduleName: "日志概要",
|
|
1213
|
+
readonly: true,
|
|
1214
|
+
targetPath,
|
|
1215
|
+
data: await client.post(targetPath)
|
|
1216
|
+
};
|
|
1217
|
+
},
|
|
1218
|
+
crm_get_oa_log_complete_stats: async (args) => {
|
|
1219
|
+
const targetPath = "/oaLog/queryCompleteStats";
|
|
1220
|
+
const query = { configId: requireOaLogConfigId(args) };
|
|
1221
|
+
return {
|
|
1222
|
+
module: "oaLog",
|
|
1223
|
+
moduleName: "日志完成统计",
|
|
1224
|
+
readonly: true,
|
|
1225
|
+
targetPath,
|
|
1226
|
+
query,
|
|
1227
|
+
data: await client.post(targetPath, undefined, query)
|
|
1228
|
+
};
|
|
1229
|
+
},
|
|
1230
|
+
crm_list_oa_log_complete_records: async (args) => {
|
|
1231
|
+
const targetPath = "/oaLog/queryCompleteOaLogList";
|
|
1232
|
+
return {
|
|
1233
|
+
module: "oaLog",
|
|
1234
|
+
moduleName: "日志已完成列表",
|
|
1235
|
+
readonly: true,
|
|
1236
|
+
targetPath,
|
|
1237
|
+
data: await client.post(targetPath, buildOaLogListBody(args))
|
|
1238
|
+
};
|
|
1239
|
+
},
|
|
1240
|
+
crm_list_oa_log_incomplete_records: async (args) => {
|
|
1241
|
+
const targetPath = "/oaLog/queryIncompleteOaLogList";
|
|
1242
|
+
return {
|
|
1243
|
+
module: "oaLog",
|
|
1244
|
+
moduleName: "日志未完成列表",
|
|
1245
|
+
readonly: true,
|
|
1246
|
+
targetPath,
|
|
1247
|
+
data: await client.post(targetPath, buildOaLogListBody(args))
|
|
1248
|
+
};
|
|
1249
|
+
},
|
|
1250
|
+
crm_list_oa_examine_categories: async (args) => {
|
|
1251
|
+
const targetPath = "/examines/queryPartList";
|
|
1252
|
+
return {
|
|
1253
|
+
module: "oaExamine",
|
|
1254
|
+
moduleName: "办公审批",
|
|
1255
|
+
readonly: true,
|
|
1256
|
+
targetPath,
|
|
1257
|
+
data: await client.post(targetPath, buildOaExamineCategoryBody(args))
|
|
1258
|
+
};
|
|
1259
|
+
},
|
|
1260
|
+
crm_list_oa_examine_groups: async (args) => {
|
|
1261
|
+
const targetPath = "/examineSuperExamines/queryExamineGroup";
|
|
1262
|
+
return {
|
|
1263
|
+
module: "oaExamine",
|
|
1264
|
+
moduleName: "办公审批",
|
|
1265
|
+
readonly: true,
|
|
1266
|
+
targetPath,
|
|
1267
|
+
data: await client.post(targetPath, buildOaExamineGroupBody(args))
|
|
1268
|
+
};
|
|
1269
|
+
},
|
|
1270
|
+
crm_list_oa_examines: async (args) => {
|
|
1271
|
+
const listDefinition = getOaExamineListDefinition(args);
|
|
1272
|
+
return {
|
|
1273
|
+
module: "oaExamine",
|
|
1274
|
+
moduleName: "办公审批",
|
|
1275
|
+
readonly: true,
|
|
1276
|
+
listType: listDefinition.key,
|
|
1277
|
+
listName: listDefinition.name,
|
|
1278
|
+
targetPath: listDefinition.targetPath,
|
|
1279
|
+
data: await client.post(listDefinition.targetPath, buildOaExamineListBody(args))
|
|
1280
|
+
};
|
|
1281
|
+
},
|
|
1282
|
+
crm_get_oa_examine: async (args) => {
|
|
1283
|
+
const examineId = requireOaExamineId(args, "查询 OA 审批详情请提供 examineId 或 id。");
|
|
1284
|
+
const targetPath = `/oaExamine/queryOaExamineInfo/${examineId}`;
|
|
1285
|
+
return {
|
|
1286
|
+
module: "oaExamine",
|
|
1287
|
+
moduleName: "办公审批",
|
|
1288
|
+
readonly: true,
|
|
1289
|
+
targetPath,
|
|
1290
|
+
data: await client.post(targetPath)
|
|
1291
|
+
};
|
|
1292
|
+
},
|
|
1293
|
+
crm_get_oa_examine_fields: async (args) => {
|
|
1294
|
+
const categoryId = args.categoryId ?? args.examineIdForFields;
|
|
1295
|
+
if (!isBlank(categoryId) && isBlank(args.examineId) && isBlank(args.id)) {
|
|
1296
|
+
const useListEndpoint = args.useFieldList || args.fieldList || args.mode === "list";
|
|
1297
|
+
const targetPath = useListEndpoint ? `/oaExamineField/queryFieldList/${categoryId}` : `/oaExamineField/queryField/${categoryId}`;
|
|
1298
|
+
return {
|
|
1299
|
+
module: "oaExamine",
|
|
1300
|
+
moduleName: "办公审批",
|
|
1301
|
+
readonly: true,
|
|
1302
|
+
targetPath,
|
|
1303
|
+
data: await client.post(targetPath, useListEndpoint ? undefined : buildOaExamineCreateFieldQuery(args, categoryId))
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
const targetPath = "/oaExamine/getField";
|
|
1307
|
+
return {
|
|
1308
|
+
module: "oaExamine",
|
|
1309
|
+
moduleName: "办公审批",
|
|
1310
|
+
readonly: true,
|
|
1311
|
+
targetPath,
|
|
1312
|
+
data: await client.post(targetPath, buildOaExamineFieldBody(args))
|
|
1313
|
+
};
|
|
1314
|
+
},
|
|
1315
|
+
crm_list_oa_examine_record_logs: async (args) => {
|
|
1316
|
+
const targetPath = "/examineRecord/queryExamineRecordLog";
|
|
1317
|
+
return {
|
|
1318
|
+
module: "oaExamine",
|
|
1319
|
+
moduleName: "办公审批",
|
|
1320
|
+
readonly: true,
|
|
1321
|
+
targetPath,
|
|
1322
|
+
data: await client.post(targetPath, buildOaExamineRecordLogBody(args))
|
|
1323
|
+
};
|
|
1324
|
+
},
|
|
1325
|
+
crm_list_oa_examine_flow_records: async (args) => {
|
|
1326
|
+
const targetPath = "/examineSuperExamine/examine/record/list";
|
|
1327
|
+
return {
|
|
1328
|
+
module: "oaExamine",
|
|
1329
|
+
moduleName: "办公审批",
|
|
1330
|
+
readonly: true,
|
|
1331
|
+
targetPath,
|
|
1332
|
+
data: await client.post(targetPath, buildOaExamineFlowRecordBody(args))
|
|
1333
|
+
};
|
|
1334
|
+
},
|
|
1335
|
+
crm_list_oa_tasks: async (args) => ({
|
|
1336
|
+
module: "oaTask",
|
|
1337
|
+
targetPath: "/oaTask/queryTaskList",
|
|
1338
|
+
data: await client.post("/oaTask/queryTaskList", buildOaTaskListBody(args))
|
|
1339
|
+
}),
|
|
1340
|
+
crm_list_related_oa_tasks: async (args) => ({
|
|
1341
|
+
module: "oaTask",
|
|
1342
|
+
targetPath: "/oaTask/queryTypeTaskList",
|
|
1343
|
+
data: await client.post("/oaTask/queryTypeTaskList", undefined, buildRelatedOaTaskQuery(args))
|
|
1344
|
+
}),
|
|
1345
|
+
crm_preview_write: (args) => previewWrite(args),
|
|
1346
|
+
crm_update_customer: async (args) => {
|
|
1347
|
+
const moduleDefinition = getModuleByKey("customer");
|
|
1348
|
+
const targetPath = `${moduleDefinition.basePath}/updateInformation`;
|
|
1349
|
+
const directId = args.customerId ?? args.id;
|
|
1350
|
+
if (!args.confirm) {
|
|
1351
|
+
return previewWrite({
|
|
1352
|
+
...args,
|
|
1353
|
+
module: "customer",
|
|
1354
|
+
id: directId ?? "<需要通过客户查询唯一命中后填充>",
|
|
1355
|
+
operation: "updateInformation",
|
|
1356
|
+
targetPath
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
const resolvedRecord = directId ? undefined : await resolveUniqueRecord(client, moduleDefinition, args);
|
|
1360
|
+
const id = directId ?? resolvedRecord?.[moduleDefinition.primaryKey] ?? resolvedRecord?.id;
|
|
1361
|
+
const body = await buildUpdateInformationBody(client, moduleDefinition, { ...args, id });
|
|
1362
|
+
const data = await client.post(targetPath, body);
|
|
1363
|
+
const verification = args.verify === false ? undefined : await verifyInformationUpdate(client, moduleDefinition, body);
|
|
1364
|
+
return cleanObject({
|
|
1365
|
+
module: moduleDefinition.key,
|
|
1366
|
+
targetPath,
|
|
1367
|
+
resolvedRecord,
|
|
1368
|
+
body,
|
|
1369
|
+
data,
|
|
1370
|
+
verification
|
|
1371
|
+
});
|
|
1372
|
+
},
|
|
1373
|
+
crm_create_record: async (args) => {
|
|
1374
|
+
const moduleDefinition = getModuleByKey(args.module);
|
|
1375
|
+
ensureWritableModule(moduleDefinition);
|
|
1376
|
+
const targetPath = `${moduleDefinition.basePath}/add`;
|
|
1377
|
+
if (!args.confirm) {
|
|
1378
|
+
return previewWrite({ ...args, operation: "create", targetPath });
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
module: moduleDefinition.key,
|
|
1382
|
+
targetPath,
|
|
1383
|
+
data: await client.post(targetPath, buildModelSaveBody(args))
|
|
1384
|
+
};
|
|
1385
|
+
},
|
|
1386
|
+
crm_update_record_fields: async (args) => {
|
|
1387
|
+
const moduleDefinition = getModuleByKey(args.module);
|
|
1388
|
+
ensureWritableModule(moduleDefinition);
|
|
1389
|
+
const targetPath = `${moduleDefinition.basePath}/updateInformation`;
|
|
1390
|
+
if (!args.confirm) {
|
|
1391
|
+
return previewWrite({ ...args, operation: "updateInformation", targetPath });
|
|
1392
|
+
}
|
|
1393
|
+
const body = await buildUpdateInformationBody(client, moduleDefinition, args);
|
|
1394
|
+
const data = await client.post(targetPath, body);
|
|
1395
|
+
const verification = args.verify === false ? undefined : await verifyInformationUpdate(client, moduleDefinition, body);
|
|
1396
|
+
return {
|
|
1397
|
+
module: moduleDefinition.key,
|
|
1398
|
+
targetPath,
|
|
1399
|
+
body,
|
|
1400
|
+
data,
|
|
1401
|
+
verification
|
|
1402
|
+
};
|
|
1403
|
+
},
|
|
1404
|
+
crm_create_product: async (args) => executeCrmProductSave(client, args, false),
|
|
1405
|
+
crm_update_product: async (args) => executeCrmProductSave(client, args, true),
|
|
1406
|
+
crm_update_product_fields: async (args) => {
|
|
1407
|
+
const moduleDefinition = getModuleByKey("product");
|
|
1408
|
+
const id = args.productId ?? args.id;
|
|
1409
|
+
const targetPath = "/crmProduct/updateInformation";
|
|
1410
|
+
if (!args.confirm) {
|
|
1411
|
+
return previewWrite({ ...args, module: "product", id, operation: "updateInformation", targetPath });
|
|
1412
|
+
}
|
|
1413
|
+
const body = await buildUpdateInformationBody(client, moduleDefinition, { ...args, id });
|
|
1414
|
+
const data = await client.post(targetPath, body);
|
|
1415
|
+
const verification = args.verify === false ? undefined : await verifyInformationUpdate(client, moduleDefinition, body);
|
|
1416
|
+
return cleanObject({
|
|
1417
|
+
module: "product",
|
|
1418
|
+
moduleName: "产品",
|
|
1419
|
+
targetPath,
|
|
1420
|
+
body,
|
|
1421
|
+
data,
|
|
1422
|
+
verification
|
|
1423
|
+
});
|
|
1424
|
+
},
|
|
1425
|
+
crm_update_product_status: async (args) => executeCrmProductSimpleWrite(client, args, "status", "/crmProduct/updateStatus", buildCrmProductStatusBody(args)),
|
|
1426
|
+
crm_transfer_products_owner: async (args) => executeCrmProductSimpleWrite(client, args, "transferOwner", "/crmProduct/changeOwnerUser", buildCrmProductTransferBody(args)),
|
|
1427
|
+
crm_delete_products: async (args) => executeCrmProductSimpleWrite(client, args, "delete", "/crmProduct/deleteByIds", requireIdArray(args.ids ?? args.productIds, "删除 CRM 产品请提供 ids 或 productIds 数组。")),
|
|
1428
|
+
crm_create_contract: async (args) => executeCrmDocumentWrite(client, "contract", args, false),
|
|
1429
|
+
crm_update_contract: async (args) => executeCrmDocumentWrite(client, "contract", args, true),
|
|
1430
|
+
crm_create_receivables: async (args) => executeCrmDocumentWrite(client, "receivables", args, false),
|
|
1431
|
+
crm_update_receivables: async (args) => executeCrmDocumentWrite(client, "receivables", args, true),
|
|
1432
|
+
crm_create_receivables_plan: async (args) => executeCrmDocumentWrite(client, "receivablesPlan", args, false),
|
|
1433
|
+
crm_update_receivables_plan: async (args) => executeCrmDocumentWrite(client, "receivablesPlan", args, true),
|
|
1434
|
+
crm_create_invoice: async (args) => executeCrmDocumentWrite(client, "invoice", args, false),
|
|
1435
|
+
crm_update_invoice: async (args) => executeCrmDocumentWrite(client, "invoice", args, true),
|
|
1436
|
+
crm_create_quotation: async (args) => executeCrmDocumentWrite(client, "quotation", args, false),
|
|
1437
|
+
crm_update_quotation: async (args) => executeCrmDocumentWrite(client, "quotation", args, true),
|
|
1438
|
+
crm_create_calendar_event: async (args) => {
|
|
1439
|
+
const targetPath = "/oaEvent/save";
|
|
1440
|
+
const body = buildCalendarEventBody(args, false);
|
|
1441
|
+
if (!args.confirm) {
|
|
1442
|
+
return previewCalendarWrite("createCalendarEvent", targetPath, body);
|
|
1443
|
+
}
|
|
1444
|
+
return {
|
|
1445
|
+
module: "calendarEvent",
|
|
1446
|
+
targetPath,
|
|
1447
|
+
body,
|
|
1448
|
+
data: await client.post(targetPath, body)
|
|
1449
|
+
};
|
|
1450
|
+
},
|
|
1451
|
+
crm_update_calendar_event: async (args) => {
|
|
1452
|
+
const targetPath = "/oaEvent/update";
|
|
1453
|
+
const body = buildCalendarEventBody(args, true);
|
|
1454
|
+
if (!args.confirm) {
|
|
1455
|
+
return previewCalendarWrite("updateCalendarEvent", targetPath, body);
|
|
1456
|
+
}
|
|
1457
|
+
return {
|
|
1458
|
+
module: "calendarEvent",
|
|
1459
|
+
targetPath,
|
|
1460
|
+
body,
|
|
1461
|
+
data: await client.post(targetPath, body)
|
|
1462
|
+
};
|
|
1463
|
+
},
|
|
1464
|
+
crm_create_oa_log: async (args) => executeOaLogWrite(client, args, false),
|
|
1465
|
+
crm_update_oa_log: async (args) => executeOaLogWrite(client, args, true),
|
|
1466
|
+
crm_delete_oa_log: async (args) => {
|
|
1467
|
+
const targetPath = "/oaLog/deleteById";
|
|
1468
|
+
const query = { logId: requireOaLogId(args, "删除 OA 日志请提供 logId 或 id。") };
|
|
1469
|
+
if (!args.confirm) {
|
|
1470
|
+
return previewOaWrite("delete", "oaLog", "日志", targetPath, query);
|
|
1471
|
+
}
|
|
1472
|
+
return {
|
|
1473
|
+
module: "oaLog",
|
|
1474
|
+
moduleName: "日志",
|
|
1475
|
+
targetPath,
|
|
1476
|
+
query,
|
|
1477
|
+
data: await client.post(targetPath, undefined, query)
|
|
1478
|
+
};
|
|
1479
|
+
},
|
|
1480
|
+
crm_toggle_oa_log_favour: async (args) => {
|
|
1481
|
+
const targetPath = "/oaLog/favourOrCancel";
|
|
1482
|
+
const query = buildOaLogFavourQuery(args);
|
|
1483
|
+
if (!args.confirm) {
|
|
1484
|
+
return previewOaWrite("favourOrCancel", "oaLog", "日志", targetPath, query);
|
|
1485
|
+
}
|
|
1486
|
+
return {
|
|
1487
|
+
module: "oaLog",
|
|
1488
|
+
moduleName: "日志",
|
|
1489
|
+
targetPath,
|
|
1490
|
+
query,
|
|
1491
|
+
data: await client.post(targetPath, undefined, query)
|
|
1492
|
+
};
|
|
1493
|
+
},
|
|
1494
|
+
crm_create_oa_examine: async (args) => {
|
|
1495
|
+
const targetPath = "/oaExamine/setOaExamine";
|
|
1496
|
+
const body = await buildOaExamineWriteBody(client, args, false);
|
|
1497
|
+
if (!args.confirm) {
|
|
1498
|
+
return previewOaWrite("createOaExamine", "oaExamine", "办公审批", targetPath, body);
|
|
1499
|
+
}
|
|
1500
|
+
return {
|
|
1501
|
+
module: "oaExamine",
|
|
1502
|
+
moduleName: "办公审批",
|
|
1503
|
+
targetPath,
|
|
1504
|
+
body,
|
|
1505
|
+
data: await client.post(targetPath, body)
|
|
1506
|
+
};
|
|
1507
|
+
},
|
|
1508
|
+
crm_save_oa_examine_draft: async (args) => {
|
|
1509
|
+
const targetPath = "/oaExamine/setOaExamineDraft";
|
|
1510
|
+
const body = await buildOaExamineWriteBody(client, args, false);
|
|
1511
|
+
if (!args.confirm) {
|
|
1512
|
+
return previewOaWrite("saveOaExamineDraft", "oaExamine", "办公审批", targetPath, body);
|
|
1513
|
+
}
|
|
1514
|
+
return {
|
|
1515
|
+
module: "oaExamine",
|
|
1516
|
+
moduleName: "办公审批",
|
|
1517
|
+
targetPath,
|
|
1518
|
+
body,
|
|
1519
|
+
data: await client.post(targetPath, body)
|
|
1520
|
+
};
|
|
1521
|
+
},
|
|
1522
|
+
crm_audit_oa_examine: async (args) => {
|
|
1523
|
+
const targetPath = "/examineRecord/auditExamine";
|
|
1524
|
+
const body = buildOaExamineAuditBody(args, false);
|
|
1525
|
+
if (!args.confirm) {
|
|
1526
|
+
return previewOaWrite("auditOaExamine", "oaExamine", "办公审批", targetPath, body);
|
|
1527
|
+
}
|
|
1528
|
+
return {
|
|
1529
|
+
module: "oaExamine",
|
|
1530
|
+
moduleName: "办公审批",
|
|
1531
|
+
targetPath,
|
|
1532
|
+
body,
|
|
1533
|
+
data: await client.post(targetPath, body)
|
|
1534
|
+
};
|
|
1535
|
+
},
|
|
1536
|
+
crm_batch_audit_oa_examine: async (args) => {
|
|
1537
|
+
const targetPath = "/examineRecord/batchAuditExamine";
|
|
1538
|
+
const body = buildOaExamineAuditBody(args, true);
|
|
1539
|
+
if (!args.confirm) {
|
|
1540
|
+
return previewOaWrite("batchAuditOaExamine", "oaExamine", "办公审批", targetPath, body);
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
module: "oaExamine",
|
|
1544
|
+
moduleName: "办公审批",
|
|
1545
|
+
targetPath,
|
|
1546
|
+
body,
|
|
1547
|
+
data: await client.post(targetPath, body)
|
|
1548
|
+
};
|
|
1549
|
+
},
|
|
1550
|
+
crm_create_oa_task: async (args) => {
|
|
1551
|
+
const targetPath = "/workTask/saveWorkTask";
|
|
1552
|
+
const body = buildOaTaskBody(args);
|
|
1553
|
+
if (!args.confirm) {
|
|
1554
|
+
return previewOaTaskWrite("createOaTask", targetPath, body);
|
|
1555
|
+
}
|
|
1556
|
+
return {
|
|
1557
|
+
module: "oaTask",
|
|
1558
|
+
targetPath,
|
|
1559
|
+
body,
|
|
1560
|
+
data: await client.post(targetPath, body)
|
|
1561
|
+
};
|
|
1562
|
+
},
|
|
1563
|
+
crm_update_oa_task_fields: async (args) => {
|
|
1564
|
+
const calls = buildOaTaskUpdateCalls(args);
|
|
1565
|
+
if (!args.confirm) {
|
|
1566
|
+
return {
|
|
1567
|
+
preview: true,
|
|
1568
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
1569
|
+
module: "oaTask",
|
|
1570
|
+
moduleName: "OA任务",
|
|
1571
|
+
operation: "updateOaTaskFields",
|
|
1572
|
+
calls
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
const results = [];
|
|
1576
|
+
for (const call of calls) {
|
|
1577
|
+
results.push({
|
|
1578
|
+
targetPath: call.targetPath,
|
|
1579
|
+
data: await client.post(call.targetPath, call.body)
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
return {
|
|
1583
|
+
module: "oaTask",
|
|
1584
|
+
results
|
|
1585
|
+
};
|
|
1586
|
+
},
|
|
1587
|
+
crm_add_followup: async (args) => {
|
|
1588
|
+
const targetPath = "/crmActivity/add";
|
|
1589
|
+
if (!args.confirm) {
|
|
1590
|
+
return previewWrite({ ...args, operation: "addFollowup", targetPath });
|
|
1591
|
+
}
|
|
1592
|
+
return {
|
|
1593
|
+
module: "activity",
|
|
1594
|
+
targetPath,
|
|
1595
|
+
data: await client.post(targetPath, buildFollowupSaveBody(args))
|
|
1596
|
+
};
|
|
1597
|
+
},
|
|
1598
|
+
crm_update_followup: async (args) => {
|
|
1599
|
+
const targetPath = args.useInformationUpdate ? "/crmActivity/updateInformation" : "/crmActivity/update";
|
|
1600
|
+
if (!args.confirm) {
|
|
1601
|
+
return previewWrite({ ...args, operation: "updateFollowup", targetPath });
|
|
1602
|
+
}
|
|
1603
|
+
const body = args.useInformationUpdate
|
|
1604
|
+
? cleanObject({ id: args.id, label: 19, list: args.list ?? args.fields ?? [], batchId: args.batchId })
|
|
1605
|
+
: buildFollowupSaveBody(args);
|
|
1606
|
+
return {
|
|
1607
|
+
module: "activity",
|
|
1608
|
+
targetPath,
|
|
1609
|
+
data: await client.post(targetPath, body)
|
|
1610
|
+
};
|
|
1611
|
+
},
|
|
1612
|
+
workorder_search_records: async (args) => searchWorkorders(client, args),
|
|
1613
|
+
workorder_get_record: async (args) => getWorkorder(client, args),
|
|
1614
|
+
workorder_get_schema: async (args) => getWorkorderSchema(client, args),
|
|
1615
|
+
workorder_preview_write: async (args) => previewWorkorderWrite(args),
|
|
1616
|
+
workorder_create_record: async (args) => saveWorkorder(client, args, false),
|
|
1617
|
+
workorder_update_record: async (args) => saveWorkorder(client, args, true)
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
async function searchWorkorders(client, args) {
|
|
1621
|
+
const scope = normalizeWorkorderScope(args.scope, true);
|
|
1622
|
+
const body = cleanObject({
|
|
1623
|
+
page: positiveInteger(args.page, 1),
|
|
1624
|
+
limit: positiveInteger(args.limit, 15),
|
|
1625
|
+
search: args.search ?? args.keyword ?? args.query,
|
|
1626
|
+
statusId: args.statusId,
|
|
1627
|
+
status: args.status,
|
|
1628
|
+
templateId: args.templateId,
|
|
1629
|
+
priority: args.priority,
|
|
1630
|
+
sceneId: args.sceneId,
|
|
1631
|
+
viewMode: args.viewMode,
|
|
1632
|
+
startTime: args.startTime,
|
|
1633
|
+
endTime: args.endTime,
|
|
1634
|
+
isPool: scope === "pool" ? 1 : 0
|
|
1635
|
+
});
|
|
1636
|
+
const targetPath = scope === "offline" ? "/wkoOfflineWorkorder/queryPageList" : "/wkoWorkorder/queryPageList";
|
|
1637
|
+
return {
|
|
1638
|
+
module: "workorder",
|
|
1639
|
+
scope,
|
|
1640
|
+
readonly: true,
|
|
1641
|
+
targetPath,
|
|
1642
|
+
data: await client.post(targetPath, body)
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
async function getWorkorder(client, args) {
|
|
1646
|
+
const scope = normalizeWorkorderScope(args.scope, false);
|
|
1647
|
+
const id = requiredWorkorderId(args.id);
|
|
1648
|
+
const prefix = scope === "offline" ? "/wkoOfflineWorkorder" : "/wkoWorkorder";
|
|
1649
|
+
const [record, information] = await Promise.all([
|
|
1650
|
+
client.post(`${prefix}/queryById/${id}`),
|
|
1651
|
+
client.post(`${prefix}/information/${id}`)
|
|
1652
|
+
]);
|
|
1653
|
+
return {
|
|
1654
|
+
module: "workorder",
|
|
1655
|
+
scope,
|
|
1656
|
+
id,
|
|
1657
|
+
record,
|
|
1658
|
+
information
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
async function getWorkorderSchema(client, args) {
|
|
1662
|
+
const scope = normalizeWorkorderScope(args.scope, false);
|
|
1663
|
+
const prefix = scope === "offline" ? "/wkoOfflineWorkorder" : "/wkoWorkorder";
|
|
1664
|
+
const id = args.id === undefined || args.id === null || args.id === "" ? "" : `/${requiredWorkorderId(args.id)}`;
|
|
1665
|
+
return {
|
|
1666
|
+
module: "workorder",
|
|
1667
|
+
scope,
|
|
1668
|
+
readonly: true,
|
|
1669
|
+
targetPath: `${prefix}/field${id}`,
|
|
1670
|
+
fields: await client.post(`${prefix}/field${id}`, cleanObject({ templateId: args.templateId }))
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
function previewWorkorderWrite(args) {
|
|
1674
|
+
const update = Boolean(args.id ?? args.workorderId ?? args.offlineWorkorderId);
|
|
1675
|
+
const prepared = prepareWorkorderWrite(args, update);
|
|
1676
|
+
return {
|
|
1677
|
+
preview: true,
|
|
1678
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用工单写接口。",
|
|
1679
|
+
...prepared
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
async function saveWorkorder(client, args, update) {
|
|
1683
|
+
const prepared = prepareWorkorderWrite(args, update);
|
|
1684
|
+
if (args.confirm !== true) {
|
|
1685
|
+
return {
|
|
1686
|
+
preview: true,
|
|
1687
|
+
message: "写入前预览:请核对字段并传 confirm=true 后再执行。",
|
|
1688
|
+
...prepared
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
const data = await client.post(prepared.targetPath, prepared.body);
|
|
1692
|
+
const id = extractSavedWorkorderId(data, prepared);
|
|
1693
|
+
const verification = id
|
|
1694
|
+
? await getWorkorder(client, { scope: prepared.scope, id })
|
|
1695
|
+
: { ok: false, message: "服务端未返回工单 ID,请使用列表查询核对写入结果。" };
|
|
1696
|
+
return {
|
|
1697
|
+
preview: false,
|
|
1698
|
+
scope: prepared.scope,
|
|
1699
|
+
targetPath: prepared.targetPath,
|
|
1700
|
+
data,
|
|
1701
|
+
verification
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
function prepareWorkorderWrite(args, update) {
|
|
1705
|
+
const scope = normalizeWorkorderScope(args.scope, false);
|
|
1706
|
+
if (scope === "offline") {
|
|
1707
|
+
const source = safeExtra(args.body ?? args.record ?? args.data);
|
|
1708
|
+
const id = update ? requiredWorkorderId(args.id ?? args.offlineWorkorderId ?? source.offlineWorkorderId) : undefined;
|
|
1709
|
+
const body = cleanObject({ ...source, ...(id ? { offlineWorkorderId: id } : {}) });
|
|
1710
|
+
return { scope, targetPath: `/wkoOfflineWorkorder/${update ? "update" : "add"}`, body };
|
|
1711
|
+
}
|
|
1712
|
+
const sourceBody = safeExtra(args.body);
|
|
1713
|
+
const sourceEntity = safeExtra(sourceBody.entity ?? args.entity ?? args.record ?? args.data);
|
|
1714
|
+
const id = update ? requiredWorkorderId(args.id ?? args.workorderId ?? sourceEntity.workorderId) : undefined;
|
|
1715
|
+
const entity = cleanObject({ ...sourceEntity, ...(id ? { workorderId: id } : {}) });
|
|
1716
|
+
const body = Object.keys(sourceBody).length > 0
|
|
1717
|
+
? { ...sourceBody, entity }
|
|
1718
|
+
: cleanObject({
|
|
1719
|
+
entity,
|
|
1720
|
+
field: args.field ?? args.fields ?? [],
|
|
1721
|
+
ownerUserList: args.ownerUserList,
|
|
1722
|
+
member: args.member,
|
|
1723
|
+
saveType: args.saveType
|
|
1724
|
+
});
|
|
1725
|
+
return { scope, targetPath: `/wkoWorkorder/${update ? "update" : "add"}`, body };
|
|
1726
|
+
}
|
|
1727
|
+
function normalizeWorkorderScope(value, allowPool) {
|
|
1728
|
+
const scope = String(value ?? "workorder").trim();
|
|
1729
|
+
const allowed = allowPool ? ["workorder", "pool", "offline"] : ["workorder", "offline"];
|
|
1730
|
+
if (!allowed.includes(scope)) {
|
|
1731
|
+
throw new Error(`不支持的工单范围:${scope}。可选值:${allowed.join("、")}。`);
|
|
1732
|
+
}
|
|
1733
|
+
return scope;
|
|
1734
|
+
}
|
|
1735
|
+
function requiredWorkorderId(value) {
|
|
1736
|
+
const id = String(value ?? "").trim();
|
|
1737
|
+
if (!id) {
|
|
1738
|
+
throw new Error("工单 ID 不能为空。");
|
|
1739
|
+
}
|
|
1740
|
+
return id;
|
|
1741
|
+
}
|
|
1742
|
+
function extractSavedWorkorderId(data, prepared) {
|
|
1743
|
+
if (prepared.scope === "offline") {
|
|
1744
|
+
return data?.offlineWorkorderId ?? prepared.body.offlineWorkorderId;
|
|
1745
|
+
}
|
|
1746
|
+
return data?.workorderId ?? prepared.body.entity?.workorderId;
|
|
1747
|
+
}
|
|
1748
|
+
const FIELD_SEARCH_CONTAINS = 3;
|
|
1749
|
+
const QUICK_SEARCH_FIELDS = {
|
|
1750
|
+
leads: [
|
|
1751
|
+
{ fieldName: "leadsName", formType: "text" },
|
|
1752
|
+
{ fieldName: "mobile", formType: "mobile" },
|
|
1753
|
+
{ fieldName: "telephone", formType: "text" }
|
|
1754
|
+
],
|
|
1755
|
+
leadsPool: [
|
|
1756
|
+
{ fieldName: "leadsName", formType: "text" },
|
|
1757
|
+
{ fieldName: "mobile", formType: "mobile" },
|
|
1758
|
+
{ fieldName: "telephone", formType: "text" }
|
|
1759
|
+
],
|
|
1760
|
+
customer: [
|
|
1761
|
+
{ fieldName: "customerName", formType: "text" },
|
|
1762
|
+
{ fieldName: "mobile", formType: "mobile" },
|
|
1763
|
+
{ fieldName: "telephone", formType: "text" }
|
|
1764
|
+
],
|
|
1765
|
+
customerPool: [
|
|
1766
|
+
{ fieldName: "customerName", formType: "text" },
|
|
1767
|
+
{ fieldName: "mobile", formType: "mobile" },
|
|
1768
|
+
{ fieldName: "telephone", formType: "text" }
|
|
1769
|
+
],
|
|
1770
|
+
contacts: [
|
|
1771
|
+
{ fieldName: "name", formType: "text" },
|
|
1772
|
+
{ fieldName: "mainFieldValue", formType: "text" },
|
|
1773
|
+
{ fieldName: "mobile", formType: "mobile" },
|
|
1774
|
+
{ fieldName: "telephone", formType: "text" },
|
|
1775
|
+
{ fieldName: "email", formType: "email" }
|
|
1776
|
+
],
|
|
1777
|
+
business: [{ fieldName: "businessName", formType: "text" }],
|
|
1778
|
+
product: [{ fieldName: "name", formType: "text" }],
|
|
1779
|
+
contract: [
|
|
1780
|
+
{ fieldName: "num", formType: "text" },
|
|
1781
|
+
{ fieldName: "name", formType: "text" },
|
|
1782
|
+
{ fieldName: "customerName", formType: "text" }
|
|
1783
|
+
],
|
|
1784
|
+
receivables: [
|
|
1785
|
+
{ fieldName: "number", formType: "text" },
|
|
1786
|
+
{ fieldName: "customerName", formType: "text" }
|
|
1787
|
+
],
|
|
1788
|
+
receivablesPlan: [
|
|
1789
|
+
{ fieldName: "customerName", formType: "text" },
|
|
1790
|
+
{ fieldName: "contractNum", formType: "text" }
|
|
1791
|
+
],
|
|
1792
|
+
invoice: [
|
|
1793
|
+
{ fieldName: "invoiceApplyNumber", formType: "text" },
|
|
1794
|
+
{ fieldName: "invoiceNumber", formType: "text" },
|
|
1795
|
+
{ fieldName: "customerName", formType: "text" }
|
|
1796
|
+
],
|
|
1797
|
+
activity: [{ fieldName: "content", formType: "text" }],
|
|
1798
|
+
quotation: [
|
|
1799
|
+
{ fieldName: "name", formType: "text" },
|
|
1800
|
+
{ fieldName: "num", formType: "text" },
|
|
1801
|
+
{ fieldName: "customerName", formType: "text" }
|
|
1802
|
+
],
|
|
1803
|
+
returnVisit: [
|
|
1804
|
+
{ fieldName: "customerName", formType: "text" },
|
|
1805
|
+
{ fieldName: "contractNum", formType: "text" }
|
|
1806
|
+
],
|
|
1807
|
+
visitPlan: [
|
|
1808
|
+
{ fieldName: "customerName", formType: "text" },
|
|
1809
|
+
{ fieldName: "contactsName", formType: "text" }
|
|
1810
|
+
]
|
|
1811
|
+
};
|
|
1812
|
+
const NAME_FIELD_BY_MODULE = {
|
|
1813
|
+
leads: { fieldName: "leadsName", formType: "text" },
|
|
1814
|
+
leadsPool: { fieldName: "leadsName", formType: "text" },
|
|
1815
|
+
customer: { fieldName: "customerName", formType: "text" },
|
|
1816
|
+
customerPool: { fieldName: "customerName", formType: "text" },
|
|
1817
|
+
contacts: { fieldName: "name", formType: "text" },
|
|
1818
|
+
business: { fieldName: "businessName", formType: "text" },
|
|
1819
|
+
product: { fieldName: "name", formType: "text" },
|
|
1820
|
+
contract: { fieldName: "name", formType: "text" },
|
|
1821
|
+
quotation: { fieldName: "name", formType: "text" }
|
|
1822
|
+
};
|
|
1823
|
+
const USER_FILTER_FORM_TYPES = new Set(["user", "single_user"]);
|
|
1824
|
+
const USER_FILTER_FIELD_NAMES = new Set(["ownerUserId", "createUserId"]);
|
|
1825
|
+
const USER_FILTER_ARGUMENTS = [
|
|
1826
|
+
{
|
|
1827
|
+
fieldName: "ownerUserId",
|
|
1828
|
+
idKeys: ["ownerUserId", "ownerUserIds"],
|
|
1829
|
+
nicknameKeys: ["ownerUserName", "ownerUserNames", "ownerName", "ownerNames"]
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
fieldName: "createUserId",
|
|
1833
|
+
idKeys: ["createUserId", "createUserIds", "creatorUserId", "creatorUserIds"],
|
|
1834
|
+
nicknameKeys: ["createUserName", "createUserNames", "creatorUserName", "creatorUserNames", "creatorName", "creatorNames"]
|
|
1835
|
+
}
|
|
1836
|
+
];
|
|
1837
|
+
async function buildResolvedSearchList(client, args, moduleDefinition) {
|
|
1838
|
+
const searchList = buildSearchList(args, moduleDefinition);
|
|
1839
|
+
const userSearchList = buildConvenienceUserSearchList(args);
|
|
1840
|
+
return resolveUserSearchFilters(client, combineSearchLists(searchList, userSearchList), args.companyId);
|
|
1841
|
+
}
|
|
1842
|
+
function buildSearchList(args, moduleDefinition) {
|
|
1843
|
+
const explicitSearchList = toArray(args.filters ?? args.searchList);
|
|
1844
|
+
const semanticSearchList = buildSemanticSearchList(args, moduleDefinition);
|
|
1845
|
+
return combineSearchLists(explicitSearchList, semanticSearchList);
|
|
1846
|
+
}
|
|
1847
|
+
function buildConvenienceUserSearchList(args) {
|
|
1848
|
+
return USER_FILTER_ARGUMENTS.flatMap(({ fieldName, idKeys, nicknameKeys }) => {
|
|
1849
|
+
const values = [...argumentValues(args, idKeys), ...argumentValues(args, nicknameKeys)];
|
|
1850
|
+
if (values.length === 0) {
|
|
1851
|
+
return [];
|
|
1852
|
+
}
|
|
1853
|
+
return [{
|
|
1854
|
+
fieldName,
|
|
1855
|
+
formType: "user",
|
|
1856
|
+
type: 1,
|
|
1857
|
+
searchEnum: 1,
|
|
1858
|
+
values
|
|
1859
|
+
}];
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
function argumentValues(args, keys) {
|
|
1863
|
+
return keys.flatMap((key) => valueArray(args[key]));
|
|
1864
|
+
}
|
|
1865
|
+
function valueArray(value) {
|
|
1866
|
+
if (value === undefined || value === null) {
|
|
1867
|
+
return [];
|
|
1868
|
+
}
|
|
1869
|
+
return Array.isArray(value) ? value : [value];
|
|
1870
|
+
}
|
|
1871
|
+
function combineSearchLists(left, right) {
|
|
1872
|
+
if (left.length === 0) {
|
|
1873
|
+
return right;
|
|
1874
|
+
}
|
|
1875
|
+
if (right.length === 0) {
|
|
1876
|
+
return left;
|
|
1877
|
+
}
|
|
1878
|
+
const leftGroups = groupSearchFilters(left);
|
|
1879
|
+
const rightGroups = groupSearchFilters(right);
|
|
1880
|
+
const result = [];
|
|
1881
|
+
let groupId = 0;
|
|
1882
|
+
for (const leftGroup of leftGroups) {
|
|
1883
|
+
for (const rightGroup of rightGroups) {
|
|
1884
|
+
for (const filter of [...leftGroup, ...rightGroup]) {
|
|
1885
|
+
result.push({ ...filter, groupId });
|
|
1886
|
+
}
|
|
1887
|
+
groupId += 1;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
return result;
|
|
1891
|
+
}
|
|
1892
|
+
function groupSearchFilters(filters) {
|
|
1893
|
+
const groups = new Map();
|
|
1894
|
+
for (const filter of filters) {
|
|
1895
|
+
const groupId = filter.groupId ?? 0;
|
|
1896
|
+
const group = groups.get(groupId) ?? [];
|
|
1897
|
+
group.push(filter);
|
|
1898
|
+
groups.set(groupId, group);
|
|
1899
|
+
}
|
|
1900
|
+
return [...groups.values()];
|
|
1901
|
+
}
|
|
1902
|
+
async function resolveUserSearchFilters(client, searchList, companyId) {
|
|
1903
|
+
const nicknameCache = new Map();
|
|
1904
|
+
const resolvedFilters = [];
|
|
1905
|
+
for (const filter of searchList) {
|
|
1906
|
+
if (!isUserSearchFilter(filter)) {
|
|
1907
|
+
resolvedFilters.push(filter);
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
const rawValues = valueArray(filter.values ?? filter.value);
|
|
1911
|
+
const searchType = Number(filter.type ?? filter.searchEnum);
|
|
1912
|
+
if (rawValues.length === 0 && (searchType === 5 || searchType === 6)) {
|
|
1913
|
+
resolvedFilters.push({ ...filter, formType: filter.formType ?? "user" });
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
if (rawValues.length === 0) {
|
|
1917
|
+
throw new Error(`用户类型筛选“${filter.fieldName ?? filter.name ?? "未知字段"}”请提供 values。`);
|
|
1918
|
+
}
|
|
1919
|
+
const values = [];
|
|
1920
|
+
for (const value of rawValues) {
|
|
1921
|
+
values.push(await resolveUserFilterValue(client, value, nicknameCache, companyId));
|
|
1922
|
+
}
|
|
1923
|
+
resolvedFilters.push({
|
|
1924
|
+
...filter,
|
|
1925
|
+
formType: filter.formType ?? "user",
|
|
1926
|
+
values: [...new Set(values)]
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
return resolvedFilters;
|
|
1930
|
+
}
|
|
1931
|
+
function isUserSearchFilter(filter) {
|
|
1932
|
+
const formType = String(filter.formType ?? "").trim().toLowerCase();
|
|
1933
|
+
return USER_FILTER_FORM_TYPES.has(formType) || USER_FILTER_FIELD_NAMES.has(String(filter.fieldName ?? filter.name ?? ""));
|
|
1934
|
+
}
|
|
1935
|
+
async function resolveUserFilterValue(client, value, nicknameCache, companyId) {
|
|
1936
|
+
const objectValue = isPlainObject(value) ? value : undefined;
|
|
1937
|
+
const idValue = objectValue ? objectValue.userId ?? objectValue.id : value;
|
|
1938
|
+
const userId = normalizePositiveUserId(idValue);
|
|
1939
|
+
if (userId) {
|
|
1940
|
+
return userId;
|
|
1941
|
+
}
|
|
1942
|
+
const nicknameValue = objectValue
|
|
1943
|
+
? objectValue.realname ?? objectValue.nickname ?? objectValue.name
|
|
1944
|
+
: value;
|
|
1945
|
+
const nickname = firstTextValue(nicknameValue);
|
|
1946
|
+
if (!nickname) {
|
|
1947
|
+
throw new Error("用户类型筛选值必须是正整数用户 ID、昵称字符串,或包含 userId/realname 的对象。");
|
|
1948
|
+
}
|
|
1949
|
+
return resolveUserIdByNickname(client, nickname, nicknameCache, companyId);
|
|
1950
|
+
}
|
|
1951
|
+
function normalizePositiveUserId(value) {
|
|
1952
|
+
if (typeof value === "number") {
|
|
1953
|
+
return Number.isSafeInteger(value) && value > 0 ? String(value) : undefined;
|
|
1954
|
+
}
|
|
1955
|
+
if (typeof value !== "string") {
|
|
1956
|
+
return undefined;
|
|
1957
|
+
}
|
|
1958
|
+
const text = value.trim();
|
|
1959
|
+
return /^[1-9]\d*$/.test(text) ? text : undefined;
|
|
1960
|
+
}
|
|
1961
|
+
async function resolveUserIdByNickname(client, nickname, cache, companyId) {
|
|
1962
|
+
const normalizedNickname = normalizeText(nickname);
|
|
1963
|
+
const cached = cache.get(normalizedNickname);
|
|
1964
|
+
if (cached) {
|
|
1965
|
+
return cached;
|
|
1966
|
+
}
|
|
1967
|
+
const page = await client.post("/adminUser/queryUserList", {
|
|
1968
|
+
page: 1,
|
|
1969
|
+
limit: 100,
|
|
1970
|
+
pageType: 1,
|
|
1971
|
+
realname: nickname,
|
|
1972
|
+
companyId
|
|
1973
|
+
});
|
|
1974
|
+
const users = Array.isArray(page.list)
|
|
1975
|
+
? page.list
|
|
1976
|
+
: Array.isArray(page.records)
|
|
1977
|
+
? page.records
|
|
1978
|
+
: [];
|
|
1979
|
+
const exactMatches = users.filter((user) => normalizeText(user.realname ?? user.name) === normalizedNickname);
|
|
1980
|
+
const validMatches = exactMatches.filter((user) => normalizePositiveUserId(user.userId ?? user.id));
|
|
1981
|
+
if (validMatches.length === 1) {
|
|
1982
|
+
const userId = normalizePositiveUserId(validMatches[0].userId ?? validMatches[0].id);
|
|
1983
|
+
cache.set(normalizedNickname, userId);
|
|
1984
|
+
return userId;
|
|
1985
|
+
}
|
|
1986
|
+
if (validMatches.length > 1) {
|
|
1987
|
+
throw new Error(`昵称“${nickname}”匹配到多个用户:${formatUserCandidates(validMatches)}。请直接传入明确的用户 ID。`);
|
|
1988
|
+
}
|
|
1989
|
+
const suggestions = users.filter((user) => normalizePositiveUserId(user.userId ?? user.id)).slice(0, 5);
|
|
1990
|
+
const suggestionText = suggestions.length > 0 ? ` 相近用户:${formatUserCandidates(suggestions)}。` : "";
|
|
1991
|
+
throw new Error(`未找到昵称完全等于“${nickname}”的用户。${suggestionText}请先使用 crm_list_users 核对昵称,或直接传入用户 ID。`);
|
|
1992
|
+
}
|
|
1993
|
+
function formatUserCandidates(users) {
|
|
1994
|
+
return users.map((user) => {
|
|
1995
|
+
const userId = normalizePositiveUserId(user.userId ?? user.id);
|
|
1996
|
+
const realname = firstTextValue(user.realname, user.name) ?? "未命名";
|
|
1997
|
+
const deptName = firstTextValue(user.deptName);
|
|
1998
|
+
return `${realname}(ID: ${userId}${deptName ? `, 部门: ${deptName}` : ""})`;
|
|
1999
|
+
}).join(";");
|
|
2000
|
+
}
|
|
2001
|
+
function buildSemanticSearchList(args, moduleDefinition) {
|
|
2002
|
+
const keyword = firstTextValue(args.keyword, args.query, args.q, args.search, args.searchText, args.text, args.term);
|
|
2003
|
+
if (keyword) {
|
|
2004
|
+
return containsFilters(QUICK_SEARCH_FIELDS[moduleDefinition.key] ?? [], keyword, 1);
|
|
2005
|
+
}
|
|
2006
|
+
const phone = firstTextValue(args.phone, args.mobile, args.telephone);
|
|
2007
|
+
if (phone) {
|
|
2008
|
+
const fields = (QUICK_SEARCH_FIELDS[moduleDefinition.key] ?? []).filter((field) => ["mobile", "telephone", "phone"].includes(field.fieldName));
|
|
2009
|
+
return containsFilters(fields, phone, 1);
|
|
2010
|
+
}
|
|
2011
|
+
const name = firstTextValue(args.name, args.customerName, args.leadsName, args.contactsName, args.businessName, args.productName, args.contractName, args.quotationName);
|
|
2012
|
+
const nameField = NAME_FIELD_BY_MODULE[moduleDefinition.key];
|
|
2013
|
+
if (name && nameField) {
|
|
2014
|
+
return containsFilters([nameField], name, 0);
|
|
2015
|
+
}
|
|
2016
|
+
const email = firstTextValue(args.email);
|
|
2017
|
+
if (email) {
|
|
2018
|
+
return containsFilters([{ fieldName: "email", formType: "email" }], email, 0);
|
|
2019
|
+
}
|
|
2020
|
+
return [];
|
|
2021
|
+
}
|
|
2022
|
+
function containsFilters(fields, value, startGroupId) {
|
|
2023
|
+
return fields.map((field, index) => ({
|
|
2024
|
+
fieldName: field.fieldName,
|
|
2025
|
+
formType: field.formType,
|
|
2026
|
+
type: FIELD_SEARCH_CONTAINS,
|
|
2027
|
+
searchEnum: FIELD_SEARCH_CONTAINS,
|
|
2028
|
+
values: [value],
|
|
2029
|
+
groupId: fields.length > 1 ? startGroupId + index : startGroupId
|
|
2030
|
+
}));
|
|
2031
|
+
}
|
|
2032
|
+
function firstTextValue(...values) {
|
|
2033
|
+
for (const value of values) {
|
|
2034
|
+
if (value === undefined || value === null) {
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
const text = String(value).trim();
|
|
2038
|
+
if (text) {
|
|
2039
|
+
return text;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return undefined;
|
|
2043
|
+
}
|
|
2044
|
+
function toArray(value) {
|
|
2045
|
+
if (Array.isArray(value)) {
|
|
2046
|
+
return value;
|
|
2047
|
+
}
|
|
2048
|
+
if (value && typeof value === "object") {
|
|
2049
|
+
return [value];
|
|
2050
|
+
}
|
|
2051
|
+
return [];
|
|
2052
|
+
}
|
|
2053
|
+
async function resolveUniqueRecord(client, moduleDefinition, args) {
|
|
2054
|
+
const searchList = await resolveUserSearchFilters(client, buildSearchList(args, moduleDefinition), args.companyId);
|
|
2055
|
+
if (searchList.length === 0) {
|
|
2056
|
+
throw new Error(`请提供可定位${moduleDefinition.name}的 customerId/id、phone/mobile、name/customerName 或 keyword。`);
|
|
2057
|
+
}
|
|
2058
|
+
const page = await client.post(`${moduleDefinition.basePath}/queryPageList`, cleanObject({
|
|
2059
|
+
page: 1,
|
|
2060
|
+
limit: 2,
|
|
2061
|
+
pageType: 1,
|
|
2062
|
+
label: moduleDefinition.label,
|
|
2063
|
+
searchList
|
|
2064
|
+
}));
|
|
2065
|
+
const list = Array.isArray(page.list) ? page.list : [];
|
|
2066
|
+
if (list.length !== 1) {
|
|
2067
|
+
throw new Error(`需要唯一命中的${moduleDefinition.name}才能写入,当前命中 ${page.totalRow ?? list.length} 条,请先缩小查询条件或直接传入 ID。`);
|
|
2068
|
+
}
|
|
2069
|
+
return list[0];
|
|
2070
|
+
}
|
|
2071
|
+
async function buildUpdateInformationBody(client, moduleDefinition, args) {
|
|
2072
|
+
const rawBody = buildUpdateInformationPreviewBody(moduleDefinition, args);
|
|
2073
|
+
const fields = await fetchModuleFields(client, moduleDefinition, args);
|
|
2074
|
+
const list = hydrateUpdateList(rawBody.list ?? [], fields);
|
|
2075
|
+
const batchId = rawBody.batchId ?? await resolveUpdateBatchId(client, moduleDefinition, rawBody, list);
|
|
2076
|
+
return {
|
|
2077
|
+
...rawBody,
|
|
2078
|
+
batchId,
|
|
2079
|
+
list
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
function buildUpdateInformationPreviewBody(moduleDefinition, args) {
|
|
2083
|
+
return cleanObject({
|
|
2084
|
+
id: args.id ?? args[moduleDefinition.primaryKey],
|
|
2085
|
+
label: moduleDefinition.label,
|
|
2086
|
+
list: rawUpdateList(args),
|
|
2087
|
+
batchId: args.batchId,
|
|
2088
|
+
idList: args.idList,
|
|
2089
|
+
ownerUserSave: args.ownerUserSave
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
function rawUpdateList(args) {
|
|
2093
|
+
const raw = args.list ?? args.fields ?? args.field;
|
|
2094
|
+
const list = toArray(raw);
|
|
2095
|
+
if (list.length > 0) {
|
|
2096
|
+
return list;
|
|
2097
|
+
}
|
|
2098
|
+
const updates = args.updates;
|
|
2099
|
+
if (!updates || typeof updates !== "object" || Array.isArray(updates)) {
|
|
2100
|
+
return [];
|
|
2101
|
+
}
|
|
2102
|
+
return Object.entries(updates).map(([key, value]) => looksLikeFieldName(key) ? { fieldName: key, value } : { name: key, value });
|
|
2103
|
+
}
|
|
2104
|
+
function looksLikeFieldName(value) {
|
|
2105
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
|
|
2106
|
+
}
|
|
2107
|
+
async function fetchModuleFields(client, moduleDefinition, args) {
|
|
2108
|
+
const id = args.id ?? args[moduleDefinition.primaryKey];
|
|
2109
|
+
const path = id ? `${moduleDefinition.basePath}/field/${id}` : `${moduleDefinition.basePath}/field`;
|
|
2110
|
+
const data = await client.post(path, undefined, cleanQuery({ type: args.type, poolId: args.poolId }));
|
|
2111
|
+
return flattenFieldDefinitions(data);
|
|
2112
|
+
}
|
|
2113
|
+
function flattenFieldDefinitions(input) {
|
|
2114
|
+
const output = [];
|
|
2115
|
+
const seen = new Set();
|
|
2116
|
+
const visit = (value) => {
|
|
2117
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
seen.add(value);
|
|
2121
|
+
if (Array.isArray(value)) {
|
|
2122
|
+
value.forEach(visit);
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
const record = value;
|
|
2126
|
+
if (record.fieldName || record.fieldId || (record.name && (record.fieldType !== undefined || record.type !== undefined || record.formType))) {
|
|
2127
|
+
output.push(record);
|
|
2128
|
+
}
|
|
2129
|
+
for (const child of Object.values(record)) {
|
|
2130
|
+
if (child && typeof child === "object") {
|
|
2131
|
+
visit(child);
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
};
|
|
2135
|
+
visit(input);
|
|
2136
|
+
return output;
|
|
2137
|
+
}
|
|
2138
|
+
function hydrateUpdateList(list, fields) {
|
|
2139
|
+
return list.map((item) => hydrateUpdateItem(item, fields));
|
|
2140
|
+
}
|
|
2141
|
+
function hydrateUpdateItem(item, fields) {
|
|
2142
|
+
const field = findFieldDefinition(item, fields);
|
|
2143
|
+
if (!field && hasUpdateMetadata(item)) {
|
|
2144
|
+
return normalizeUpdateItem(item);
|
|
2145
|
+
}
|
|
2146
|
+
if (!field) {
|
|
2147
|
+
const fieldLabel = item.name ?? item.fieldName ?? item.fieldId ?? "<unknown>";
|
|
2148
|
+
throw new Error(`未在字段 schema 中找到可写字段:${fieldLabel}。请先调用 crm_get_module_schema 确认字段名。`);
|
|
2149
|
+
}
|
|
2150
|
+
const merged = cleanObject({
|
|
2151
|
+
fieldId: item.fieldId ?? field.fieldId ?? field.id,
|
|
2152
|
+
fieldName: item.fieldName ?? field.fieldName,
|
|
2153
|
+
name: item.name ?? field.name,
|
|
2154
|
+
fieldType: item.fieldType ?? field.fieldType,
|
|
2155
|
+
type: item.type ?? field.type,
|
|
2156
|
+
formType: item.formType ?? field.formType,
|
|
2157
|
+
value: normalizeOptionValue(item.value, field)
|
|
2158
|
+
});
|
|
2159
|
+
return normalizeUpdateItem(merged);
|
|
2160
|
+
}
|
|
2161
|
+
function hasUpdateMetadata(item) {
|
|
2162
|
+
return Boolean(item.fieldName && item.fieldType !== undefined && item.type !== undefined);
|
|
2163
|
+
}
|
|
2164
|
+
function normalizeUpdateItem(item) {
|
|
2165
|
+
return cleanObject({
|
|
2166
|
+
fieldId: item.fieldId,
|
|
2167
|
+
fieldName: item.fieldName,
|
|
2168
|
+
name: item.name,
|
|
2169
|
+
fieldType: item.fieldType,
|
|
2170
|
+
type: item.type,
|
|
2171
|
+
formType: item.formType,
|
|
2172
|
+
value: item.value
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
async function resolveUpdateBatchId(client, moduleDefinition, body, list) {
|
|
2176
|
+
if (body.batchId || !body.id || !updateNeedsBatchId(list)) {
|
|
2177
|
+
return undefined;
|
|
2178
|
+
}
|
|
2179
|
+
const data = await client.post(`${moduleDefinition.basePath}/queryById/${body.id}`);
|
|
2180
|
+
return findFirstValueByKey(data, "batchId");
|
|
2181
|
+
}
|
|
2182
|
+
function updateNeedsBatchId(list) {
|
|
2183
|
+
return list.some((item) => {
|
|
2184
|
+
const fieldType = Number(item.fieldType);
|
|
2185
|
+
return fieldType === 0 || fieldType === 2;
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
function findFirstValueByKey(input, key) {
|
|
2189
|
+
const seen = new Set();
|
|
2190
|
+
const visit = (value) => {
|
|
2191
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
2192
|
+
return undefined;
|
|
2193
|
+
}
|
|
2194
|
+
seen.add(value);
|
|
2195
|
+
if (Array.isArray(value)) {
|
|
2196
|
+
for (const item of value) {
|
|
2197
|
+
const found = visit(item);
|
|
2198
|
+
if (found !== undefined && found !== null && found !== "") {
|
|
2199
|
+
return found;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return undefined;
|
|
2203
|
+
}
|
|
2204
|
+
const record = value;
|
|
2205
|
+
if (record[key] !== undefined && record[key] !== null && record[key] !== "") {
|
|
2206
|
+
return record[key];
|
|
2207
|
+
}
|
|
2208
|
+
for (const child of Object.values(record)) {
|
|
2209
|
+
const found = visit(child);
|
|
2210
|
+
if (found !== undefined && found !== null && found !== "") {
|
|
2211
|
+
return found;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
return undefined;
|
|
2215
|
+
};
|
|
2216
|
+
return visit(input);
|
|
2217
|
+
}
|
|
2218
|
+
function findFieldDefinition(item, fields) {
|
|
2219
|
+
const fieldId = item.fieldId !== undefined ? String(item.fieldId) : undefined;
|
|
2220
|
+
const fieldName = normalizeText(item.fieldName);
|
|
2221
|
+
const name = normalizeText(item.name);
|
|
2222
|
+
return fields.find((field) => {
|
|
2223
|
+
if (fieldId && String(field.fieldId ?? field.id) === fieldId) {
|
|
2224
|
+
return true;
|
|
2225
|
+
}
|
|
2226
|
+
if (fieldName && normalizeText(field.fieldName) === fieldName) {
|
|
2227
|
+
return true;
|
|
2228
|
+
}
|
|
2229
|
+
return Boolean(name && normalizeText(field.name) === name);
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
function normalizeOptionValue(value, field) {
|
|
2233
|
+
const options = parseFieldOptions(field.options ?? field.setting ?? field.optionsData);
|
|
2234
|
+
if (options.length === 0 || value === undefined || value === null || value === "") {
|
|
2235
|
+
return value;
|
|
2236
|
+
}
|
|
2237
|
+
const valueText = normalizeText(value);
|
|
2238
|
+
const match = options.find((option) => [option.value, option.name, option.label].some((candidate) => normalizeText(candidate) === valueText));
|
|
2239
|
+
if (!match) {
|
|
2240
|
+
const available = options.map((option) => option.name ?? option.label ?? option.value).filter((option) => option !== undefined);
|
|
2241
|
+
throw new Error(`字段“${field.name ?? field.fieldName}”的可选值不包含“${value}”。可选值:${available.join("、")}`);
|
|
2242
|
+
}
|
|
2243
|
+
return value;
|
|
2244
|
+
}
|
|
2245
|
+
function parseFieldOptions(input) {
|
|
2246
|
+
if (!input) {
|
|
2247
|
+
return [];
|
|
2248
|
+
}
|
|
2249
|
+
if (Array.isArray(input)) {
|
|
2250
|
+
return input.map((option) => (typeof option === "object" && option ? option : { value: option, name: option }));
|
|
2251
|
+
}
|
|
2252
|
+
if (typeof input === "string") {
|
|
2253
|
+
const text = input.trim();
|
|
2254
|
+
if (!text) {
|
|
2255
|
+
return [];
|
|
2256
|
+
}
|
|
2257
|
+
try {
|
|
2258
|
+
return parseFieldOptions(JSON.parse(text));
|
|
2259
|
+
}
|
|
2260
|
+
catch {
|
|
2261
|
+
return text.split(",").map((option) => option.trim()).filter(Boolean).map((option) => ({ value: option, name: option }));
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
if (typeof input === "object") {
|
|
2265
|
+
const object = input;
|
|
2266
|
+
return parseFieldOptions(object.options ?? object.list ?? object.data ?? []);
|
|
2267
|
+
}
|
|
2268
|
+
return [];
|
|
2269
|
+
}
|
|
2270
|
+
async function verifyInformationUpdate(client, moduleDefinition, body) {
|
|
2271
|
+
const endpoint = `${moduleDefinition.basePath}/information/${body.id}`;
|
|
2272
|
+
const data = await client.post(endpoint);
|
|
2273
|
+
const checked = (body.list ?? []).map((item) => {
|
|
2274
|
+
const actualValue = findInformationValue(data, item);
|
|
2275
|
+
return {
|
|
2276
|
+
fieldName: item.fieldName,
|
|
2277
|
+
name: item.name,
|
|
2278
|
+
expectedValue: item.value,
|
|
2279
|
+
actualValue,
|
|
2280
|
+
matched: valuesMatch(item.value, actualValue)
|
|
2281
|
+
};
|
|
2282
|
+
});
|
|
2283
|
+
return {
|
|
2284
|
+
endpoint,
|
|
2285
|
+
ok: checked.every((item) => item.matched),
|
|
2286
|
+
checked
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
function findInformationValue(data, field) {
|
|
2290
|
+
const direct = findDirectInformationValue(data, field);
|
|
2291
|
+
if (direct.found) {
|
|
2292
|
+
return direct.value;
|
|
2293
|
+
}
|
|
2294
|
+
const seen = new Set();
|
|
2295
|
+
const wantedFieldName = normalizeText(field.fieldName);
|
|
2296
|
+
const wantedName = normalizeText(field.name);
|
|
2297
|
+
const visit = (value) => {
|
|
2298
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
2299
|
+
return { found: false };
|
|
2300
|
+
}
|
|
2301
|
+
seen.add(value);
|
|
2302
|
+
if (Array.isArray(value)) {
|
|
2303
|
+
for (const item of value) {
|
|
2304
|
+
const result = visit(item);
|
|
2305
|
+
if (result.found) {
|
|
2306
|
+
return result;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
return { found: false };
|
|
2310
|
+
}
|
|
2311
|
+
const record = value;
|
|
2312
|
+
const recordFieldName = normalizeText(record.fieldName);
|
|
2313
|
+
const recordName = normalizeText(record.name);
|
|
2314
|
+
if ((wantedFieldName && recordFieldName === wantedFieldName) || (wantedName && recordName === wantedName)) {
|
|
2315
|
+
return { found: true, value: extractInformationValue(record) };
|
|
2316
|
+
}
|
|
2317
|
+
for (const child of Object.values(record)) {
|
|
2318
|
+
const result = visit(child);
|
|
2319
|
+
if (result.found) {
|
|
2320
|
+
return result;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
return { found: false };
|
|
2324
|
+
};
|
|
2325
|
+
return visit(data).value;
|
|
2326
|
+
}
|
|
2327
|
+
function findDirectInformationValue(data, field) {
|
|
2328
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
2329
|
+
return { found: false };
|
|
2330
|
+
}
|
|
2331
|
+
const record = data;
|
|
2332
|
+
if (field.fieldName && Object.prototype.hasOwnProperty.call(record, field.fieldName)) {
|
|
2333
|
+
return { found: true, value: record[field.fieldName] };
|
|
2334
|
+
}
|
|
2335
|
+
return { found: false };
|
|
2336
|
+
}
|
|
2337
|
+
function extractInformationValue(record) {
|
|
2338
|
+
if (Object.prototype.hasOwnProperty.call(record, "value")) {
|
|
2339
|
+
return record.value;
|
|
2340
|
+
}
|
|
2341
|
+
if (Object.prototype.hasOwnProperty.call(record, "valueDesc")) {
|
|
2342
|
+
return record.valueDesc;
|
|
2343
|
+
}
|
|
2344
|
+
if (Object.prototype.hasOwnProperty.call(record, "fieldValue")) {
|
|
2345
|
+
return record.fieldValue;
|
|
2346
|
+
}
|
|
2347
|
+
return undefined;
|
|
2348
|
+
}
|
|
2349
|
+
function valuesMatch(expected, actual) {
|
|
2350
|
+
return normalizeComparableValue(expected) === normalizeComparableValue(actual);
|
|
2351
|
+
}
|
|
2352
|
+
function normalizeComparableValue(value) {
|
|
2353
|
+
if (value === undefined || value === null) {
|
|
2354
|
+
return "";
|
|
2355
|
+
}
|
|
2356
|
+
if (typeof value === "object") {
|
|
2357
|
+
return JSON.stringify(value);
|
|
2358
|
+
}
|
|
2359
|
+
return String(value).trim();
|
|
2360
|
+
}
|
|
2361
|
+
function normalizeText(value) {
|
|
2362
|
+
return value === undefined || value === null ? "" : String(value).trim().toLowerCase();
|
|
2363
|
+
}
|
|
2364
|
+
async function listCustomerPools(client, args) {
|
|
2365
|
+
const pools = await client.post("/crmCustomerPool/queryPoolNameListByAuth");
|
|
2366
|
+
const poolList = Array.isArray(pools) ? pools : [];
|
|
2367
|
+
const includeSamples = Boolean(args.includeSamples);
|
|
2368
|
+
const limit = includeSamples ? positiveInteger(args.sampleLimit ?? args.limit, 3) : 1;
|
|
2369
|
+
const searchList = args.filters ?? args.searchList ?? [];
|
|
2370
|
+
const poolSummaries = await Promise.all(poolList.map(async (pool) => {
|
|
2371
|
+
const poolId = pool.poolId ?? pool.id;
|
|
2372
|
+
if (!poolId) {
|
|
2373
|
+
throw new Error("客户公海授权列表缺少 poolId,无法统计池内客户数量。");
|
|
2374
|
+
}
|
|
2375
|
+
const page = await client.post("/crmCustomerPool/queryPageList", cleanObject({
|
|
2376
|
+
page: 1,
|
|
2377
|
+
limit,
|
|
2378
|
+
pageType: 1,
|
|
2379
|
+
label: 9,
|
|
2380
|
+
poolId,
|
|
2381
|
+
searchList
|
|
2382
|
+
}));
|
|
2383
|
+
const summary = {
|
|
2384
|
+
poolId,
|
|
2385
|
+
poolName: pool.poolName ?? pool.name,
|
|
2386
|
+
customerCount: page.totalRow ?? page.total ?? 0
|
|
2387
|
+
};
|
|
2388
|
+
if (includeSamples) {
|
|
2389
|
+
const list = Array.isArray(page.list) ? page.list : [];
|
|
2390
|
+
summary.samples = list.map((record) => ({
|
|
2391
|
+
customerId: record.customerId ?? record.id,
|
|
2392
|
+
customerName: record.customerName ?? record.name,
|
|
2393
|
+
ownerUserName: record.ownerUserName,
|
|
2394
|
+
createTime: record.createTime
|
|
2395
|
+
}));
|
|
2396
|
+
}
|
|
2397
|
+
return summary;
|
|
2398
|
+
}));
|
|
2399
|
+
return {
|
|
2400
|
+
module: "customerPool",
|
|
2401
|
+
readonly: true,
|
|
2402
|
+
poolCount: poolSummaries.length,
|
|
2403
|
+
pools: poolSummaries
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
function positiveInteger(value, fallback) {
|
|
2407
|
+
const numberValue = Number(value);
|
|
2408
|
+
if (!Number.isInteger(numberValue) || numberValue < 1) {
|
|
2409
|
+
return fallback;
|
|
2410
|
+
}
|
|
2411
|
+
return numberValue;
|
|
2412
|
+
}
|
|
2413
|
+
function isPoolModule(moduleDefinition) {
|
|
2414
|
+
return moduleDefinition.key === "customerPool" || moduleDefinition.key === "leadsPool";
|
|
2415
|
+
}
|
|
2416
|
+
function getPoolRecordModule(moduleDefinition) {
|
|
2417
|
+
return getModuleByKey(moduleDefinition.key === "customerPool" ? "customer" : "leads");
|
|
2418
|
+
}
|
|
2419
|
+
async function resolvePoolContext(client, moduleDefinition, args) {
|
|
2420
|
+
if (!isPoolModule(moduleDefinition)) {
|
|
2421
|
+
throw new Error(`Module ${moduleDefinition.key} is not a pool module.`);
|
|
2422
|
+
}
|
|
2423
|
+
if (args.poolId) {
|
|
2424
|
+
return { poolId: args.poolId, pool: cleanObject({ poolId: args.poolId, poolName: args.poolName }) };
|
|
2425
|
+
}
|
|
2426
|
+
const pools = await client.post(`${moduleDefinition.basePath}/queryPoolNameListByAuth`);
|
|
2427
|
+
const poolList = Array.isArray(pools) ? pools : [];
|
|
2428
|
+
const selectedPool = args.poolName
|
|
2429
|
+
? poolList.find((pool) => String(pool.poolName ?? pool.name ?? "").trim() === String(args.poolName).trim())
|
|
2430
|
+
: poolList[0];
|
|
2431
|
+
if (!selectedPool) {
|
|
2432
|
+
throw new Error(`当前账号没有可查询的${moduleDefinition.name},请确认公海/线索池权限或传入 poolId。`);
|
|
2433
|
+
}
|
|
2434
|
+
const poolId = selectedPool.poolId ?? selectedPool.id;
|
|
2435
|
+
if (!poolId) {
|
|
2436
|
+
throw new Error(`${moduleDefinition.name}授权列表缺少 poolId,无法查询池内数据。`);
|
|
2437
|
+
}
|
|
2438
|
+
return { poolId, pool: selectedPool };
|
|
2439
|
+
}
|
|
2440
|
+
async function callRelatedRecords(client, moduleDefinition, args) {
|
|
2441
|
+
const relation = String(args.relation ?? args.type ?? "").trim();
|
|
2442
|
+
const pageBody = cleanObject({ page: args.page ?? 1, limit: args.limit ?? 15, pageType: 1, id: args.id, ...safeExtra(args.extra) });
|
|
2443
|
+
if (moduleDefinition.key === "business" && relation === "product") {
|
|
2444
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmBusiness/queryProduct", pageBody) };
|
|
2445
|
+
}
|
|
2446
|
+
if (moduleDefinition.key === "business" && relation === "contacts") {
|
|
2447
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmBusiness/queryContacts", pageBody) };
|
|
2448
|
+
}
|
|
2449
|
+
if (moduleDefinition.key === "contacts" && relation === "business") {
|
|
2450
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmContacts/queryBusiness", pageBody) };
|
|
2451
|
+
}
|
|
2452
|
+
if (moduleDefinition.key === "contract" && relation === "product") {
|
|
2453
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmContract/queryProductListByContractId", pageBody) };
|
|
2454
|
+
}
|
|
2455
|
+
if (moduleDefinition.key === "contract" && relation === "receivablesPlan") {
|
|
2456
|
+
return {
|
|
2457
|
+
module: moduleDefinition.key,
|
|
2458
|
+
relation,
|
|
2459
|
+
data: await client.post("/crmContract/queryReceivablesPlansByContractId", undefined, {
|
|
2460
|
+
contractId: args.id,
|
|
2461
|
+
receivablesId: args.receivablesId,
|
|
2462
|
+
productId: args.productId
|
|
2463
|
+
})
|
|
2464
|
+
};
|
|
339
2465
|
}
|
|
340
|
-
|
|
2466
|
+
if (moduleDefinition.key === "receivables" && relation === "product") {
|
|
2467
|
+
return { module: moduleDefinition.key, relation, data: await client.post(`/crmReceivables/queryProductList/${args.id}`) };
|
|
2468
|
+
}
|
|
2469
|
+
if (moduleDefinition.key === "quotation" && relation === "product") {
|
|
2470
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmQuotation/queryProductList", pageBody) };
|
|
2471
|
+
}
|
|
2472
|
+
if (moduleDefinition.key === "product" && relation === "saleProduct") {
|
|
2473
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmProduct/querySaleProductPageList", pageBody) };
|
|
2474
|
+
}
|
|
2475
|
+
if (moduleDefinition.key === "receivablesPlan" && relation === "contractCustomer") {
|
|
2476
|
+
return { module: moduleDefinition.key, relation, data: await client.post("/crmReceivablesPlan/queryByContractAndCustomer", pageBody) };
|
|
2477
|
+
}
|
|
2478
|
+
throw new Error(`当前 MCP 未配置“${moduleDefinition.name} / ${relation}”关联查询白名单。`);
|
|
341
2479
|
}
|
|
342
|
-
function
|
|
343
|
-
return
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
2480
|
+
function buildHrmEmployeeListBody(args) {
|
|
2481
|
+
return cleanObject({
|
|
2482
|
+
page: args.page ?? 1,
|
|
2483
|
+
limit: args.limit ?? 15,
|
|
2484
|
+
pageType: args.pageType ?? 1,
|
|
2485
|
+
employeeName: firstTextValue(args.employeeName, args.name, args.keyword, args.query, args.q, args.search),
|
|
2486
|
+
mobile: args.mobile ?? args.phone,
|
|
2487
|
+
sex: args.sex,
|
|
2488
|
+
entryTime: args.entryTime,
|
|
2489
|
+
jobNumber: args.jobNumber,
|
|
2490
|
+
deptId: args.deptId,
|
|
2491
|
+
post: args.post,
|
|
2492
|
+
becomeTime: args.becomeTime,
|
|
2493
|
+
workAddress: args.workAddress,
|
|
2494
|
+
employmentForms: args.employmentForms,
|
|
2495
|
+
status: args.status,
|
|
2496
|
+
employeeSurvey: args.employeeSurvey,
|
|
2497
|
+
toDoRemind: args.toDoRemind,
|
|
2498
|
+
employeeIds: args.employeeIds,
|
|
2499
|
+
sortField: args.sortField,
|
|
2500
|
+
order: args.order
|
|
2501
|
+
});
|
|
351
2502
|
}
|
|
352
|
-
function
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
2503
|
+
function buildHrmDepartmentListBody(args) {
|
|
2504
|
+
return cleanObject({
|
|
2505
|
+
name: firstTextValue(args.name, args.deptName, args.keyword, args.query, args.q, args.search),
|
|
2506
|
+
type: args.type ?? "tree",
|
|
2507
|
+
id: args.id ?? args.parentId
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
function buildHrmDepartmentEmployeesBody(args) {
|
|
2511
|
+
return cleanObject({
|
|
2512
|
+
page: args.page ?? 1,
|
|
2513
|
+
limit: args.limit ?? 15,
|
|
2514
|
+
pageType: args.pageType ?? 1,
|
|
2515
|
+
deptId: requireHrmDeptId(args),
|
|
2516
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.name, args.employeeName)
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
function buildHrmAttendanceMonthRecordBody(args) {
|
|
2520
|
+
return cleanObject({
|
|
2521
|
+
page: args.page ?? 1,
|
|
2522
|
+
limit: args.limit ?? 15,
|
|
2523
|
+
pageType: args.pageType ?? 1,
|
|
2524
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.employeeName, args.name),
|
|
2525
|
+
deptIds: args.deptIds ?? args.deptId,
|
|
2526
|
+
times: args.times ?? args.timeRange,
|
|
2527
|
+
sortField: args.sortField,
|
|
2528
|
+
order: args.order,
|
|
2529
|
+
isFullAttendance: args.isFullAttendance,
|
|
2530
|
+
employeeId: args.employeeId ?? args.id,
|
|
2531
|
+
isResignation: args.isResignation
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
function buildHrmAttendanceDailyDetailBody(args) {
|
|
2535
|
+
return cleanObject({
|
|
2536
|
+
employeeId: requireHrmEmployeeId(args),
|
|
2537
|
+
currentDate: args.currentDate ?? args.date ?? args.day,
|
|
2538
|
+
hrmAttendanceGroup: args.hrmAttendanceGroup,
|
|
2539
|
+
multi: args.multi,
|
|
2540
|
+
companyId: args.companyId
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
function buildHrmLeaveRecordBody(args) {
|
|
2544
|
+
return cleanObject({
|
|
2545
|
+
page: args.page ?? 1,
|
|
2546
|
+
limit: args.limit ?? 15,
|
|
2547
|
+
pageType: args.pageType ?? 1,
|
|
2548
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.employeeName, args.name),
|
|
2549
|
+
employeeId: args.employeeId ?? args.id,
|
|
2550
|
+
times: args.times ?? args.timeRange,
|
|
2551
|
+
deptIds: args.deptIds ?? args.deptId,
|
|
2552
|
+
leaveTypes: args.leaveTypes ?? args.leaveType,
|
|
2553
|
+
sortField: args.sortField,
|
|
2554
|
+
order: args.order
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
function requireHrmEmployeeId(args) {
|
|
2558
|
+
const employeeId = args.employeeId ?? args.id;
|
|
2559
|
+
if (isBlank(employeeId)) {
|
|
2560
|
+
throw new Error("请提供 employeeId 或 id。");
|
|
2561
|
+
}
|
|
2562
|
+
return employeeId;
|
|
2563
|
+
}
|
|
2564
|
+
function requireHrmDeptId(args) {
|
|
2565
|
+
const deptId = args.deptId ?? args.id;
|
|
2566
|
+
if (isBlank(deptId)) {
|
|
2567
|
+
throw new Error("请提供 deptId 或 id。");
|
|
2568
|
+
}
|
|
2569
|
+
return deptId;
|
|
2570
|
+
}
|
|
2571
|
+
function getFinanceEndpointByKey(input, definitions, label) {
|
|
2572
|
+
const value = String(input ?? "").trim().toLowerCase();
|
|
2573
|
+
const definition = definitions.find((item) => item.aliases.some((alias) => alias.toLowerCase() === value));
|
|
2574
|
+
if (!definition) {
|
|
2575
|
+
throw new Error(`当前 MCP 仅支持这些${label}只读查询:${definitions.map((item) => item.key).join("、")}。`);
|
|
2576
|
+
}
|
|
2577
|
+
return definition;
|
|
2578
|
+
}
|
|
2579
|
+
function buildFinanceSearchBody(args, defaults = {}, omitKeys = []) {
|
|
2580
|
+
const body = buildFinanceBody(args, ["keyword", "query", "q", ...omitKeys], defaults);
|
|
2581
|
+
const search = firstTextValue(args.search, args.keyword, args.query, args.q);
|
|
2582
|
+
if (search) {
|
|
2583
|
+
body.search = search;
|
|
2584
|
+
}
|
|
2585
|
+
return cleanObject(body);
|
|
2586
|
+
}
|
|
2587
|
+
function buildFinanceBody(args, omitKeys = [], defaults = {}) {
|
|
2588
|
+
const omit = new Set(omitKeys);
|
|
2589
|
+
const body = { ...defaults };
|
|
2590
|
+
for (const [key, value] of Object.entries(args ?? {})) {
|
|
2591
|
+
if (!omit.has(key) && value !== undefined && value !== null) {
|
|
2592
|
+
body[key] = value;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return cleanObject(body);
|
|
2596
|
+
}
|
|
2597
|
+
function requireFinanceCertificateId(args) {
|
|
2598
|
+
const certificateId = args.certificateId ?? args.id;
|
|
2599
|
+
if (isBlank(certificateId)) {
|
|
2600
|
+
throw new Error("查询财务凭证详情请提供 certificateId 或 id。");
|
|
2601
|
+
}
|
|
2602
|
+
return certificateId;
|
|
2603
|
+
}
|
|
2604
|
+
function getJxcModuleByKey(input) {
|
|
2605
|
+
const value = String(input ?? "").trim();
|
|
2606
|
+
const moduleDefinition = JXC_MODULES.find((module) => module.aliases.some((alias) => String(alias) === value));
|
|
2607
|
+
if (!moduleDefinition) {
|
|
2608
|
+
throw new Error(`当前 MCP 仅内置这些 JXC 主数据模块:${JXC_MODULES.map((module) => module.key).join("、")}。其他 JXC 接口请使用 crm_write_jxc 指定固定路径。`);
|
|
2609
|
+
}
|
|
2610
|
+
return moduleDefinition;
|
|
2611
|
+
}
|
|
2612
|
+
function buildJxcSearchBody(args, moduleDefinition) {
|
|
2613
|
+
return cleanObject({
|
|
2614
|
+
page: args.page ?? 1,
|
|
2615
|
+
limit: args.limit ?? 15,
|
|
2616
|
+
pageType: args.pageType ?? 1,
|
|
2617
|
+
label: moduleDefinition.label,
|
|
2618
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.name),
|
|
2619
|
+
sceneId: args.sceneId,
|
|
2620
|
+
sortField: args.sortField,
|
|
2621
|
+
order: args.order,
|
|
2622
|
+
searchList: args.searchList ?? args.filters,
|
|
2623
|
+
ids: args.ids,
|
|
2624
|
+
sortIds: args.sortIds,
|
|
2625
|
+
mainFieldValues: args.mainFieldValues,
|
|
2626
|
+
extraMap: args.extraMap,
|
|
2627
|
+
validFieldAuth: args.validFieldAuth,
|
|
2628
|
+
warehouseId: moduleDefinition.key === "product" ? args.warehouseId : undefined,
|
|
2629
|
+
isRelateProductSearch: moduleDefinition.key === "product" ? args.isRelateProductSearch : undefined
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
function buildJxcStockProductsBody(args) {
|
|
2633
|
+
return cleanObject({
|
|
2634
|
+
page: args.page ?? 1,
|
|
2635
|
+
limit: args.limit ?? 15,
|
|
2636
|
+
pageType: args.pageType ?? 1,
|
|
2637
|
+
label: 34,
|
|
2638
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.productName, args.name),
|
|
2639
|
+
searchList: args.searchList ?? args.filters,
|
|
2640
|
+
productName: args.productName,
|
|
2641
|
+
productTypeId: args.productTypeId,
|
|
2642
|
+
productCode: args.productCode,
|
|
2643
|
+
warehouseId: args.warehouseId,
|
|
2644
|
+
isWarehouseGroup: args.isWarehouseGroup,
|
|
2645
|
+
stock: args.stock,
|
|
2646
|
+
sortField: args.sortField,
|
|
2647
|
+
order: args.order
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
function buildJxcStockMovementsBody(args) {
|
|
2651
|
+
return cleanObject({
|
|
2652
|
+
page: args.page ?? 1,
|
|
2653
|
+
limit: args.limit ?? 15,
|
|
2654
|
+
pageType: args.pageType ?? 1,
|
|
2655
|
+
label: 35,
|
|
2656
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.productName, args.name),
|
|
2657
|
+
sceneId: args.sceneId,
|
|
2658
|
+
sortField: args.sortField,
|
|
2659
|
+
order: args.order,
|
|
2660
|
+
searchList: args.searchList ?? args.filters,
|
|
2661
|
+
ids: args.ids,
|
|
2662
|
+
sortIds: args.sortIds,
|
|
2663
|
+
mainFieldValues: args.mainFieldValues,
|
|
2664
|
+
extraMap: args.extraMap
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
function requireJxcId(args, message) {
|
|
2668
|
+
const id = args.id ?? args.recordId ?? args[args.module ? `${String(args.module)}Id` : "id"];
|
|
2669
|
+
if (isBlank(id)) {
|
|
2670
|
+
throw new Error(message);
|
|
2671
|
+
}
|
|
2672
|
+
return id;
|
|
2673
|
+
}
|
|
2674
|
+
function requireJxcProductId(args) {
|
|
2675
|
+
const productId = args.productId ?? args.id;
|
|
2676
|
+
if (isBlank(productId)) {
|
|
2677
|
+
throw new Error("查询 JXC 产品库存请提供 productId 或 id。");
|
|
2678
|
+
}
|
|
2679
|
+
return productId;
|
|
2680
|
+
}
|
|
2681
|
+
function requireWarehouseProductIdsMap(args) {
|
|
2682
|
+
const value = args.warehouseProductIdsMap ?? args.map ?? args.data;
|
|
2683
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2684
|
+
throw new Error("查询仓库产品库存数量请提供 warehouseProductIdsMap,例如 { \"8\": [100] }。");
|
|
2685
|
+
}
|
|
2686
|
+
return value;
|
|
2687
|
+
}
|
|
2688
|
+
function requireWarehouseIds(args) {
|
|
2689
|
+
const value = args.warehouseIds ?? args.ids;
|
|
2690
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
2691
|
+
throw new Error("查询仓库名称请提供 warehouseIds 数组。");
|
|
2692
|
+
}
|
|
2693
|
+
return value;
|
|
2694
|
+
}
|
|
2695
|
+
async function executeDomainWrite(client, domain, args) {
|
|
2696
|
+
const targetPath = normalizeDomainTargetPath(domain, args);
|
|
2697
|
+
const body = explicitWriteBody(args);
|
|
2698
|
+
const query = cleanQuery(args.query ?? {});
|
|
2699
|
+
if (!args.confirm) {
|
|
2700
|
+
return previewDomainWrite(domain, targetPath, body, query);
|
|
2701
|
+
}
|
|
2702
|
+
return cleanObject({
|
|
2703
|
+
domain: domain.key,
|
|
2704
|
+
moduleName: domain.name,
|
|
2705
|
+
targetPath,
|
|
2706
|
+
body,
|
|
2707
|
+
query: Object.keys(query).length > 0 ? query : undefined,
|
|
2708
|
+
data: await client.post(targetPath, body, query)
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
function previewDomainWrite(domain, targetPath, body, query) {
|
|
2712
|
+
return cleanObject({
|
|
2713
|
+
preview: true,
|
|
2714
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
2715
|
+
domain: domain.key,
|
|
2716
|
+
moduleName: domain.name,
|
|
2717
|
+
targetPath,
|
|
2718
|
+
body,
|
|
2719
|
+
query: query && Object.keys(query).length > 0 ? query : undefined
|
|
2720
|
+
});
|
|
2721
|
+
}
|
|
2722
|
+
function normalizeDomainTargetPath(domain, args) {
|
|
2723
|
+
const explicitPath = args.targetPath ?? args.path ?? args.endpoint;
|
|
2724
|
+
const targetPath = explicitPath
|
|
2725
|
+
? normalizeEndpointPath(explicitPath)
|
|
2726
|
+
: buildDomainTargetPath(args.module ?? args.controller ?? args.basePath, args.operation ?? args.action);
|
|
2727
|
+
if (!domain.allowedPrefixes.some((prefix) => targetPath.startsWith(prefix))) {
|
|
2728
|
+
throw new Error(`${domain.name} 写入工具只能调用 ${domain.allowedPrefixes.join("、")} 开头的 72CRM 固定接口。`);
|
|
2729
|
+
}
|
|
2730
|
+
return targetPath;
|
|
2731
|
+
}
|
|
2732
|
+
function buildDomainTargetPath(moduleName, operation) {
|
|
2733
|
+
const moduleSegment = normalizePathSegment(moduleName, "module");
|
|
2734
|
+
const operationSegment = normalizePathSegment(operation, "operation");
|
|
2735
|
+
return normalizeEndpointPath(`/${moduleSegment}/${operationSegment}`);
|
|
2736
|
+
}
|
|
2737
|
+
function normalizeEndpointPath(input) {
|
|
2738
|
+
const raw = String(input ?? "").trim();
|
|
2739
|
+
if (!raw) {
|
|
2740
|
+
throw new Error("请提供 targetPath,或提供 module + operation 组合。");
|
|
2741
|
+
}
|
|
2742
|
+
if (raw.includes("://") || raw.startsWith("//") || raw.includes("..") || raw.includes("?") || raw.includes("#")) {
|
|
2743
|
+
throw new Error("targetPath 只能是 72CRM 固定接口路径,不允许 URL、上级目录或查询串。");
|
|
2744
|
+
}
|
|
2745
|
+
return raw.startsWith("/") ? raw : `/${raw}`;
|
|
2746
|
+
}
|
|
2747
|
+
function normalizePathSegment(input, name) {
|
|
2748
|
+
const value = String(input ?? "").trim().replace(/^\/+|\/+$/g, "");
|
|
2749
|
+
if (!value || value.includes("://") || value.includes("..") || value.includes("?") || value.includes("#")) {
|
|
2750
|
+
throw new Error(`请提供合法的 ${name}。`);
|
|
2751
|
+
}
|
|
2752
|
+
return value;
|
|
2753
|
+
}
|
|
2754
|
+
const DOMAIN_WRITE_CONTROL_KEYS = new Set([
|
|
2755
|
+
"targetPath",
|
|
2756
|
+
"path",
|
|
2757
|
+
"endpoint",
|
|
2758
|
+
"module",
|
|
2759
|
+
"controller",
|
|
2760
|
+
"basePath",
|
|
2761
|
+
"operation",
|
|
2762
|
+
"action",
|
|
2763
|
+
"body",
|
|
2764
|
+
"data",
|
|
2765
|
+
"query",
|
|
2766
|
+
"confirm"
|
|
2767
|
+
]);
|
|
2768
|
+
function explicitWriteBody(args) {
|
|
2769
|
+
if (Object.prototype.hasOwnProperty.call(args, "body")) {
|
|
2770
|
+
return args.body;
|
|
2771
|
+
}
|
|
2772
|
+
if (Object.prototype.hasOwnProperty.call(args, "data")) {
|
|
2773
|
+
return args.data;
|
|
2774
|
+
}
|
|
2775
|
+
const body = {};
|
|
2776
|
+
for (const [key, value] of Object.entries(args ?? {})) {
|
|
2777
|
+
if (!DOMAIN_WRITE_CONTROL_KEYS.has(key) && value !== undefined && value !== null) {
|
|
2778
|
+
body[key] = value;
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
return Object.keys(body).length > 0 ? body : undefined;
|
|
2782
|
+
}
|
|
2783
|
+
function buildJxcRecordSaveBody(args, moduleDefinition, requireId) {
|
|
2784
|
+
const explicitPayload = Object.prototype.hasOwnProperty.call(args, "body")
|
|
2785
|
+
? args.body
|
|
2786
|
+
: Object.prototype.hasOwnProperty.call(args, "data")
|
|
2787
|
+
? args.data
|
|
2788
|
+
: undefined;
|
|
2789
|
+
const body = explicitPayload && typeof explicitPayload === "object" && !Array.isArray(explicitPayload)
|
|
2790
|
+
? { ...explicitPayload }
|
|
2791
|
+
: buildModelSaveBody(args);
|
|
2792
|
+
if (!body.entity || typeof body.entity !== "object" || Array.isArray(body.entity)) {
|
|
2793
|
+
body.entity = {};
|
|
2794
|
+
}
|
|
2795
|
+
else {
|
|
2796
|
+
body.entity = { ...body.entity };
|
|
2797
|
+
}
|
|
2798
|
+
if (requireId) {
|
|
2799
|
+
const id = args.id ?? args.recordId ?? args[moduleDefinition.primaryKey] ?? body.entity[moduleDefinition.primaryKey];
|
|
2800
|
+
if (isBlank(id)) {
|
|
2801
|
+
throw new Error(`修改 JXC ${moduleDefinition.name}请提供 id 或 ${moduleDefinition.primaryKey}。`);
|
|
2802
|
+
}
|
|
2803
|
+
if (isBlank(body.entity[moduleDefinition.primaryKey])) {
|
|
2804
|
+
body.entity[moduleDefinition.primaryKey] = id;
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
return cleanObject(body);
|
|
2808
|
+
}
|
|
2809
|
+
function buildJxcUpdateInformationBody(args, moduleDefinition) {
|
|
2810
|
+
const id = args.id ?? args.recordId ?? args[moduleDefinition.primaryKey];
|
|
2811
|
+
if (isBlank(id)) {
|
|
2812
|
+
throw new Error(`修改 JXC ${moduleDefinition.name}字段请提供 id 或 ${moduleDefinition.primaryKey}。`);
|
|
2813
|
+
}
|
|
2814
|
+
return cleanObject({
|
|
2815
|
+
id,
|
|
2816
|
+
label: moduleDefinition.label,
|
|
2817
|
+
list: rawUpdateList(args),
|
|
2818
|
+
batchId: args.batchId
|
|
2819
|
+
});
|
|
2820
|
+
}
|
|
2821
|
+
function buildFinanceVoucherWriteBody(args, requireId) {
|
|
2822
|
+
const explicitBody = explicitWriteBody(args);
|
|
2823
|
+
const body = explicitBody && typeof explicitBody === "object" && !Array.isArray(explicitBody)
|
|
2824
|
+
? { ...explicitBody }
|
|
2825
|
+
: {};
|
|
2826
|
+
const certificateId = args.certificateId ?? args.id ?? body.certificateId;
|
|
2827
|
+
if (requireId && isBlank(certificateId)) {
|
|
2828
|
+
throw new Error("修改财务凭证请提供 certificateId 或 id。");
|
|
2829
|
+
}
|
|
2830
|
+
if (requireId && isBlank(body.certificateId)) {
|
|
2831
|
+
body.certificateId = certificateId;
|
|
2832
|
+
}
|
|
2833
|
+
return cleanObject(body);
|
|
2834
|
+
}
|
|
2835
|
+
async function buildCrmProductSearchBody(client, args, saleOnly) {
|
|
2836
|
+
const moduleDefinition = getModuleByKey("product");
|
|
2837
|
+
let searchList = await buildResolvedSearchList(client, args, moduleDefinition);
|
|
2838
|
+
const categoryId = args.categoryId ?? args.productCategoryId;
|
|
2839
|
+
if (!isBlank(categoryId) && !searchList.some((item) => item.fieldName === "categoryId")) {
|
|
2840
|
+
searchList = combineSearchLists(searchList, [{
|
|
2841
|
+
type: 1,
|
|
2842
|
+
values: [categoryId],
|
|
2843
|
+
formType: "category",
|
|
2844
|
+
fieldName: "categoryId"
|
|
2845
|
+
}]);
|
|
2846
|
+
}
|
|
2847
|
+
return cleanObject({
|
|
2848
|
+
page: args.page ?? 1,
|
|
2849
|
+
limit: args.limit ?? 15,
|
|
2850
|
+
pageType: saleOnly ? 0 : args.pageType ?? 1,
|
|
2851
|
+
label: moduleDefinition.label,
|
|
2852
|
+
status: saleOnly ? undefined : args.status,
|
|
2853
|
+
sortIds: args.sortIds,
|
|
2854
|
+
searchList: searchList.length > 0 ? searchList : undefined
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2857
|
+
async function executeCrmProductSave(client, args, requireId) {
|
|
2858
|
+
const targetPath = requireId ? "/crmProduct/update" : "/crmProduct/add";
|
|
2859
|
+
const body = buildCrmProductSaveBody(args, requireId);
|
|
2860
|
+
if (!args.confirm) {
|
|
2861
|
+
return {
|
|
2862
|
+
preview: true,
|
|
2863
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
2864
|
+
module: "product",
|
|
2865
|
+
moduleName: "产品",
|
|
2866
|
+
operation: requireId ? "update" : "add",
|
|
2867
|
+
targetPath,
|
|
2868
|
+
body
|
|
2869
|
+
};
|
|
2870
|
+
}
|
|
2871
|
+
return {
|
|
2872
|
+
module: "product",
|
|
2873
|
+
moduleName: "产品",
|
|
2874
|
+
targetPath,
|
|
2875
|
+
body,
|
|
2876
|
+
data: await client.post(targetPath, body)
|
|
2877
|
+
};
|
|
2878
|
+
}
|
|
2879
|
+
function buildCrmProductSaveBody(args, requireId) {
|
|
2880
|
+
const explicitPayload = hasExplicitBody(args) ? objectWriteBody(args) : buildModelSaveBody(args);
|
|
2881
|
+
const body = explicitPayload && typeof explicitPayload === "object" && !Array.isArray(explicitPayload)
|
|
2882
|
+
? { ...explicitPayload }
|
|
2883
|
+
: {};
|
|
2884
|
+
if (!isPlainObject(body.entity)) {
|
|
2885
|
+
body.entity = {};
|
|
2886
|
+
}
|
|
2887
|
+
else {
|
|
2888
|
+
body.entity = { ...body.entity };
|
|
2889
|
+
}
|
|
2890
|
+
const productId = args.productId ?? args.id ?? body.entity.productId;
|
|
2891
|
+
if (requireId && isBlank(productId)) {
|
|
2892
|
+
throw new Error("编辑 CRM 产品请提供 productId 或 id。");
|
|
2893
|
+
}
|
|
2894
|
+
if (!isBlank(productId) && isBlank(body.entity.productId)) {
|
|
2895
|
+
body.entity.productId = productId;
|
|
2896
|
+
}
|
|
2897
|
+
if (!Array.isArray(body.field)) {
|
|
2898
|
+
body.field = [];
|
|
2899
|
+
}
|
|
2900
|
+
return cleanObject(body);
|
|
2901
|
+
}
|
|
2902
|
+
async function executeCrmProductSimpleWrite(client, args, operation, targetPath, body) {
|
|
2903
|
+
if (!args.confirm) {
|
|
2904
|
+
return {
|
|
2905
|
+
preview: true,
|
|
2906
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
2907
|
+
module: "product",
|
|
2908
|
+
moduleName: "产品",
|
|
2909
|
+
operation,
|
|
2910
|
+
targetPath,
|
|
2911
|
+
body
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
return {
|
|
2915
|
+
module: "product",
|
|
2916
|
+
moduleName: "产品",
|
|
2917
|
+
targetPath,
|
|
2918
|
+
body,
|
|
2919
|
+
data: await client.post(targetPath, body)
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
function buildCrmProductStatusBody(args) {
|
|
2923
|
+
if (hasExplicitBody(args)) {
|
|
2924
|
+
return objectWriteBody(args);
|
|
2925
|
+
}
|
|
2926
|
+
if (isBlank(args.status)) {
|
|
2927
|
+
throw new Error("修改 CRM 产品上下架状态请提供 status。");
|
|
2928
|
+
}
|
|
2929
|
+
return {
|
|
2930
|
+
ids: requireIdArray(args.ids ?? args.productIds, "修改 CRM 产品状态请提供 ids 或 productIds 数组。"),
|
|
2931
|
+
status: args.status
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
function buildCrmProductTransferBody(args) {
|
|
2935
|
+
if (hasExplicitBody(args)) {
|
|
2936
|
+
return objectWriteBody(args);
|
|
2937
|
+
}
|
|
2938
|
+
const ownerUserId = args.ownerUserId ?? args.userId;
|
|
2939
|
+
if (isBlank(ownerUserId)) {
|
|
2940
|
+
throw new Error("转移 CRM 产品负责人请提供 ownerUserId。");
|
|
2941
|
+
}
|
|
2942
|
+
return cleanObject({
|
|
2943
|
+
ids: requireIdArray(args.ids ?? args.productIds, "转移 CRM 产品负责人请提供 ids 或 productIds 数组。"),
|
|
2944
|
+
ownerUserId,
|
|
2945
|
+
transferType: args.transferType,
|
|
2946
|
+
power: args.power,
|
|
2947
|
+
changeType: args.changeType,
|
|
2948
|
+
expiresTime: args.expiresTime
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
function requireCrmProductId(args, message) {
|
|
2952
|
+
const productId = args.productId ?? args.id;
|
|
2953
|
+
if (isBlank(productId)) {
|
|
2954
|
+
throw new Error(message);
|
|
2955
|
+
}
|
|
2956
|
+
return productId;
|
|
2957
|
+
}
|
|
2958
|
+
function requireIdArray(value, message) {
|
|
2959
|
+
const ids = Array.isArray(value) ? value : isBlank(value) ? [] : [value];
|
|
2960
|
+
if (ids.length === 0) {
|
|
2961
|
+
throw new Error(message);
|
|
2962
|
+
}
|
|
2963
|
+
return ids;
|
|
2964
|
+
}
|
|
2965
|
+
function buildOaAnnouncementListQuery(args) {
|
|
2966
|
+
return buildPassthroughBody(args, ["keyword", "query", "q", "search"], {
|
|
2967
|
+
page: args.page ?? 1,
|
|
2968
|
+
limit: args.limit ?? 15,
|
|
2969
|
+
type: args.type
|
|
2970
|
+
});
|
|
2971
|
+
}
|
|
2972
|
+
async function executeOaAnnouncementWrite(client, args, requireId) {
|
|
2973
|
+
const targetPath = requireId ? "/oaAnnouncement/setAnnouncement" : "/oaAnnouncement/addAnnouncement";
|
|
2974
|
+
const body = buildOaAnnouncementBody(args, requireId);
|
|
2975
|
+
if (!args.confirm) {
|
|
2976
|
+
return previewOaWrite(requireId ? "update" : "create", "oaAnnouncement", "公告", targetPath, body);
|
|
2977
|
+
}
|
|
2978
|
+
return {
|
|
2979
|
+
module: "oaAnnouncement",
|
|
2980
|
+
moduleName: "公告",
|
|
2981
|
+
targetPath,
|
|
2982
|
+
body,
|
|
2983
|
+
data: await client.post(targetPath, body)
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
function buildOaAnnouncementBody(args, requireId) {
|
|
2987
|
+
const body = hasExplicitBody(args) ? objectWriteBody(args) : buildPassthroughBody(args, ["id"]);
|
|
2988
|
+
const announcementId = args.announcementId ?? args.id ?? body.announcementId;
|
|
2989
|
+
if (requireId && isBlank(announcementId)) {
|
|
2990
|
+
throw new Error("编辑 OA 公告请提供 announcementId 或 id。");
|
|
2991
|
+
}
|
|
2992
|
+
if (!isBlank(announcementId) && isBlank(body.announcementId)) {
|
|
2993
|
+
body.announcementId = announcementId;
|
|
2994
|
+
}
|
|
2995
|
+
for (const key of ["deptIds", "ownerUserIds", "readUserIds"]) {
|
|
2996
|
+
if (body[key] !== undefined) {
|
|
2997
|
+
body[key] = toCommaSeparated(body[key]);
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
return cleanObject(body);
|
|
3001
|
+
}
|
|
3002
|
+
function buildOaAnnouncementReadBody(args) {
|
|
3003
|
+
const body = hasExplicitBody(args) ? objectWriteBody(args) : {};
|
|
3004
|
+
const announcementId = args.announcementId ?? args.id ?? body.announcementId;
|
|
3005
|
+
if (isBlank(announcementId)) {
|
|
3006
|
+
throw new Error("标记 OA 公告已读请提供 announcementId 或 id。");
|
|
3007
|
+
}
|
|
3008
|
+
if (isBlank(body.announcementId)) {
|
|
3009
|
+
body.announcementId = announcementId;
|
|
3010
|
+
}
|
|
3011
|
+
return cleanObject(body);
|
|
3012
|
+
}
|
|
3013
|
+
function requireOaAnnouncementId(args, message) {
|
|
3014
|
+
const announcementId = args.announcementId ?? args.id;
|
|
3015
|
+
if (isBlank(announcementId)) {
|
|
3016
|
+
throw new Error(message);
|
|
3017
|
+
}
|
|
3018
|
+
return announcementId;
|
|
3019
|
+
}
|
|
3020
|
+
function buildOaLogListBody(args) {
|
|
3021
|
+
const body = buildPassthroughBody(args, ["keyword", "query", "q"], {
|
|
3022
|
+
page: args.page ?? 1,
|
|
3023
|
+
limit: args.limit ?? 15
|
|
3024
|
+
});
|
|
3025
|
+
const search = firstTextValue(args.search, args.keyword, args.query, args.q, args.title, args.content);
|
|
3026
|
+
if (search && body.search === undefined) {
|
|
3027
|
+
body.search = search;
|
|
3028
|
+
}
|
|
3029
|
+
return cleanObject(body);
|
|
3030
|
+
}
|
|
3031
|
+
function buildOaLogTemplateListBody(args) {
|
|
3032
|
+
const body = buildPassthroughBody(args, ["keyword", "query", "q"], {
|
|
3033
|
+
page: args.page ?? 1,
|
|
3034
|
+
limit: args.limit ?? 15
|
|
3035
|
+
});
|
|
3036
|
+
const search = firstTextValue(args.search, args.keyword, args.query, args.q, args.name);
|
|
3037
|
+
if (search && body.search === undefined) {
|
|
3038
|
+
body.search = search;
|
|
3039
|
+
}
|
|
3040
|
+
return cleanObject(body);
|
|
3041
|
+
}
|
|
3042
|
+
function buildOaLogFieldQuery(args) {
|
|
3043
|
+
return cleanQuery({
|
|
3044
|
+
configId: requireOaLogConfigId(args),
|
|
3045
|
+
type: args.type
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
async function executeOaLogWrite(client, args, requireId) {
|
|
3049
|
+
const targetPath = "/oaLog/addOrUpdate";
|
|
3050
|
+
const body = await buildOaLogWriteBody(client, args, requireId);
|
|
3051
|
+
if (!args.confirm) {
|
|
3052
|
+
return previewOaWrite(requireId ? "update" : "create", "oaLog", "日志", targetPath, body);
|
|
3053
|
+
}
|
|
3054
|
+
return {
|
|
3055
|
+
module: "oaLog",
|
|
3056
|
+
moduleName: "日志",
|
|
3057
|
+
targetPath,
|
|
3058
|
+
body,
|
|
3059
|
+
data: await client.post(targetPath, body)
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
async function buildOaLogWriteBody(client, args, requireId) {
|
|
3063
|
+
if (hasExplicitBody(args)) {
|
|
3064
|
+
return finalizeOaLogWriteBody(objectWriteBody(args), args, requireId);
|
|
3065
|
+
}
|
|
3066
|
+
const body = buildOaLogBaseBody(args);
|
|
3067
|
+
const fieldValuesInput = args.fieldValues ?? args.updates ?? args.fields ?? args.fieldForm;
|
|
3068
|
+
const shouldBuildFields = !Array.isArray(args.field)
|
|
3069
|
+
&& !Array.isArray(args.fields)
|
|
3070
|
+
&& !isBlank(args.categoryId)
|
|
3071
|
+
&& fieldValuesInput
|
|
3072
|
+
&& typeof fieldValuesInput === "object";
|
|
3073
|
+
if (shouldBuildFields) {
|
|
3074
|
+
const logId = args.logId ?? args.id;
|
|
3075
|
+
const targetPath = logId ? `/oaLogTemplateField/field/${logId}` : "/oaLogTemplateField/field";
|
|
3076
|
+
const fieldMetadata = await client.post(targetPath, undefined, buildOaLogFieldQuery(args));
|
|
3077
|
+
const fields = flattenOaExamineFields(fieldMetadata);
|
|
3078
|
+
const fieldValues = normalizeFieldValues(fieldValuesInput);
|
|
3079
|
+
for (const [rawKey, rawValue] of Object.entries(fieldValues)) {
|
|
3080
|
+
const field = findOaExamineField(fields, rawKey);
|
|
3081
|
+
if (!field) {
|
|
3082
|
+
throw new Error(`OA 日志模板 ${args.categoryId} 未找到字段“${rawKey}”,请先用 crm_get_oa_log_fields 查询字段名。`);
|
|
3083
|
+
}
|
|
3084
|
+
appendOaLogFieldValue(body, field, rawValue);
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
else if (Array.isArray(args.field)) {
|
|
3088
|
+
body.field = args.field;
|
|
3089
|
+
}
|
|
3090
|
+
else if (Array.isArray(args.fields)) {
|
|
3091
|
+
body.field = args.fields;
|
|
3092
|
+
}
|
|
3093
|
+
return finalizeOaLogWriteBody(body, args, requireId);
|
|
3094
|
+
}
|
|
3095
|
+
function buildOaLogBaseBody(args) {
|
|
3096
|
+
const body = buildPassthroughBody(args, [
|
|
3097
|
+
"id",
|
|
3098
|
+
"fields",
|
|
3099
|
+
"fieldValues",
|
|
3100
|
+
"updates",
|
|
3101
|
+
"fieldForm",
|
|
3102
|
+
"type"
|
|
3103
|
+
]);
|
|
3104
|
+
for (const key of ["sendUserIds", "sendDeptIds", "readUserIds", "customerIds", "contactsIds", "businessIds", "contractIds", "receivablesIds", "quotationIds"]) {
|
|
3105
|
+
if (body[key] !== undefined) {
|
|
3106
|
+
body[key] = toCommaSeparated(body[key]);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
if (!Array.isArray(body.field)) {
|
|
3110
|
+
body.field = [];
|
|
3111
|
+
}
|
|
3112
|
+
return cleanObject(body);
|
|
3113
|
+
}
|
|
3114
|
+
function appendOaLogFieldValue(body, field, rawValue) {
|
|
3115
|
+
const value = getOaExamineRealValue(field, rawValue);
|
|
3116
|
+
if (Number(field.fieldType) === 1) {
|
|
3117
|
+
body[field.fieldName] = isEmptyValue(value) ? "" : value;
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
if (field.formType !== "desc_text") {
|
|
3121
|
+
body.field.push({
|
|
3122
|
+
fieldName: field.fieldName,
|
|
3123
|
+
fieldType: field.fieldType,
|
|
3124
|
+
name: field.name,
|
|
3125
|
+
type: field.type,
|
|
3126
|
+
fieldId: field.fieldId,
|
|
3127
|
+
value
|
|
3128
|
+
});
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
function finalizeOaLogWriteBody(body, args, requireId) {
|
|
3132
|
+
const logId = args.logId ?? args.id ?? body.logId;
|
|
3133
|
+
if (requireId && isBlank(logId)) {
|
|
3134
|
+
throw new Error("编辑 OA 日志请提供 logId 或 id。");
|
|
3135
|
+
}
|
|
3136
|
+
if (!isBlank(logId) && isBlank(body.logId)) {
|
|
3137
|
+
body.logId = logId;
|
|
3138
|
+
}
|
|
3139
|
+
if (!Array.isArray(body.field)) {
|
|
3140
|
+
body.field = [];
|
|
3141
|
+
}
|
|
3142
|
+
return cleanObject(body);
|
|
3143
|
+
}
|
|
3144
|
+
function requireOaLogId(args, message) {
|
|
3145
|
+
const logId = args.logId ?? args.id;
|
|
3146
|
+
if (isBlank(logId)) {
|
|
3147
|
+
throw new Error(message);
|
|
3148
|
+
}
|
|
3149
|
+
return logId;
|
|
3150
|
+
}
|
|
3151
|
+
function requireOaLogConfigId(args) {
|
|
3152
|
+
const configId = args.configId ?? args.categoryId;
|
|
3153
|
+
if (isBlank(configId)) {
|
|
3154
|
+
throw new Error("查询 OA 日志模板字段或完成统计请提供 configId 或 categoryId。");
|
|
3155
|
+
}
|
|
3156
|
+
return configId;
|
|
3157
|
+
}
|
|
3158
|
+
function buildOaLogFavourQuery(args) {
|
|
3159
|
+
return {
|
|
3160
|
+
logId: requireOaLogId(args, "点赞或取消点赞 OA 日志请提供 logId 或 id。"),
|
|
3161
|
+
isFavour: args.isFavour ?? args.favour ?? args.favorite ?? true
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
async function executeCrmDocumentWrite(client, moduleKey, args, requireId) {
|
|
3165
|
+
const definition = getCrmDocumentWriteDefinition(moduleKey);
|
|
3166
|
+
const operation = requireId ? "update" : "add";
|
|
3167
|
+
const targetPath = `${definition.basePath}/${operation}`;
|
|
3168
|
+
const body = await buildCrmDocumentWriteBody(client, definition, args, requireId);
|
|
3169
|
+
const approvalNotice = getCrmDocumentApprovalNotice(definition, body);
|
|
3170
|
+
if (!args.confirm) {
|
|
3171
|
+
return previewCrmDocumentWrite(definition, operation, targetPath, body, approvalNotice);
|
|
3172
|
+
}
|
|
3173
|
+
return cleanObject({
|
|
3174
|
+
module: definition.key,
|
|
3175
|
+
moduleName: definition.name,
|
|
3176
|
+
targetPath,
|
|
3177
|
+
body,
|
|
3178
|
+
approvalNotice,
|
|
3179
|
+
data: await client.post(targetPath, body)
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
function getCrmDocumentWriteDefinition(moduleKey) {
|
|
3183
|
+
const definition = CRM_DOCUMENT_WRITE_DEFINITIONS.find((item) => item.key === moduleKey);
|
|
3184
|
+
if (!definition) {
|
|
3185
|
+
throw new Error(`当前 MCP 未配置 ${moduleKey} 单据写入。`);
|
|
3186
|
+
}
|
|
3187
|
+
return definition;
|
|
3188
|
+
}
|
|
3189
|
+
async function buildCrmDocumentWriteBody(client, definition, args, requireId) {
|
|
3190
|
+
if (hasExplicitBody(args)) {
|
|
3191
|
+
return finalizeCrmDocumentBody(definition, objectWriteBody(args), args, requireId);
|
|
3192
|
+
}
|
|
3193
|
+
if (hasRawCrmDocumentShape(args)) {
|
|
3194
|
+
return finalizeCrmDocumentBody(definition, buildRawCrmDocumentBody(args), args, requireId);
|
|
3195
|
+
}
|
|
3196
|
+
return buildStructuredCrmDocumentBody(client, definition, args, requireId);
|
|
3197
|
+
}
|
|
3198
|
+
function hasExplicitBody(args) {
|
|
3199
|
+
return Object.prototype.hasOwnProperty.call(args, "body") || Object.prototype.hasOwnProperty.call(args, "data");
|
|
3200
|
+
}
|
|
3201
|
+
function hasRawCrmDocumentShape(args) {
|
|
3202
|
+
return isPlainObject(args.entity)
|
|
3203
|
+
|| Array.isArray(args.field)
|
|
3204
|
+
|| Array.isArray(args.fields)
|
|
3205
|
+
|| Array.isArray(args.product)
|
|
3206
|
+
|| isPlainObject(args.product)
|
|
3207
|
+
|| Array.isArray(args.contract);
|
|
3208
|
+
}
|
|
3209
|
+
function buildRawCrmDocumentBody(args) {
|
|
3210
|
+
return cleanObject({
|
|
3211
|
+
entity: isPlainObject(args.entity) ? { ...args.entity } : {},
|
|
3212
|
+
field: Array.isArray(args.field) ? args.field : Array.isArray(args.fields) ? args.fields : [],
|
|
3213
|
+
product: args.product,
|
|
3214
|
+
contract: args.contract,
|
|
3215
|
+
examineFlowData: args.examineFlowData,
|
|
3216
|
+
...safeExtra(args.extra)
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
async function buildStructuredCrmDocumentBody(client, definition, args, requireId) {
|
|
3220
|
+
const fieldValues = normalizeFieldValues(args.fieldValues ?? args.updates ?? args.fields ?? args.fieldForm);
|
|
3221
|
+
if (Object.keys(fieldValues).length === 0 && !isPlainObject(args.entity)) {
|
|
3222
|
+
throw new Error(`请提供 ${definition.name} 的 body/data 原始 payload,或提供 entity/field,或提供 fields/updates 让 MCP 自动组装。`);
|
|
3223
|
+
}
|
|
3224
|
+
const recordId = args[definition.primaryKey] ?? args.id ?? args.recordId;
|
|
3225
|
+
const fieldPath = requireId && !isBlank(recordId) ? `${definition.basePath}/field/${recordId}` : `${definition.basePath}/field`;
|
|
3226
|
+
const fieldMetadata = await client.post(fieldPath, undefined, cleanQuery({ type: args.type }));
|
|
3227
|
+
const fields = flattenOaExamineFields(fieldMetadata);
|
|
3228
|
+
if (fields.length === 0 && Object.keys(fieldValues).length > 0) {
|
|
3229
|
+
throw new Error(`未从 ${fieldPath} 读取到 ${definition.name} 字段,无法自动组装单据字段。`);
|
|
3230
|
+
}
|
|
3231
|
+
const params = {
|
|
3232
|
+
entity: isPlainObject(args.entity) ? { ...args.entity } : {},
|
|
3233
|
+
field: []
|
|
3234
|
+
};
|
|
3235
|
+
for (const [rawKey, rawValue] of Object.entries(fieldValues)) {
|
|
3236
|
+
const field = findOaExamineField(fields, rawKey);
|
|
3237
|
+
if (!field) {
|
|
3238
|
+
throw new Error(`${definition.name} 未找到字段“${rawKey}”,请先用 crm_get_module_schema 查询字段名。`);
|
|
3239
|
+
}
|
|
3240
|
+
appendCrmDocumentFieldValue(params, field, rawValue);
|
|
3241
|
+
}
|
|
3242
|
+
if (args.product !== undefined && params.product === undefined) {
|
|
3243
|
+
params.product = args.product;
|
|
3244
|
+
}
|
|
3245
|
+
if (args.contract !== undefined && params.contract === undefined) {
|
|
3246
|
+
params.contract = args.contract;
|
|
3247
|
+
}
|
|
3248
|
+
if (args.examineFlowData !== undefined) {
|
|
3249
|
+
params.examineFlowData = args.examineFlowData;
|
|
3250
|
+
}
|
|
3251
|
+
return finalizeCrmDocumentBody(definition, params, args, requireId);
|
|
3252
|
+
}
|
|
3253
|
+
function appendCrmDocumentFieldValue(params, field, rawValue) {
|
|
3254
|
+
if (field.formType === "product" && !field.isModuleSelect) {
|
|
3255
|
+
appendCrmProductParams(params, rawValue, Array.isArray(field.productFields) ? field.productFields : []);
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
if (field.formType === "map_address") {
|
|
3259
|
+
appendCrmAddressParams(params.entity, rawValue);
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3262
|
+
const value = getOaExamineRealValue(field, rawValue);
|
|
3263
|
+
if (Number(field.fieldType) === 1) {
|
|
3264
|
+
params.entity[field.fieldName] = isEmptyValue(value) ? "" : value;
|
|
3265
|
+
return;
|
|
3266
|
+
}
|
|
3267
|
+
if (field.formType !== "desc_text") {
|
|
3268
|
+
params.field.push({
|
|
3269
|
+
fieldName: field.fieldName,
|
|
3270
|
+
fieldType: field.fieldType,
|
|
3271
|
+
name: field.name,
|
|
3272
|
+
type: field.type,
|
|
3273
|
+
fieldId: field.fieldId,
|
|
3274
|
+
value
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
function appendCrmProductParams(params, rawValue, productFields) {
|
|
3279
|
+
if (!rawValue) {
|
|
3280
|
+
params.product = [];
|
|
3281
|
+
params.entity.totalPrice = "";
|
|
3282
|
+
params.entity.discountRate = "";
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
const productValue = isPlainObject(rawValue) ? rawValue : { product: rawValue };
|
|
3286
|
+
const products = Array.isArray(productValue.product) ? productValue.product : Array.isArray(rawValue) ? rawValue : [];
|
|
3287
|
+
params.product = products.map((item) => {
|
|
3288
|
+
if (!isPlainObject(item)) {
|
|
3289
|
+
return item;
|
|
3290
|
+
}
|
|
3291
|
+
const product = { ...item };
|
|
3292
|
+
for (const [key, value] of Object.entries(product)) {
|
|
3293
|
+
const field = productFields.find((productField) => productField.fieldName === key);
|
|
3294
|
+
if (field && Number(field.fieldType) === 0) {
|
|
3295
|
+
product[key] = getOaExamineRealValue(field, value);
|
|
3296
|
+
}
|
|
356
3297
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return text;
|
|
3298
|
+
if (Array.isArray(product.name) && product.name.length > 0 && isPlainObject(product.name[0])) {
|
|
3299
|
+
product.name = product.name[0].name ?? "";
|
|
360
3300
|
}
|
|
3301
|
+
product.salesPrice = product.salesPrice === "" || product.salesPrice === undefined ? 0 : product.salesPrice;
|
|
3302
|
+
product.num = product.num === "" || product.num === undefined ? 0 : product.num;
|
|
3303
|
+
product.discount = product.discount === "" || product.discount === undefined ? 0 : product.discount;
|
|
3304
|
+
return product;
|
|
3305
|
+
});
|
|
3306
|
+
params.entity.totalPrice = productValue.totalPrice ?? 0;
|
|
3307
|
+
params.entity.discountRate = productValue.discountRate ?? 0;
|
|
3308
|
+
}
|
|
3309
|
+
function appendCrmAddressParams(entity, rawValue) {
|
|
3310
|
+
if (!isPlainObject(rawValue)) {
|
|
3311
|
+
entity.address = "";
|
|
3312
|
+
entity.detailAddress = "";
|
|
3313
|
+
entity.location = "";
|
|
3314
|
+
entity.lng = "";
|
|
3315
|
+
entity.lat = "";
|
|
3316
|
+
return;
|
|
361
3317
|
}
|
|
362
|
-
|
|
3318
|
+
const value = rawValue;
|
|
3319
|
+
entity.address = Array.isArray(value.address) ? value.address.join(",") : value.address ?? "";
|
|
3320
|
+
entity.detailAddress = value.detailAddress;
|
|
3321
|
+
entity.location = value.location;
|
|
3322
|
+
entity.lng = value.lng;
|
|
3323
|
+
entity.lat = value.lat;
|
|
363
3324
|
}
|
|
364
|
-
function
|
|
365
|
-
if (
|
|
366
|
-
|
|
3325
|
+
function finalizeCrmDocumentBody(definition, body, args, requireId) {
|
|
3326
|
+
if (!isPlainObject(body.entity)) {
|
|
3327
|
+
body.entity = {};
|
|
367
3328
|
}
|
|
368
|
-
|
|
369
|
-
|
|
3329
|
+
else {
|
|
3330
|
+
body.entity = { ...body.entity };
|
|
370
3331
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const searchList = buildSearchList(args, moduleDefinition);
|
|
375
|
-
if (searchList.length === 0) {
|
|
376
|
-
throw new Error(`请提供可定位${moduleDefinition.name}的 customerId/id、phone/mobile、name/customerName 或 keyword。`);
|
|
3332
|
+
const recordId = args[definition.primaryKey] ?? args.id ?? args.recordId ?? body.entity[definition.primaryKey];
|
|
3333
|
+
if (requireId && isBlank(recordId)) {
|
|
3334
|
+
throw new Error(`编辑${definition.name}请提供 ${definition.primaryKey} 或 id。`);
|
|
377
3335
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
limit: 2,
|
|
381
|
-
pageType: 1,
|
|
382
|
-
label: moduleDefinition.label,
|
|
383
|
-
searchList
|
|
384
|
-
}));
|
|
385
|
-
const list = Array.isArray(page.list) ? page.list : [];
|
|
386
|
-
if (list.length !== 1) {
|
|
387
|
-
throw new Error(`需要唯一命中的${moduleDefinition.name}才能写入,当前命中 ${page.totalRow ?? list.length} 条,请先缩小查询条件或直接传入 ID。`);
|
|
3336
|
+
if (!isBlank(recordId) && isBlank(body.entity[definition.primaryKey])) {
|
|
3337
|
+
body.entity[definition.primaryKey] = recordId;
|
|
388
3338
|
}
|
|
389
|
-
|
|
3339
|
+
if (requireId && !isBlank(args.batchId) && isBlank(body.entity.batchId)) {
|
|
3340
|
+
body.entity.batchId = args.batchId;
|
|
3341
|
+
}
|
|
3342
|
+
if ((args.draft || args.isDraft) && isBlank(body.entity.checkStatus)) {
|
|
3343
|
+
body.entity.checkStatus = 5;
|
|
3344
|
+
}
|
|
3345
|
+
if (!Array.isArray(body.field)) {
|
|
3346
|
+
body.field = [];
|
|
3347
|
+
}
|
|
3348
|
+
return cleanObject(body);
|
|
390
3349
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return {
|
|
397
|
-
...rawBody,
|
|
398
|
-
batchId,
|
|
399
|
-
list
|
|
400
|
-
};
|
|
3350
|
+
function getCrmDocumentApprovalNotice(definition, body) {
|
|
3351
|
+
if (!definition.requiresApprovalFlow || body.examineFlowData || body.entity?.checkStatus === 5) {
|
|
3352
|
+
return undefined;
|
|
3353
|
+
}
|
|
3354
|
+
return `${definition.name}通常会走审批流;当前请求未包含 examineFlowData。如线上审批配置要求审批,请先补充审批流数据,或传 draft=true 保存草稿。`;
|
|
401
3355
|
}
|
|
402
|
-
function
|
|
3356
|
+
function previewCrmDocumentWrite(definition, operation, targetPath, body, approvalNotice) {
|
|
403
3357
|
return cleanObject({
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
3358
|
+
preview: true,
|
|
3359
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
3360
|
+
module: definition.key,
|
|
3361
|
+
moduleName: definition.name,
|
|
3362
|
+
operation,
|
|
3363
|
+
targetPath,
|
|
3364
|
+
approvalNotice,
|
|
3365
|
+
body
|
|
410
3366
|
});
|
|
411
3367
|
}
|
|
412
|
-
function
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
3368
|
+
function buildAddressBookListBody(args) {
|
|
3369
|
+
const body = buildPassthroughBody(args, ["keyword", "query", "q", "name"], {
|
|
3370
|
+
page: args.page ?? 1,
|
|
3371
|
+
limit: args.limit ?? 15
|
|
3372
|
+
});
|
|
3373
|
+
const search = firstTextValue(args.search, args.keyword, args.query, args.q, args.realname, args.name);
|
|
3374
|
+
if (search && body.search === undefined && body.realname === undefined) {
|
|
3375
|
+
body.search = search;
|
|
417
3376
|
}
|
|
418
|
-
const
|
|
419
|
-
if (!
|
|
420
|
-
|
|
3377
|
+
const deptId = args.deptId ?? args.departmentId;
|
|
3378
|
+
if (!isBlank(deptId) && body.deptId === undefined) {
|
|
3379
|
+
body.deptId = deptId;
|
|
421
3380
|
}
|
|
422
|
-
return
|
|
3381
|
+
return cleanObject(body);
|
|
423
3382
|
}
|
|
424
|
-
function
|
|
425
|
-
|
|
3383
|
+
function buildAddressBookDepartmentBody(args) {
|
|
3384
|
+
const body = buildPassthroughBody(args, ["keyword", "query", "q"], {
|
|
3385
|
+
type: args.type ?? "tree"
|
|
3386
|
+
});
|
|
3387
|
+
const name = firstTextValue(args.name, args.deptName, args.keyword, args.query, args.q);
|
|
3388
|
+
if (name && body.name === undefined && body.deptName === undefined) {
|
|
3389
|
+
body.name = name;
|
|
3390
|
+
}
|
|
3391
|
+
return cleanObject(body);
|
|
426
3392
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const path = id ? `${moduleDefinition.basePath}/field/${id}` : `${moduleDefinition.basePath}/field`;
|
|
430
|
-
const data = await client.post(path, undefined, cleanQuery({ type: args.type, poolId: args.poolId }));
|
|
431
|
-
return flattenFieldDefinitions(data);
|
|
3393
|
+
function addressBookDeptIds(args) {
|
|
3394
|
+
return requireIdArray(args.deptIds ?? args.ids ?? args.deptId ?? args.id, "查询通讯录部门用户请提供 deptIds/ids 数组或 deptId/id。");
|
|
432
3395
|
}
|
|
433
|
-
function
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const record = value;
|
|
446
|
-
if (record.fieldName || record.fieldId || (record.name && (record.fieldType !== undefined || record.type !== undefined || record.formType))) {
|
|
447
|
-
output.push(record);
|
|
448
|
-
}
|
|
449
|
-
for (const child of Object.values(record)) {
|
|
450
|
-
if (child && typeof child === "object") {
|
|
451
|
-
visit(child);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
visit(input);
|
|
456
|
-
return output;
|
|
3396
|
+
function buildAddressBookUserDeptRoleBody(args) {
|
|
3397
|
+
const body = cleanObject({
|
|
3398
|
+
userIds: args.userIds,
|
|
3399
|
+
deptIds: args.deptIds,
|
|
3400
|
+
roleIds: args.roleIds
|
|
3401
|
+
});
|
|
3402
|
+
if (!Array.isArray(body.userIds) && !Array.isArray(body.deptIds) && !Array.isArray(body.roleIds)) {
|
|
3403
|
+
throw new Error("查询通讯录用户、部门、角色聚合信息请至少提供 userIds、deptIds 或 roleIds。");
|
|
3404
|
+
}
|
|
3405
|
+
return body;
|
|
457
3406
|
}
|
|
458
|
-
function
|
|
459
|
-
|
|
3407
|
+
function requireAddressBookUserId(args, message) {
|
|
3408
|
+
const userId = args.userId ?? args.id;
|
|
3409
|
+
if (isBlank(userId)) {
|
|
3410
|
+
throw new Error(message);
|
|
3411
|
+
}
|
|
3412
|
+
return userId;
|
|
460
3413
|
}
|
|
461
|
-
function
|
|
462
|
-
const
|
|
463
|
-
if (!
|
|
464
|
-
|
|
3414
|
+
function requireAddressBookUsername(args) {
|
|
3415
|
+
const username = firstTextValue(args.username, args.userName, args.name);
|
|
3416
|
+
if (!username) {
|
|
3417
|
+
throw new Error("查询通讯录用户数量请提供 username 或 userName。");
|
|
465
3418
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
3419
|
+
return username;
|
|
3420
|
+
}
|
|
3421
|
+
function buildAddressBookAttentionBody(args) {
|
|
3422
|
+
const body = objectWriteBody(args);
|
|
3423
|
+
const userId = args.userId ?? args.id ?? body.userId;
|
|
3424
|
+
if (isBlank(userId)) {
|
|
3425
|
+
throw new Error("切换通讯录关注状态请提供 userId 或 id。");
|
|
469
3426
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
fieldType: item.fieldType ?? field.fieldType,
|
|
475
|
-
type: item.type ?? field.type,
|
|
476
|
-
formType: item.formType ?? field.formType,
|
|
477
|
-
value: normalizeOptionValue(item.value, field)
|
|
478
|
-
});
|
|
479
|
-
return normalizeUpdateItem(merged);
|
|
3427
|
+
if (isBlank(body.userId)) {
|
|
3428
|
+
body.userId = userId;
|
|
3429
|
+
}
|
|
3430
|
+
return cleanObject(body);
|
|
480
3431
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
3432
|
+
function buildOaExamineCategoryBody(args) {
|
|
3433
|
+
const body = buildPassthroughBody(args, ["keyword", "query", "q"], {
|
|
3434
|
+
pageType: args.pageType ?? 0,
|
|
3435
|
+
label: args.label ?? 0
|
|
3436
|
+
});
|
|
3437
|
+
const search = firstTextValue(args.search, args.keyword, args.query, args.q, args.name);
|
|
3438
|
+
if (search && body.search === undefined) {
|
|
3439
|
+
body.search = search;
|
|
3440
|
+
}
|
|
3441
|
+
return cleanObject(body);
|
|
483
3442
|
}
|
|
484
|
-
function
|
|
485
|
-
return
|
|
486
|
-
|
|
487
|
-
fieldName: item.fieldName,
|
|
488
|
-
name: item.name,
|
|
489
|
-
fieldType: item.fieldType,
|
|
490
|
-
type: item.type,
|
|
491
|
-
formType: item.formType,
|
|
492
|
-
value: item.value
|
|
3443
|
+
function buildOaExamineGroupBody(args) {
|
|
3444
|
+
return buildPassthroughBody(args, [], {
|
|
3445
|
+
groupType: args.groupType ?? 0
|
|
493
3446
|
});
|
|
494
3447
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
3448
|
+
function getOaExamineListDefinition(args) {
|
|
3449
|
+
const listType = firstTextValue(args.listType, args.view, args.scene, args.kind, args.mode, "upcoming") ?? "upcoming";
|
|
3450
|
+
const tab = firstTextValue(args.tab, args.scope);
|
|
3451
|
+
const candidates = [
|
|
3452
|
+
tab ? `${listType}.${tab}` : undefined,
|
|
3453
|
+
tab ? `${listType}${tab}` : undefined,
|
|
3454
|
+
listType
|
|
3455
|
+
]
|
|
3456
|
+
.filter((value) => Boolean(value))
|
|
3457
|
+
.map(normalizeOaExamineAlias);
|
|
3458
|
+
const definition = candidates
|
|
3459
|
+
.map((candidate) => OA_EXAMINE_LIST_ENDPOINTS.find((item) => item.aliases.some((alias) => normalizeOaExamineAlias(alias) === candidate)))
|
|
3460
|
+
.find((item) => Boolean(item));
|
|
3461
|
+
if (!definition) {
|
|
3462
|
+
throw new Error(`当前 MCP 支持的 OA 审批列表类型:${OA_EXAMINE_LIST_ENDPOINTS.map((item) => item.key).join("、")}。`);
|
|
498
3463
|
}
|
|
499
|
-
|
|
500
|
-
return findFirstValueByKey(data, "batchId");
|
|
3464
|
+
return definition;
|
|
501
3465
|
}
|
|
502
|
-
function
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
3466
|
+
function buildOaExamineListBody(args) {
|
|
3467
|
+
const body = buildPassthroughBody(args, ["listType", "view", "scene", "kind", "mode", "tab", "scope", "keyword", "query", "q", "filters"], {
|
|
3468
|
+
page: args.page ?? 1,
|
|
3469
|
+
limit: args.limit ?? 15
|
|
506
3470
|
});
|
|
3471
|
+
const search = firstTextValue(args.search, args.keyword, args.query, args.q, args.name, args.content);
|
|
3472
|
+
if (search && body.search === undefined) {
|
|
3473
|
+
body.search = search;
|
|
3474
|
+
}
|
|
3475
|
+
if (args.filters && body.searchBO === undefined) {
|
|
3476
|
+
body.searchBO = { searchList: args.filters };
|
|
3477
|
+
}
|
|
3478
|
+
return cleanObject(body);
|
|
507
3479
|
}
|
|
508
|
-
function
|
|
509
|
-
|
|
3480
|
+
function buildOaExamineFieldBody(args) {
|
|
3481
|
+
return buildPassthroughBody(args, ["id", "examineIdForFields"], {
|
|
3482
|
+
examineId: requireOaExamineId(args, "查询 OA 审批详情字段请提供 examineId 或 id。"),
|
|
3483
|
+
isDetail: args.isDetail ?? 1,
|
|
3484
|
+
type: args.type ?? 1
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
function buildOaExamineCreateFieldQuery(args, categoryId) {
|
|
3488
|
+
return buildPassthroughBody(args, [
|
|
3489
|
+
"id",
|
|
3490
|
+
"examineIdForFields",
|
|
3491
|
+
"mode",
|
|
3492
|
+
"useFieldList",
|
|
3493
|
+
"fieldList",
|
|
3494
|
+
"fields",
|
|
3495
|
+
"updates",
|
|
3496
|
+
"fieldValues",
|
|
3497
|
+
"fieldForm",
|
|
3498
|
+
"entity",
|
|
3499
|
+
"oaExamine",
|
|
3500
|
+
"oaExamineRelation",
|
|
3501
|
+
"oaExamineTravelList",
|
|
3502
|
+
"examineFlowData",
|
|
3503
|
+
"batchId",
|
|
3504
|
+
"strictRequired",
|
|
3505
|
+
"customerId",
|
|
3506
|
+
"customerIds",
|
|
3507
|
+
"contactsId",
|
|
3508
|
+
"contactsIds",
|
|
3509
|
+
"businessId",
|
|
3510
|
+
"businessIds",
|
|
3511
|
+
"contractId",
|
|
3512
|
+
"contractIds",
|
|
3513
|
+
"receivablesId",
|
|
3514
|
+
"receivablesIds",
|
|
3515
|
+
"quotationId",
|
|
3516
|
+
"quotationIds"
|
|
3517
|
+
], {
|
|
3518
|
+
categoryId,
|
|
3519
|
+
needHidden: args.needHidden ?? true
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3522
|
+
function buildOaExamineRecordLogBody(args) {
|
|
3523
|
+
const recordId = args.recordId ?? args.examineRecordId ?? args.id;
|
|
3524
|
+
if (isBlank(recordId)) {
|
|
3525
|
+
throw new Error("查询 OA 审批记录日志请提供 recordId、examineRecordId 或 id。");
|
|
3526
|
+
}
|
|
3527
|
+
return buildPassthroughBody(args, ["id", "examineRecordId"], { recordId });
|
|
3528
|
+
}
|
|
3529
|
+
function buildOaExamineFlowRecordBody(args) {
|
|
3530
|
+
const recordId = args.recordId ?? args.examineRecordId ?? args.id;
|
|
3531
|
+
if (isBlank(recordId)) {
|
|
3532
|
+
throw new Error("查询 OA 审批流程节点请提供 recordId、examineRecordId 或 id。");
|
|
3533
|
+
}
|
|
3534
|
+
return buildPassthroughBody(args, ["id", "examineRecordId"], { recordId });
|
|
3535
|
+
}
|
|
3536
|
+
async function buildOaExamineWriteBody(client, args, requireId) {
|
|
3537
|
+
const body = objectWriteBody(args);
|
|
3538
|
+
const examineId = args.examineId ?? args.id ?? body.examineId;
|
|
3539
|
+
if (requireId && isBlank(examineId)) {
|
|
3540
|
+
throw new Error("修改 OA 审批请提供 examineId 或 id。");
|
|
3541
|
+
}
|
|
3542
|
+
if (shouldBuildStructuredOaExamineBody(args, body)) {
|
|
3543
|
+
return buildStructuredOaExamineWriteBody(client, args, examineId);
|
|
3544
|
+
}
|
|
3545
|
+
if (!isBlank(examineId) && isBlank(body.examineId)) {
|
|
3546
|
+
body.examineId = examineId;
|
|
3547
|
+
}
|
|
3548
|
+
return cleanObject(body);
|
|
3549
|
+
}
|
|
3550
|
+
function shouldBuildStructuredOaExamineBody(args, body) {
|
|
3551
|
+
if (body.oaExamine || body.oaExamineRelation || body.oaExamineTravelList || Array.isArray(body.field)) {
|
|
3552
|
+
return false;
|
|
3553
|
+
}
|
|
3554
|
+
const categoryId = args.categoryId ?? body.categoryId;
|
|
3555
|
+
const fieldValues = args.fieldValues ?? args.fields ?? args.updates ?? args.fieldForm;
|
|
3556
|
+
return !isBlank(categoryId) && fieldValues && typeof fieldValues === "object";
|
|
3557
|
+
}
|
|
3558
|
+
async function buildStructuredOaExamineWriteBody(client, args, examineId) {
|
|
3559
|
+
const categoryId = args.categoryId;
|
|
3560
|
+
if (isBlank(categoryId)) {
|
|
3561
|
+
throw new Error("自动组装 OA 审批自定义字段请提供 categoryId。");
|
|
3562
|
+
}
|
|
3563
|
+
const targetPath = `/oaExamineField/queryField/${categoryId}`;
|
|
3564
|
+
const fieldMetadata = await client.post(targetPath, buildOaExamineCreateFieldQuery(args, categoryId));
|
|
3565
|
+
const fields = flattenOaExamineFields(fieldMetadata);
|
|
3566
|
+
if (fields.length === 0) {
|
|
3567
|
+
throw new Error(`未从 ${targetPath} 读取到 OA 审批字段,无法自动组装自定义字段。`);
|
|
3568
|
+
}
|
|
3569
|
+
const fieldValues = normalizeFieldValues(args.fieldValues ?? args.fields ?? args.updates ?? args.fieldForm);
|
|
3570
|
+
const params = {
|
|
3571
|
+
oaExamine: cleanObject({
|
|
3572
|
+
...(isPlainObject(args.oaExamine) ? args.oaExamine : {}),
|
|
3573
|
+
...(isPlainObject(args.entity) ? args.entity : {}),
|
|
3574
|
+
categoryId,
|
|
3575
|
+
examineId: isBlank(examineId) ? undefined : examineId,
|
|
3576
|
+
batchId: args.batchId ?? args.oaExamine?.batchId ?? randomUUID()
|
|
3577
|
+
}),
|
|
3578
|
+
oaExamineRelation: buildOaExamineRelation(args),
|
|
3579
|
+
field: [],
|
|
3580
|
+
oaExamineTravelList: Array.isArray(args.oaExamineTravelList) ? [...args.oaExamineTravelList] : []
|
|
3581
|
+
};
|
|
3582
|
+
for (const [rawKey, rawValue] of Object.entries(fieldValues)) {
|
|
3583
|
+
const field = findOaExamineField(fields, rawKey);
|
|
3584
|
+
if (!field) {
|
|
3585
|
+
throw new Error(`OA 审批类型 ${categoryId} 未找到字段“${rawKey}”,请先用 crm_get_oa_examine_fields 查询字段名。`);
|
|
3586
|
+
}
|
|
3587
|
+
appendOaExamineFieldValue(params, field, rawValue);
|
|
3588
|
+
}
|
|
3589
|
+
if (args.examineFlowData !== undefined) {
|
|
3590
|
+
params.examineFlowData = args.examineFlowData;
|
|
3591
|
+
}
|
|
3592
|
+
if (args.strictRequired) {
|
|
3593
|
+
const missing = findMissingRequiredOaFields(fields, fieldValues);
|
|
3594
|
+
if (missing.length > 0) {
|
|
3595
|
+
throw new Error(`OA 审批缺少必填字段:${missing.map((field) => field.name ?? field.fieldName).join("、")}。`);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
return cleanObject(params);
|
|
3599
|
+
}
|
|
3600
|
+
function flattenOaExamineFields(input) {
|
|
3601
|
+
const output = [];
|
|
510
3602
|
const visit = (value) => {
|
|
511
|
-
if (!
|
|
512
|
-
return
|
|
3603
|
+
if (!Array.isArray(value)) {
|
|
3604
|
+
return;
|
|
513
3605
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
3606
|
+
for (const item of value) {
|
|
3607
|
+
if (Array.isArray(item)) {
|
|
3608
|
+
visit(item);
|
|
3609
|
+
}
|
|
3610
|
+
else if (isPlainObject(item)) {
|
|
3611
|
+
const record = item;
|
|
3612
|
+
if (record.fieldName || record.fieldId) {
|
|
3613
|
+
output.push(record);
|
|
3614
|
+
}
|
|
3615
|
+
for (const key of ["list", "fieldList", "children"]) {
|
|
3616
|
+
if (Array.isArray(record[key])) {
|
|
3617
|
+
visit(record[key]);
|
|
3618
|
+
}
|
|
520
3619
|
}
|
|
521
3620
|
}
|
|
522
|
-
return undefined;
|
|
523
3621
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
3622
|
+
};
|
|
3623
|
+
visit(input);
|
|
3624
|
+
return output;
|
|
3625
|
+
}
|
|
3626
|
+
function normalizeFieldValues(input) {
|
|
3627
|
+
if (Array.isArray(input)) {
|
|
3628
|
+
const output = {};
|
|
3629
|
+
for (const item of input) {
|
|
3630
|
+
if (!isPlainObject(item)) {
|
|
3631
|
+
continue;
|
|
3632
|
+
}
|
|
3633
|
+
const record = item;
|
|
3634
|
+
const key = record.fieldName ?? record.name ?? record.field ?? record.fieldId;
|
|
3635
|
+
if (!isBlank(key)) {
|
|
3636
|
+
output[String(key)] = Object.prototype.hasOwnProperty.call(record, "value") ? record.value : record;
|
|
3637
|
+
}
|
|
527
3638
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
3639
|
+
return output;
|
|
3640
|
+
}
|
|
3641
|
+
if (isPlainObject(input)) {
|
|
3642
|
+
return input;
|
|
3643
|
+
}
|
|
3644
|
+
return {};
|
|
3645
|
+
}
|
|
3646
|
+
function findOaExamineField(fields, key) {
|
|
3647
|
+
const target = normalizeFieldLookupKey(key);
|
|
3648
|
+
return fields.find((field) => [field.fieldName, field.name, field.field, field.fieldId]
|
|
3649
|
+
.some((value) => normalizeFieldLookupKey(value) === target));
|
|
3650
|
+
}
|
|
3651
|
+
function appendOaExamineFieldValue(params, field, rawValue) {
|
|
3652
|
+
if (field.fieldName === "cause") {
|
|
3653
|
+
appendOaExamineCauseValue(params, field, rawValue);
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
const value = getOaExamineRealValue(field, rawValue);
|
|
3657
|
+
if (Number(field.fieldType) === 1) {
|
|
3658
|
+
params.oaExamine[field.fieldName] = isEmptyValue(value) ? "" : value;
|
|
3659
|
+
return;
|
|
3660
|
+
}
|
|
3661
|
+
if (field.formType !== "desc_text") {
|
|
3662
|
+
params.field.push({
|
|
3663
|
+
fieldName: field.fieldName,
|
|
3664
|
+
fieldType: field.fieldType,
|
|
3665
|
+
name: field.name,
|
|
3666
|
+
type: field.type,
|
|
3667
|
+
fieldId: field.fieldId,
|
|
3668
|
+
value
|
|
3669
|
+
});
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
function appendOaExamineCauseValue(params, field, rawValue) {
|
|
3673
|
+
const value = isPlainObject(rawValue) && Array.isArray(rawValue.list) ? rawValue : { list: rawValue };
|
|
3674
|
+
const list = Array.isArray(value.list) ? value.list : [];
|
|
3675
|
+
if (field.formType === "business_cause") {
|
|
3676
|
+
params.oaExamineTravelList = list;
|
|
3677
|
+
return;
|
|
3678
|
+
}
|
|
3679
|
+
if (field.formType === "examine_cause") {
|
|
3680
|
+
for (const item of list) {
|
|
3681
|
+
if (isPlainObject(item)) {
|
|
3682
|
+
const copy = { ...item };
|
|
3683
|
+
delete copy.imgList;
|
|
3684
|
+
params.oaExamineTravelList.push(copy);
|
|
532
3685
|
}
|
|
533
3686
|
}
|
|
534
|
-
|
|
535
|
-
};
|
|
536
|
-
return visit(input);
|
|
3687
|
+
}
|
|
537
3688
|
}
|
|
538
|
-
function
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (fieldId && String(field.fieldId ?? field.id) === fieldId) {
|
|
544
|
-
return true;
|
|
3689
|
+
function getOaExamineRealValue(field, value) {
|
|
3690
|
+
const formType = field.formType;
|
|
3691
|
+
if (formType === "contract" && field.multiContract) {
|
|
3692
|
+
if (field.multiType === "receivables") {
|
|
3693
|
+
return value;
|
|
545
3694
|
}
|
|
546
|
-
if (
|
|
547
|
-
return
|
|
3695
|
+
if (field.multiType === "invoice" && Array.isArray(value)) {
|
|
3696
|
+
return value.map((item) => {
|
|
3697
|
+
if (!isPlainObject(item)) {
|
|
3698
|
+
return item;
|
|
3699
|
+
}
|
|
3700
|
+
const record = item;
|
|
3701
|
+
return {
|
|
3702
|
+
contractId: record.contractId,
|
|
3703
|
+
receivablesIdList: Array.isArray(record.receivablesList)
|
|
3704
|
+
? record.receivablesList.map((receivable) => receivable.receivablesId)
|
|
3705
|
+
: [],
|
|
3706
|
+
currInvoiceMoney: record.currInvoiceMoney,
|
|
3707
|
+
remark: record.remark
|
|
3708
|
+
};
|
|
3709
|
+
});
|
|
548
3710
|
}
|
|
549
|
-
|
|
550
|
-
|
|
3711
|
+
}
|
|
3712
|
+
if (isModuleSelectFormType(formType)) {
|
|
3713
|
+
return getModuleSelectValue(field, value);
|
|
3714
|
+
}
|
|
3715
|
+
if (formType === "user" || formType === "single_user" || formType === "structure") {
|
|
3716
|
+
return getUserOrStructureValue(formType, value);
|
|
3717
|
+
}
|
|
3718
|
+
if (formType === "file") {
|
|
3719
|
+
return getFileBatchId(value);
|
|
3720
|
+
}
|
|
3721
|
+
if (formType === "category") {
|
|
3722
|
+
return Array.isArray(value) ? value[value.length - 1] : value ?? "";
|
|
3723
|
+
}
|
|
3724
|
+
if (formType === "checkbox") {
|
|
3725
|
+
return Array.isArray(value) ? value.join(",") : value;
|
|
3726
|
+
}
|
|
3727
|
+
if (formType === "detail_table") {
|
|
3728
|
+
return getDetailTableValue(field, value);
|
|
3729
|
+
}
|
|
3730
|
+
if (formType === "number" || formType === "floatnumber") {
|
|
3731
|
+
return typeof value === "number" ? (Number.isNaN(value) ? "" : String(value)) : value;
|
|
3732
|
+
}
|
|
3733
|
+
if (formType === "data_dictionary") {
|
|
3734
|
+
return value ?? null;
|
|
3735
|
+
}
|
|
3736
|
+
return value;
|
|
551
3737
|
}
|
|
552
|
-
function
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
3738
|
+
function isModuleSelectFormType(formType) {
|
|
3739
|
+
return [
|
|
3740
|
+
"leads",
|
|
3741
|
+
"contacts",
|
|
3742
|
+
"customer",
|
|
3743
|
+
"contract",
|
|
3744
|
+
"receivables",
|
|
3745
|
+
"business",
|
|
3746
|
+
"workorder",
|
|
3747
|
+
"quotation",
|
|
3748
|
+
"visitPlan",
|
|
3749
|
+
"supplier",
|
|
3750
|
+
"warehouse",
|
|
3751
|
+
"invoice"
|
|
3752
|
+
].includes(String(formType));
|
|
3753
|
+
}
|
|
3754
|
+
function getModuleSelectValue(field, value) {
|
|
3755
|
+
if (isEmptyValue(value)) {
|
|
3756
|
+
return "";
|
|
556
3757
|
}
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
3758
|
+
const formType = String(field.formType);
|
|
3759
|
+
if (Array.isArray(value) && value.length > 0 && isPlainObject(value[0])) {
|
|
3760
|
+
if (formType === "invoice" && field.isMulti == 1) {
|
|
3761
|
+
return value.map((item) => item[`${formType}Id`]);
|
|
3762
|
+
}
|
|
3763
|
+
return value[0][`${formType}Id`] ?? value[0].id;
|
|
3764
|
+
}
|
|
3765
|
+
if (isPlainObject(value)) {
|
|
3766
|
+
const record = value;
|
|
3767
|
+
return record[`${formType}Id`] ?? record.id;
|
|
3768
|
+
}
|
|
3769
|
+
if (Array.isArray(value)) {
|
|
3770
|
+
return value[value.length - 1];
|
|
562
3771
|
}
|
|
563
3772
|
return value;
|
|
564
3773
|
}
|
|
565
|
-
function
|
|
566
|
-
|
|
567
|
-
|
|
3774
|
+
function getUserOrStructureValue(formType, value) {
|
|
3775
|
+
let normalized = value ?? [];
|
|
3776
|
+
if (Array.isArray(value) && value.length > 0 && isPlainObject(value[0])) {
|
|
3777
|
+
normalized = value.map((item) => {
|
|
3778
|
+
const record = item;
|
|
3779
|
+
return formType === "structure" ? record.id ?? record.deptId : record.userId ?? record.id;
|
|
3780
|
+
});
|
|
3781
|
+
}
|
|
3782
|
+
else if (isPlainObject(value)) {
|
|
3783
|
+
const record = value;
|
|
3784
|
+
normalized = formType === "structure" ? record.id ?? record.deptId : record.userId ?? record.id;
|
|
568
3785
|
}
|
|
569
|
-
|
|
570
|
-
|
|
3786
|
+
return Array.isArray(normalized) ? normalized.join(",") : normalized;
|
|
3787
|
+
}
|
|
3788
|
+
function getFileBatchId(value) {
|
|
3789
|
+
if (Array.isArray(value) && value.length > 0 && isPlainObject(value[0])) {
|
|
3790
|
+
return value[0].batchId ?? "";
|
|
571
3791
|
}
|
|
572
|
-
if (
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
3792
|
+
if (isPlainObject(value)) {
|
|
3793
|
+
return value.batchId ?? "";
|
|
3794
|
+
}
|
|
3795
|
+
return "";
|
|
3796
|
+
}
|
|
3797
|
+
function getDetailTableValue(field, value) {
|
|
3798
|
+
const fieldExtendList = Array.isArray(field.fieldExtendList) ? field.fieldExtendList : [];
|
|
3799
|
+
const rows = Array.isArray(value) ? value : [];
|
|
3800
|
+
const result = [];
|
|
3801
|
+
for (const row of rows) {
|
|
3802
|
+
if (!isPlainObject(row) || isDetailRowEmpty(fieldExtendList, row)) {
|
|
3803
|
+
continue;
|
|
579
3804
|
}
|
|
580
|
-
|
|
581
|
-
|
|
3805
|
+
const rowRecord = row;
|
|
3806
|
+
const batchId = rowRecord.batchId || randomUUID();
|
|
3807
|
+
const rowFields = [];
|
|
3808
|
+
for (const tableField of fieldExtendList) {
|
|
3809
|
+
if (!isPlainObject(tableField)) {
|
|
3810
|
+
continue;
|
|
3811
|
+
}
|
|
3812
|
+
const copy = { ...tableField };
|
|
3813
|
+
delete copy.companyId;
|
|
3814
|
+
copy.value = getOaExamineRealValue(copy, rowRecord[copy.fieldName]);
|
|
3815
|
+
copy.batchId = batchId;
|
|
3816
|
+
rowFields.push(copy);
|
|
582
3817
|
}
|
|
3818
|
+
result.push(rowFields);
|
|
583
3819
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
3820
|
+
return result;
|
|
3821
|
+
}
|
|
3822
|
+
function isDetailRowEmpty(fields, row) {
|
|
3823
|
+
return fields.every((field) => isEmptyValue(row[field.fieldName]));
|
|
3824
|
+
}
|
|
3825
|
+
function buildOaExamineRelation(args) {
|
|
3826
|
+
if (isPlainObject(args.oaExamineRelation)) {
|
|
3827
|
+
return { ...args.oaExamineRelation };
|
|
587
3828
|
}
|
|
588
|
-
return
|
|
3829
|
+
return cleanObject({
|
|
3830
|
+
customerIds: toCommaSeparated(args.customerIds ?? args.customerId),
|
|
3831
|
+
contactsIds: toCommaSeparated(args.contactsIds ?? args.contactsId),
|
|
3832
|
+
businessIds: toCommaSeparated(args.businessIds ?? args.businessId),
|
|
3833
|
+
contractIds: toCommaSeparated(args.contractIds ?? args.contractId),
|
|
3834
|
+
receivablesIds: toCommaSeparated(args.receivablesIds ?? args.receivablesId),
|
|
3835
|
+
quotationIds: toCommaSeparated(args.quotationIds ?? args.quotationId)
|
|
3836
|
+
});
|
|
589
3837
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
name: item.name,
|
|
598
|
-
expectedValue: item.value,
|
|
599
|
-
actualValue,
|
|
600
|
-
matched: valuesMatch(item.value, actualValue)
|
|
601
|
-
};
|
|
3838
|
+
function findMissingRequiredOaFields(fields, values) {
|
|
3839
|
+
return fields.filter((field) => {
|
|
3840
|
+
if (Number(field.isNull) !== 1 || field.formType === "desc_text") {
|
|
3841
|
+
return false;
|
|
3842
|
+
}
|
|
3843
|
+
const value = values[field.fieldName] ?? values[field.name] ?? values[field.fieldId];
|
|
3844
|
+
return isEmptyValue(value);
|
|
602
3845
|
});
|
|
603
|
-
return {
|
|
604
|
-
endpoint,
|
|
605
|
-
ok: checked.every((item) => item.matched),
|
|
606
|
-
checked
|
|
607
|
-
};
|
|
608
3846
|
}
|
|
609
|
-
function
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
3847
|
+
function normalizeFieldLookupKey(value) {
|
|
3848
|
+
return String(value ?? "").trim().toLowerCase();
|
|
3849
|
+
}
|
|
3850
|
+
function isPlainObject(value) {
|
|
3851
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3852
|
+
}
|
|
3853
|
+
function isEmptyValue(value) {
|
|
3854
|
+
if (value === undefined || value === null || value === "") {
|
|
3855
|
+
return true;
|
|
613
3856
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
3857
|
+
if (Array.isArray(value)) {
|
|
3858
|
+
return value.length === 0;
|
|
3859
|
+
}
|
|
3860
|
+
if (isPlainObject(value)) {
|
|
3861
|
+
return Object.keys(value).length === 0;
|
|
3862
|
+
}
|
|
3863
|
+
return false;
|
|
3864
|
+
}
|
|
3865
|
+
function buildOaExamineAuditBody(args, batch) {
|
|
3866
|
+
const body = objectWriteBody(args);
|
|
3867
|
+
const recordId = args.recordId ?? args.examineRecordId ?? args.id ?? body.recordId;
|
|
3868
|
+
const recordIds = args.recordIds ?? args.examineRecordIds ?? args.ids ?? body.recordIds;
|
|
3869
|
+
if (batch) {
|
|
3870
|
+
if (!Array.isArray(recordIds) || recordIds.length === 0) {
|
|
3871
|
+
throw new Error("批量审批 OA 审批请提供 recordIds、examineRecordIds 或 ids 数组。");
|
|
620
3872
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
for (const item of value) {
|
|
624
|
-
const result = visit(item);
|
|
625
|
-
if (result.found) {
|
|
626
|
-
return result;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return { found: false };
|
|
3873
|
+
if (body.recordIds === undefined) {
|
|
3874
|
+
body.recordIds = recordIds;
|
|
630
3875
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return { found: true, value: extractInformationValue(record) };
|
|
3876
|
+
}
|
|
3877
|
+
else {
|
|
3878
|
+
if (isBlank(recordId)) {
|
|
3879
|
+
throw new Error("审批 OA 审批请提供 recordId、examineRecordId 或 id。");
|
|
636
3880
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (result.found) {
|
|
640
|
-
return result;
|
|
641
|
-
}
|
|
3881
|
+
if (isBlank(body.recordId)) {
|
|
3882
|
+
body.recordId = recordId;
|
|
642
3883
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
return visit(data).value;
|
|
3884
|
+
}
|
|
3885
|
+
return cleanObject(body);
|
|
646
3886
|
}
|
|
647
|
-
function
|
|
648
|
-
|
|
649
|
-
|
|
3887
|
+
function requireOaExamineId(args, message) {
|
|
3888
|
+
const examineId = args.examineId ?? args.id;
|
|
3889
|
+
if (isBlank(examineId)) {
|
|
3890
|
+
throw new Error(message);
|
|
650
3891
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
3892
|
+
return examineId;
|
|
3893
|
+
}
|
|
3894
|
+
function buildPassthroughBody(args, omitKeys = [], defaults = {}) {
|
|
3895
|
+
const omit = new Set(["confirm", ...omitKeys]);
|
|
3896
|
+
const body = { ...defaults };
|
|
3897
|
+
for (const [key, value] of Object.entries(args ?? {})) {
|
|
3898
|
+
if (!omit.has(key) && value !== undefined && value !== null) {
|
|
3899
|
+
body[key] = value;
|
|
3900
|
+
}
|
|
654
3901
|
}
|
|
655
|
-
return
|
|
3902
|
+
return cleanObject(body);
|
|
656
3903
|
}
|
|
657
|
-
function
|
|
658
|
-
|
|
659
|
-
|
|
3904
|
+
function objectWriteBody(args) {
|
|
3905
|
+
const explicitBody = explicitWriteBody(args);
|
|
3906
|
+
return explicitBody && typeof explicitBody === "object" && !Array.isArray(explicitBody)
|
|
3907
|
+
? { ...explicitBody }
|
|
3908
|
+
: {};
|
|
3909
|
+
}
|
|
3910
|
+
function normalizeOaExamineAlias(value) {
|
|
3911
|
+
return String(value ?? "").trim().replace(/[\s._-]+/g, "").toLowerCase();
|
|
3912
|
+
}
|
|
3913
|
+
function previewOaWrite(operation, module, moduleName, targetPath, body) {
|
|
3914
|
+
return {
|
|
3915
|
+
preview: true,
|
|
3916
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
3917
|
+
module,
|
|
3918
|
+
moduleName,
|
|
3919
|
+
operation,
|
|
3920
|
+
targetPath,
|
|
3921
|
+
body
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
function buildCalendarListBody(args) {
|
|
3925
|
+
return cleanObject({
|
|
3926
|
+
startTime: args.startTime ?? args.start,
|
|
3927
|
+
endTime: args.endTime ?? args.end,
|
|
3928
|
+
typeIds: args.typeIds,
|
|
3929
|
+
userId: args.userId
|
|
3930
|
+
});
|
|
3931
|
+
}
|
|
3932
|
+
function buildCalendarDetailBody(args) {
|
|
3933
|
+
return cleanObject({
|
|
3934
|
+
eventId: requireCalendarEventId(args),
|
|
3935
|
+
startTime: args.startTime ?? args.start,
|
|
3936
|
+
endTime: args.endTime ?? args.end
|
|
3937
|
+
});
|
|
3938
|
+
}
|
|
3939
|
+
function buildCalendarEventBody(args, requireEventId) {
|
|
3940
|
+
const event = buildCalendarEvent(args);
|
|
3941
|
+
if (requireEventId && isBlank(event.eventId)) {
|
|
3942
|
+
throw new Error("修改日程请提供 eventId 或 id。");
|
|
660
3943
|
}
|
|
661
|
-
|
|
662
|
-
|
|
3944
|
+
const relation = args.relation !== undefined ? args.relation : buildCalendarRelation(args);
|
|
3945
|
+
const notice = args.notice ?? args.notices ?? [];
|
|
3946
|
+
return cleanObject({
|
|
3947
|
+
event,
|
|
3948
|
+
relation,
|
|
3949
|
+
notice,
|
|
3950
|
+
type: args.type,
|
|
3951
|
+
time: args.time
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
function buildCalendarEvent(args) {
|
|
3955
|
+
const event = args.event && typeof args.event === "object" && !Array.isArray(args.event)
|
|
3956
|
+
? { ...args.event }
|
|
3957
|
+
: cleanObject({
|
|
3958
|
+
eventId: args.eventId ?? args.id,
|
|
3959
|
+
title: args.title,
|
|
3960
|
+
typeId: args.typeId,
|
|
3961
|
+
startTime: args.startTime ?? args.start,
|
|
3962
|
+
endTime: args.endTime ?? args.end,
|
|
3963
|
+
ownerUserIds: args.ownerUserIds ?? args.ownerUserId ?? args.userIds,
|
|
3964
|
+
repetitionType: args.repetitionType,
|
|
3965
|
+
repeatRate: args.repeatRate,
|
|
3966
|
+
repeatTime: args.repeatTime,
|
|
3967
|
+
endType: args.endType,
|
|
3968
|
+
endTypeConfig: args.endTypeConfig,
|
|
3969
|
+
repeatStartTime: args.repeatStartTime,
|
|
3970
|
+
repeatEndTime: args.repeatEndTime,
|
|
3971
|
+
batchId: args.batchId,
|
|
3972
|
+
status: args.status
|
|
3973
|
+
});
|
|
3974
|
+
const directEventId = args.eventId ?? args.id;
|
|
3975
|
+
if (!isBlank(directEventId) && isBlank(event.eventId)) {
|
|
3976
|
+
event.eventId = directEventId;
|
|
663
3977
|
}
|
|
664
|
-
if (
|
|
665
|
-
|
|
3978
|
+
if (event.ownerUserIds !== undefined) {
|
|
3979
|
+
event.ownerUserIds = toCommaSeparated(event.ownerUserIds);
|
|
666
3980
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
function valuesMatch(expected, actual) {
|
|
670
|
-
return normalizeComparableValue(expected) === normalizeComparableValue(actual);
|
|
671
|
-
}
|
|
672
|
-
function normalizeComparableValue(value) {
|
|
673
|
-
if (value === undefined || value === null) {
|
|
674
|
-
return "";
|
|
3981
|
+
if (event.repetitionType === undefined || event.repetitionType === null) {
|
|
3982
|
+
event.repetitionType = 1;
|
|
675
3983
|
}
|
|
676
|
-
if (
|
|
677
|
-
|
|
3984
|
+
if (event.endType === undefined || event.endType === null) {
|
|
3985
|
+
event.endType = 1;
|
|
678
3986
|
}
|
|
679
|
-
return
|
|
3987
|
+
return cleanObject(event);
|
|
680
3988
|
}
|
|
681
|
-
function
|
|
682
|
-
|
|
3989
|
+
function buildCalendarRelation(args) {
|
|
3990
|
+
const relation = cleanObject({
|
|
3991
|
+
customerIds: toCommaSeparated(args.customerIds ?? args.customerId),
|
|
3992
|
+
leadsIds: toCommaSeparated(args.leadsIds ?? args.leadsId),
|
|
3993
|
+
quotationIds: toCommaSeparated(args.quotationIds ?? args.quotationId),
|
|
3994
|
+
receivablesIds: toCommaSeparated(args.receivablesIds ?? args.receivablesId),
|
|
3995
|
+
contactsIds: toCommaSeparated(args.contactsIds ?? args.contactsId),
|
|
3996
|
+
businessIds: toCommaSeparated(args.businessIds ?? args.businessId),
|
|
3997
|
+
contractIds: toCommaSeparated(args.contractIds ?? args.contractId),
|
|
3998
|
+
roomIds: toCommaSeparated(args.roomIds ?? args.roomId)
|
|
3999
|
+
});
|
|
4000
|
+
return Object.keys(relation).length > 0 ? relation : undefined;
|
|
683
4001
|
}
|
|
684
|
-
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (!poolId) {
|
|
693
|
-
throw new Error("客户公海授权列表缺少 poolId,无法统计池内客户数量。");
|
|
694
|
-
}
|
|
695
|
-
const page = await client.post("/crmCustomerPool/queryPageList", cleanObject({
|
|
696
|
-
page: 1,
|
|
697
|
-
limit,
|
|
698
|
-
pageType: 1,
|
|
699
|
-
label: 9,
|
|
700
|
-
poolId,
|
|
701
|
-
searchList
|
|
702
|
-
}));
|
|
703
|
-
const summary = {
|
|
704
|
-
poolId,
|
|
705
|
-
poolName: pool.poolName ?? pool.name,
|
|
706
|
-
customerCount: page.totalRow ?? page.total ?? 0
|
|
707
|
-
};
|
|
708
|
-
if (includeSamples) {
|
|
709
|
-
const list = Array.isArray(page.list) ? page.list : [];
|
|
710
|
-
summary.samples = list.map((record) => ({
|
|
711
|
-
customerId: record.customerId ?? record.id,
|
|
712
|
-
customerName: record.customerName ?? record.name,
|
|
713
|
-
ownerUserName: record.ownerUserName,
|
|
714
|
-
createTime: record.createTime
|
|
715
|
-
}));
|
|
716
|
-
}
|
|
717
|
-
return summary;
|
|
718
|
-
}));
|
|
4002
|
+
function requireCalendarEventId(args) {
|
|
4003
|
+
const eventId = args.eventId ?? args.id;
|
|
4004
|
+
if (isBlank(eventId)) {
|
|
4005
|
+
throw new Error("查询日程详情请提供 eventId 或 id。");
|
|
4006
|
+
}
|
|
4007
|
+
return eventId;
|
|
4008
|
+
}
|
|
4009
|
+
function previewCalendarWrite(operation, targetPath, body) {
|
|
719
4010
|
return {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
4011
|
+
preview: true,
|
|
4012
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
4013
|
+
module: "calendarEvent",
|
|
4014
|
+
moduleName: "日程",
|
|
4015
|
+
operation,
|
|
4016
|
+
targetPath,
|
|
4017
|
+
body
|
|
724
4018
|
};
|
|
725
4019
|
}
|
|
726
|
-
function
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
4020
|
+
function buildOaTaskListBody(args) {
|
|
4021
|
+
return cleanObject({
|
|
4022
|
+
page: args.page,
|
|
4023
|
+
limit: args.limit,
|
|
4024
|
+
type: args.type,
|
|
4025
|
+
status: args.status,
|
|
4026
|
+
priority: args.priority,
|
|
4027
|
+
dueDate: args.dueDate,
|
|
4028
|
+
mold: args.mold,
|
|
4029
|
+
userId: args.userId,
|
|
4030
|
+
mainUserId: args.mainUserId,
|
|
4031
|
+
mainUserIds: args.mainUserIds,
|
|
4032
|
+
search: firstTextValue(args.search, args.keyword, args.query, args.q, args.name)
|
|
4033
|
+
});
|
|
735
4034
|
}
|
|
736
|
-
function
|
|
737
|
-
return
|
|
4035
|
+
function buildRelatedOaTaskQuery(args) {
|
|
4036
|
+
return cleanQuery({
|
|
4037
|
+
type: args.type,
|
|
4038
|
+
typeId: args.typeId ?? args.id
|
|
4039
|
+
});
|
|
738
4040
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
4041
|
+
function buildOaTaskBody(args) {
|
|
4042
|
+
const task = args.task && typeof args.task === "object" && !Array.isArray(args.task)
|
|
4043
|
+
? { ...args.task }
|
|
4044
|
+
: cleanObject({
|
|
4045
|
+
taskId: args.taskId ?? args.id,
|
|
4046
|
+
name: args.name ?? args.title,
|
|
4047
|
+
createUserId: args.createUserId,
|
|
4048
|
+
mainUserId: args.mainUserId,
|
|
4049
|
+
ownerUserId: args.ownerUserId ?? args.ownerUserIds,
|
|
4050
|
+
status: args.status,
|
|
4051
|
+
classId: args.classId,
|
|
4052
|
+
labelId: args.labelId ?? args.labelIds,
|
|
4053
|
+
description: args.description,
|
|
4054
|
+
pid: args.pid,
|
|
4055
|
+
startTime: args.startTime ?? args.start,
|
|
4056
|
+
stopTime: args.stopTime ?? args.endTime ?? args.end,
|
|
4057
|
+
priority: args.priority,
|
|
4058
|
+
workId: args.workId,
|
|
4059
|
+
isTop: args.isTop,
|
|
4060
|
+
isOpen: args.isOpen,
|
|
4061
|
+
orderNum: args.orderNum,
|
|
4062
|
+
topOrderNum: args.topOrderNum,
|
|
4063
|
+
batchId: args.batchId,
|
|
4064
|
+
isArchive: args.isArchive,
|
|
4065
|
+
customerIds: args.customerIds ?? args.customerId,
|
|
4066
|
+
contactsIds: args.contactsIds ?? args.contactsId,
|
|
4067
|
+
businessIds: args.businessIds ?? args.businessId,
|
|
4068
|
+
contractIds: args.contractIds ?? args.contractId
|
|
4069
|
+
});
|
|
4070
|
+
for (const key of ["ownerUserId", "labelId", "customerIds", "contactsIds", "businessIds", "contractIds"]) {
|
|
4071
|
+
if (task[key] !== undefined) {
|
|
4072
|
+
task[key] = toCommaSeparated(task[key]);
|
|
4073
|
+
}
|
|
742
4074
|
}
|
|
743
|
-
|
|
744
|
-
|
|
4075
|
+
return cleanObject(task);
|
|
4076
|
+
}
|
|
4077
|
+
function buildOaTaskUpdateCalls(args) {
|
|
4078
|
+
const taskId = requireOaTaskId(args);
|
|
4079
|
+
const source = args.updates && typeof args.updates === "object" && !Array.isArray(args.updates) ? args.updates : args;
|
|
4080
|
+
const calls = [];
|
|
4081
|
+
const name = firstPresentValue(source.name, source.title);
|
|
4082
|
+
if (name !== undefined) {
|
|
4083
|
+
calls.push({ targetPath: "/workTask/setWorkTaskTitle", body: { taskId, name } });
|
|
745
4084
|
}
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
? poolList.find((pool) => String(pool.poolName ?? pool.name ?? "").trim() === String(args.poolName).trim())
|
|
750
|
-
: poolList[0];
|
|
751
|
-
if (!selectedPool) {
|
|
752
|
-
throw new Error(`当前账号没有可查询的${moduleDefinition.name},请确认公海/线索池权限或传入 poolId。`);
|
|
4085
|
+
const description = firstPresentValue(source.description);
|
|
4086
|
+
if (description !== undefined) {
|
|
4087
|
+
calls.push({ targetPath: "/workTask/setWorkTaskDescription", body: { taskId, description } });
|
|
753
4088
|
}
|
|
754
|
-
const
|
|
755
|
-
if (
|
|
756
|
-
|
|
4089
|
+
const mainUserId = firstPresentValue(source.mainUserId, source.userId);
|
|
4090
|
+
if (mainUserId !== undefined) {
|
|
4091
|
+
calls.push({ targetPath: "/workTask/setWorkTaskMainUser", body: { taskId, userId: mainUserId } });
|
|
757
4092
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const relation = String(args.relation ?? args.type ?? "").trim();
|
|
762
|
-
const pageBody = cleanObject({ page: args.page ?? 1, limit: args.limit ?? 15, pageType: 1, id: args.id, ...safeExtra(args.extra) });
|
|
763
|
-
if (moduleDefinition.key === "business" && relation === "product") {
|
|
764
|
-
return { module: moduleDefinition.key, relation, data: await client.post("/crmBusiness/queryProduct", pageBody) };
|
|
4093
|
+
const ownerUserId = firstPresentValue(source.ownerUserId, source.ownerUserIds);
|
|
4094
|
+
if (ownerUserId !== undefined) {
|
|
4095
|
+
calls.push({ targetPath: "/workTask/setWorkTaskOwnerUser", body: { taskId, ownerUserId: toCommaSeparated(ownerUserId) } });
|
|
765
4096
|
}
|
|
766
|
-
|
|
767
|
-
|
|
4097
|
+
const startTime = firstPresentValue(source.startTime, source.start);
|
|
4098
|
+
const stopTime = firstPresentValue(source.stopTime, source.endTime, source.end);
|
|
4099
|
+
if (startTime !== undefined || stopTime !== undefined) {
|
|
4100
|
+
calls.push({
|
|
4101
|
+
targetPath: "/workTask/setWorkTaskTime",
|
|
4102
|
+
body: cleanObject({ taskId, startTime, stopTime })
|
|
4103
|
+
});
|
|
768
4104
|
}
|
|
769
|
-
|
|
770
|
-
|
|
4105
|
+
const labelId = firstPresentValue(source.labelId, source.labelIds);
|
|
4106
|
+
if (labelId !== undefined) {
|
|
4107
|
+
calls.push({ targetPath: "/workTask/setWorkTaskLabel", body: { taskId, labelId: toCommaSeparated(labelId) } });
|
|
771
4108
|
}
|
|
772
|
-
|
|
773
|
-
|
|
4109
|
+
const priority = firstPresentValue(source.priority);
|
|
4110
|
+
if (priority !== undefined) {
|
|
4111
|
+
calls.push({ targetPath: "/workTask/setWorkTaskPriority", body: { taskId, priority } });
|
|
774
4112
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
relation,
|
|
779
|
-
data: await client.post("/crmContract/queryReceivablesPlansByContractId", undefined, {
|
|
780
|
-
contractId: args.id,
|
|
781
|
-
receivablesId: args.receivablesId,
|
|
782
|
-
productId: args.productId
|
|
783
|
-
})
|
|
784
|
-
};
|
|
4113
|
+
const status = firstPresentValue(source.status);
|
|
4114
|
+
if (status !== undefined) {
|
|
4115
|
+
calls.push({ targetPath: "/workTask/setWorkTaskStatus", body: { taskId, status } });
|
|
785
4116
|
}
|
|
786
|
-
if (
|
|
787
|
-
|
|
4117
|
+
if (calls.length === 0) {
|
|
4118
|
+
throw new Error("请提供要更新的 OA 任务字段:name、description、mainUserId、ownerUserId、startTime/stopTime、labelId、priority 或 status。");
|
|
788
4119
|
}
|
|
789
|
-
|
|
790
|
-
|
|
4120
|
+
return calls;
|
|
4121
|
+
}
|
|
4122
|
+
function requireOaTaskId(args) {
|
|
4123
|
+
const taskId = args.taskId ?? args.id;
|
|
4124
|
+
if (isBlank(taskId)) {
|
|
4125
|
+
throw new Error("更新 OA 任务请提供 taskId 或 id。");
|
|
791
4126
|
}
|
|
792
|
-
|
|
793
|
-
|
|
4127
|
+
return taskId;
|
|
4128
|
+
}
|
|
4129
|
+
function previewOaTaskWrite(operation, targetPath, body) {
|
|
4130
|
+
return {
|
|
4131
|
+
preview: true,
|
|
4132
|
+
message: "写入前预览:未传 confirm=true,MCP 不会调用 72CRM 写接口。",
|
|
4133
|
+
module: "oaTask",
|
|
4134
|
+
moduleName: "OA任务",
|
|
4135
|
+
operation,
|
|
4136
|
+
targetPath,
|
|
4137
|
+
body
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
4140
|
+
function toCommaSeparated(value) {
|
|
4141
|
+
if (Array.isArray(value)) {
|
|
4142
|
+
return value.join(",");
|
|
794
4143
|
}
|
|
795
|
-
|
|
796
|
-
|
|
4144
|
+
return value;
|
|
4145
|
+
}
|
|
4146
|
+
function firstPresentValue(...values) {
|
|
4147
|
+
for (const value of values) {
|
|
4148
|
+
if (value !== undefined && value !== null) {
|
|
4149
|
+
return value;
|
|
4150
|
+
}
|
|
797
4151
|
}
|
|
798
|
-
|
|
4152
|
+
return undefined;
|
|
4153
|
+
}
|
|
4154
|
+
function isBlank(value) {
|
|
4155
|
+
return value === undefined || value === null || String(value).trim() === "";
|
|
799
4156
|
}
|
|
800
4157
|
function previewWrite(args) {
|
|
801
4158
|
const moduleDefinition = args.module ? getModuleByKey(args.module) : undefined;
|