@wukongcrm/mcp-server 0.2.4 → 0.2.5
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 +22 -1
- package/dist/server.js +22 -19
- package/dist/tools.js +165 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -397,13 +397,34 @@ wukong-mcp
|
|
|
397
397
|
{
|
|
398
398
|
"fieldName": "ownerUserId",
|
|
399
399
|
"formType": "user",
|
|
400
|
-
"type":
|
|
400
|
+
"type": 3,
|
|
401
401
|
"values": ["张三"]
|
|
402
402
|
}
|
|
403
403
|
]
|
|
404
404
|
}
|
|
405
405
|
```
|
|
406
406
|
|
|
407
|
+
旧 CRM 的人员筛选只会处理操作 `3`(包含)、`4`(不包含)、`5`(为空)和 `6`(不为空);其中按人员 ID 匹配必须使用 `type=3`。MCP 的人员便捷参数会固定生成操作 `3`,显式传入未实现的人员操作会在请求后端前报错,避免返回未按人员过滤的数据。
|
|
408
|
+
|
|
409
|
+
日期字段必须明确传 `formType=date` 或 `formType=datetime`。固定范围使用操作 `14`,`values` 按开始、结束顺序提供两个值;`date` 使用 `YYYY-MM-DD`,`datetime` 使用 `YYYY-MM-DD HH:mm:ss`。查询完整自然日时,边界为当天 `00:00:00` 和 `23:59:59`,后端范围两端均包含:
|
|
410
|
+
|
|
411
|
+
```json
|
|
412
|
+
{
|
|
413
|
+
"module": "activity",
|
|
414
|
+
"createUserName": "<人员唯一昵称>",
|
|
415
|
+
"filters": [
|
|
416
|
+
{
|
|
417
|
+
"fieldName": "createTime",
|
|
418
|
+
"formType": "datetime",
|
|
419
|
+
"type": 14,
|
|
420
|
+
"values": ["YYYY-MM-DD 00:00:00", "YYYY-MM-DD 23:59:59"]
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
`filters` 和 `searchList` 是同一个高级筛选参数的两个名称,只能选一个。每项必须提供字段名和 `type`/`searchEnum`;两种操作码同时出现时必须相同。除操作 `5`/`6` 外必须提供非空 `values`。日期范围也可传后端已有的单个预设时间值(例如 `today`、`week`、`month`);不支持的操作、无效日期、倒置边界和缺失值会明确报错。
|
|
427
|
+
|
|
407
428
|
如需单独核对用户 ID,可调用 `crm_find_user_id`:
|
|
408
429
|
|
|
409
430
|
```json
|
package/dist/server.js
CHANGED
|
@@ -13,13 +13,16 @@ const idSchema = z.union([z.string(), z.number()]);
|
|
|
13
13
|
const passthroughSchema = z.object({}).passthrough();
|
|
14
14
|
const toolOutputSchema = z.object({ result: z.unknown() });
|
|
15
15
|
const searchFilterSchema = z.object({
|
|
16
|
-
fieldName: z.string().optional(),
|
|
17
|
-
name: z.string().optional(),
|
|
18
|
-
formType: z.string().optional(),
|
|
19
|
-
type: z.number().optional(),
|
|
20
|
-
searchEnum: z.union([
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
fieldName: z.string().optional().describe("后端字段名;与兼容字段 name 至少提供一个。"),
|
|
17
|
+
name: z.string().optional().describe("fieldName 的兼容别名。"),
|
|
18
|
+
formType: z.string().optional().describe("字段类型。人员用 user/single_user;日期用 date,日期时间用 datetime。"),
|
|
19
|
+
type: z.number().int().min(1).max(14).optional().describe("高级筛选操作码;与 searchEnum 同义,同时提供时必须一致。人员 ID 匹配用 3,日期范围用 14。"),
|
|
20
|
+
searchEnum: z.union([
|
|
21
|
+
z.number().int().min(1).max(14),
|
|
22
|
+
z.enum(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"])
|
|
23
|
+
]).optional().describe("高级筛选操作码;与 type 同义,同时提供时必须一致。"),
|
|
24
|
+
values: z.array(z.unknown()).optional().describe("筛选值数组。除为空/不为空操作 5/6 外必须提供;datetime 范围 14 使用 [YYYY-MM-DD HH:mm:ss, YYYY-MM-DD HH:mm:ss]。"),
|
|
25
|
+
groupId: z.number().int().optional().describe("同组条件为 AND,不同组为 OR。")
|
|
23
26
|
}).passthrough();
|
|
24
27
|
const crmCreateRecordSchema = z.object({
|
|
25
28
|
module: moduleSchema,
|
|
@@ -32,16 +35,16 @@ const crmCreateRecordSchema = z.object({
|
|
|
32
35
|
confirm: z.boolean().optional()
|
|
33
36
|
}).strict();
|
|
34
37
|
const crmUserSearchShape = {
|
|
35
|
-
ownerUserId: idSchema.optional(),
|
|
36
|
-
ownerUserIds: z.array(idSchema).optional(),
|
|
37
|
-
ownerUserName: z.string().optional(),
|
|
38
|
-
ownerUserNames: z.array(z.string()).optional(),
|
|
39
|
-
createUserId: idSchema.optional(),
|
|
40
|
-
createUserIds: z.array(idSchema).optional(),
|
|
41
|
-
createUserName: z.string().optional(),
|
|
42
|
-
createUserNames: z.array(z.string()).optional(),
|
|
43
|
-
filters: z.array(searchFilterSchema).optional(),
|
|
44
|
-
searchList: z.array(searchFilterSchema).optional()
|
|
38
|
+
ownerUserId: idSchema.optional().describe("负责人用户 ID;MCP 转为后端支持的人员包含操作 3。"),
|
|
39
|
+
ownerUserIds: z.array(idSchema).optional().describe("多个负责人用户 ID。"),
|
|
40
|
+
ownerUserName: z.string().optional().describe("负责人唯一昵称;MCP 先解析为用户 ID,重名时报错。"),
|
|
41
|
+
ownerUserNames: z.array(z.string()).optional().describe("多个负责人唯一昵称。"),
|
|
42
|
+
createUserId: idSchema.optional().describe("创建人用户 ID;查询跟进记录时表示跟进人员。"),
|
|
43
|
+
createUserIds: z.array(idSchema).optional().describe("多个创建人用户 ID。"),
|
|
44
|
+
createUserName: z.string().optional().describe("创建人唯一昵称;查询跟进记录时表示跟进人员。"),
|
|
45
|
+
createUserNames: z.array(z.string()).optional().describe("多个创建人唯一昵称。"),
|
|
46
|
+
filters: z.array(searchFilterSchema).optional().describe("高级筛选列表;与 searchList 二选一。"),
|
|
47
|
+
searchList: z.array(searchFilterSchema).optional().describe("filters 的兼容别名;不能与 filters 同时提供。")
|
|
45
48
|
};
|
|
46
49
|
const toolDefinitions = [
|
|
47
50
|
["crm_auth_status", "使用 CRM_API_KEY 自动换取 Admin-Token 后检查当前登录态,异常时提示检查 API Key。", z.object({}).optional()],
|
|
@@ -49,7 +52,7 @@ const toolDefinitions = [
|
|
|
49
52
|
["crm_list_customer_pools", "查询当前账号有权限访问的客户公海,并统计每个公海下面有多少客户;只读,不领取、不分配、不流转。", z.object({ includeSamples: z.boolean().optional(), sampleLimit: z.number().optional() }).passthrough()],
|
|
50
53
|
["crm_get_module_schema", "获取模块字段结构和列表头,供本地 AI 判断字段怎么填。", z.object({ module: moduleSchema, id: z.union([z.string(), z.number()]).optional() }).passthrough()],
|
|
51
54
|
["crm_validate_field", "调用固定字段校验接口,校验字段值或联系人查重。", passthroughSchema],
|
|
52
|
-
["crm_search_records", "
|
|
55
|
+
["crm_search_records", "查询模块分页列表。人员便捷参数和 user 筛选会解析为用户 ID,并使用后端支持的操作 3;日期/日期时间范围筛选必须使用操作 14。查询某人员某天的跟进记录时,module=activity,人员使用 createUserId(s)/createUserName(s),createTime 使用 formType=datetime 和当天 00:00:00 至 23:59:59 的范围。", z.object({ module: moduleSchema, page: z.number().optional(), limit: z.number().optional(), ...crmUserSearchShape }).passthrough()],
|
|
53
56
|
["crm_get_record", "按模块和 ID 查询单条 CRM 记录详情。", z.object({ module: moduleSchema, id: z.union([z.string(), z.number()]) }).passthrough()],
|
|
54
57
|
["crm_get_record_information", "查询详情页字段展示数据,包含字段中文名和值。", z.object({ module: moduleSchema, id: z.union([z.string(), z.number()]) }).passthrough()],
|
|
55
58
|
["crm_get_related_records", "查询固定白名单内的关联数据,例如商机产品、联系人商机、合同回款计划、产品上架列表。", z.object({ module: moduleSchema, id: z.union([z.string(), z.number()]).optional(), relation: z.string() }).passthrough()],
|
|
@@ -192,7 +195,7 @@ export function createWukongMcpServer(config = {}) {
|
|
|
192
195
|
const { oauth, ...crmConfig } = config;
|
|
193
196
|
const server = new McpServer({
|
|
194
197
|
name: "wukong-mcp",
|
|
195
|
-
version: "0.2.
|
|
198
|
+
version: "0.2.5"
|
|
196
199
|
}, {
|
|
197
200
|
instructions: "先使用只读工具确认模块、记录和字段,再执行写入。所有写入必须显式传 confirm=true;不要尝试任意 URL 请求。"
|
|
198
201
|
});
|
package/dist/tools.js
CHANGED
|
@@ -1746,6 +1746,10 @@ function extractSavedWorkorderId(data, prepared) {
|
|
|
1746
1746
|
return data?.workorderId ?? prepared.body.entity?.workorderId;
|
|
1747
1747
|
}
|
|
1748
1748
|
const FIELD_SEARCH_CONTAINS = 3;
|
|
1749
|
+
const FIELD_SEARCH_NOT_CONTAINS = 4;
|
|
1750
|
+
const FIELD_SEARCH_IS_NULL = 5;
|
|
1751
|
+
const FIELD_SEARCH_IS_NOT_NULL = 6;
|
|
1752
|
+
const FIELD_SEARCH_RANGE = 14;
|
|
1749
1753
|
const QUICK_SEARCH_FIELDS = {
|
|
1750
1754
|
leads: [
|
|
1751
1755
|
{ fieldName: "leadsName", formType: "text" },
|
|
@@ -1822,6 +1826,42 @@ const NAME_FIELD_BY_MODULE = {
|
|
|
1822
1826
|
};
|
|
1823
1827
|
const USER_FILTER_FORM_TYPES = new Set(["user", "single_user"]);
|
|
1824
1828
|
const USER_FILTER_FIELD_NAMES = new Set(["ownerUserId", "createUserId"]);
|
|
1829
|
+
const DATE_FILTER_FORM_TYPES = new Set(["date", "datetime"]);
|
|
1830
|
+
const NUMBER_RANGE_FORM_TYPES = new Set(["number", "floatnumber", "percent", "field_attention"]);
|
|
1831
|
+
const USER_FILTER_OPERATIONS = new Set([
|
|
1832
|
+
FIELD_SEARCH_CONTAINS,
|
|
1833
|
+
FIELD_SEARCH_NOT_CONTAINS,
|
|
1834
|
+
FIELD_SEARCH_IS_NULL,
|
|
1835
|
+
FIELD_SEARCH_IS_NOT_NULL
|
|
1836
|
+
]);
|
|
1837
|
+
const DATE_FILTER_OPERATIONS = new Set([1, 2, 5, 6, 7, 8, 9, 10, FIELD_SEARCH_RANGE]);
|
|
1838
|
+
const PRESET_DATE_RANGES = new Set([
|
|
1839
|
+
"year",
|
|
1840
|
+
"lastYear",
|
|
1841
|
+
"quarter",
|
|
1842
|
+
"lastQuarter",
|
|
1843
|
+
"month",
|
|
1844
|
+
"lastMonth",
|
|
1845
|
+
"week",
|
|
1846
|
+
"lastWeek",
|
|
1847
|
+
"today",
|
|
1848
|
+
"yesterday",
|
|
1849
|
+
"nextYear",
|
|
1850
|
+
"firstHalfYear",
|
|
1851
|
+
"nextHalfYear",
|
|
1852
|
+
"nextQuarter",
|
|
1853
|
+
"nextMonth",
|
|
1854
|
+
"nextWeek",
|
|
1855
|
+
"tomorrow",
|
|
1856
|
+
"previous7day",
|
|
1857
|
+
"previous30day",
|
|
1858
|
+
"future7day",
|
|
1859
|
+
"future30day",
|
|
1860
|
+
"recently7day",
|
|
1861
|
+
"recently30day",
|
|
1862
|
+
"recently60day",
|
|
1863
|
+
"recentlyHalfYear"
|
|
1864
|
+
]);
|
|
1825
1865
|
const USER_FILTER_ARGUMENTS = [
|
|
1826
1866
|
{
|
|
1827
1867
|
fieldName: "ownerUserId",
|
|
@@ -1837,9 +1877,13 @@ const USER_FILTER_ARGUMENTS = [
|
|
|
1837
1877
|
async function buildResolvedSearchList(client, args, moduleDefinition) {
|
|
1838
1878
|
const searchList = buildSearchList(args, moduleDefinition);
|
|
1839
1879
|
const userSearchList = buildConvenienceUserSearchList(args);
|
|
1840
|
-
|
|
1880
|
+
const validatedSearchList = validateSearchFilters(combineSearchLists(searchList, userSearchList));
|
|
1881
|
+
return resolveUserSearchFilters(client, validatedSearchList, args.companyId);
|
|
1841
1882
|
}
|
|
1842
1883
|
function buildSearchList(args, moduleDefinition) {
|
|
1884
|
+
if (args.filters !== undefined && args.searchList !== undefined) {
|
|
1885
|
+
throw new Error("filters 和 searchList 不能同时提供,请只使用其中一个高级筛选参数。");
|
|
1886
|
+
}
|
|
1843
1887
|
const explicitSearchList = toArray(args.filters ?? args.searchList);
|
|
1844
1888
|
const semanticSearchList = buildSemanticSearchList(args, moduleDefinition);
|
|
1845
1889
|
return combineSearchLists(explicitSearchList, semanticSearchList);
|
|
@@ -1853,12 +1897,126 @@ function buildConvenienceUserSearchList(args) {
|
|
|
1853
1897
|
return [{
|
|
1854
1898
|
fieldName,
|
|
1855
1899
|
formType: "user",
|
|
1856
|
-
|
|
1857
|
-
|
|
1900
|
+
// 旧 CRM 的 EsUtil.userSearch 只有 CONTAINS(3) 才会生成用户 ID terms 查询。
|
|
1901
|
+
type: FIELD_SEARCH_CONTAINS,
|
|
1902
|
+
searchEnum: FIELD_SEARCH_CONTAINS,
|
|
1858
1903
|
values
|
|
1859
1904
|
}];
|
|
1860
1905
|
});
|
|
1861
1906
|
}
|
|
1907
|
+
function validateSearchFilters(filters) {
|
|
1908
|
+
return filters.map((filter, index) => {
|
|
1909
|
+
if (!isPlainObject(filter)) {
|
|
1910
|
+
throw new Error(`高级筛选第 ${index + 1} 项必须是对象。`);
|
|
1911
|
+
}
|
|
1912
|
+
const fieldName = firstTextValue(filter.fieldName, filter.name);
|
|
1913
|
+
if (!fieldName) {
|
|
1914
|
+
throw new Error(`高级筛选第 ${index + 1} 项请提供 fieldName(或兼容字段 name)。`);
|
|
1915
|
+
}
|
|
1916
|
+
const typeOperation = parseSearchOperation(filter.type, "type", fieldName);
|
|
1917
|
+
const enumOperation = parseSearchOperation(filter.searchEnum, "searchEnum", fieldName);
|
|
1918
|
+
if (typeOperation !== undefined && enumOperation !== undefined && typeOperation !== enumOperation) {
|
|
1919
|
+
throw new Error(`筛选“${fieldName}”的 type=${typeOperation} 与 searchEnum=${enumOperation} 冲突,请保持一致或只提供一个。`);
|
|
1920
|
+
}
|
|
1921
|
+
const operation = enumOperation ?? typeOperation;
|
|
1922
|
+
if (operation === undefined) {
|
|
1923
|
+
throw new Error(`筛选“${fieldName}”请提供 type 或 searchEnum。`);
|
|
1924
|
+
}
|
|
1925
|
+
if (filter.values !== undefined && !Array.isArray(filter.values)) {
|
|
1926
|
+
throw new Error(`筛选“${fieldName}”的 values 必须是数组。`);
|
|
1927
|
+
}
|
|
1928
|
+
const values = valueArray(filter.values);
|
|
1929
|
+
const isNullOperation = operation === FIELD_SEARCH_IS_NULL || operation === FIELD_SEARCH_IS_NOT_NULL;
|
|
1930
|
+
if (!isNullOperation && values.length === 0) {
|
|
1931
|
+
throw new Error(`筛选“${fieldName}”使用操作 ${operation} 时请提供 values。`);
|
|
1932
|
+
}
|
|
1933
|
+
const formType = String(filter.formType ?? "").trim().toLowerCase();
|
|
1934
|
+
const userFilter = USER_FILTER_FORM_TYPES.has(formType) || USER_FILTER_FIELD_NAMES.has(fieldName);
|
|
1935
|
+
if (userFilter && !USER_FILTER_OPERATIONS.has(operation)) {
|
|
1936
|
+
throw new Error(`人员筛选“${fieldName}”使用了后端未实现的操作 ${operation};仅支持 3、4、5、6。按人员 ID 匹配请使用操作 3。`);
|
|
1937
|
+
}
|
|
1938
|
+
if (DATE_FILTER_FORM_TYPES.has(formType)) {
|
|
1939
|
+
validateDateFilter(fieldName, formType, operation, values);
|
|
1940
|
+
}
|
|
1941
|
+
else if (operation === FIELD_SEARCH_RANGE) {
|
|
1942
|
+
if (!NUMBER_RANGE_FORM_TYPES.has(formType)) {
|
|
1943
|
+
throw new Error(`筛选“${fieldName}”的范围操作 14 仅支持 date、datetime 或数字类型 formType。`);
|
|
1944
|
+
}
|
|
1945
|
+
if (values.length !== 2 || values.some((value) => !isFiniteNumberValue(value))) {
|
|
1946
|
+
throw new Error(`数字筛选“${fieldName}”的范围操作 14 必须提供两个有效数字边界值。`);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
// formType 大小写错误会让后端退回文本搜索;这里统一为契约中的小写值。
|
|
1950
|
+
const normalizedFilter = formType ? { ...filter, formType } : filter;
|
|
1951
|
+
return DATE_FILTER_FORM_TYPES.has(formType) && values.length > 0
|
|
1952
|
+
? { ...normalizedFilter, values: values.map((value) => String(value).trim()) }
|
|
1953
|
+
: normalizedFilter;
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
function parseSearchOperation(value, key, fieldName) {
|
|
1957
|
+
if (value === undefined || value === null) {
|
|
1958
|
+
return undefined;
|
|
1959
|
+
}
|
|
1960
|
+
const text = String(value).trim();
|
|
1961
|
+
if (!/^\d+$/.test(text)) {
|
|
1962
|
+
throw new Error(`筛选“${fieldName}”的 ${key} 必须是 1 到 14 的整数操作码。`);
|
|
1963
|
+
}
|
|
1964
|
+
const operation = Number(text);
|
|
1965
|
+
if (!Number.isInteger(operation) || operation < 1 || operation > 14) {
|
|
1966
|
+
throw new Error(`筛选“${fieldName}”的 ${key}=${text} 不受支持;操作码范围为 1 到 14。`);
|
|
1967
|
+
}
|
|
1968
|
+
return operation;
|
|
1969
|
+
}
|
|
1970
|
+
function validateDateFilter(fieldName, formType, operation, values) {
|
|
1971
|
+
if (!DATE_FILTER_OPERATIONS.has(operation)) {
|
|
1972
|
+
throw new Error(`日期筛选“${fieldName}”的操作 ${operation} 不受后端支持;范围筛选请使用操作 14。`);
|
|
1973
|
+
}
|
|
1974
|
+
if (operation === FIELD_SEARCH_IS_NULL || operation === FIELD_SEARCH_IS_NOT_NULL) {
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
if (operation === FIELD_SEARCH_RANGE && values.length === 1) {
|
|
1978
|
+
const preset = typeof values[0] === "string" ? values[0].trim() : "";
|
|
1979
|
+
if (PRESET_DATE_RANGES.has(preset)) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
throw new Error(`日期筛选“${fieldName}”的范围操作 14 请提供两个边界值,或单个受支持的预设时间值。`);
|
|
1983
|
+
}
|
|
1984
|
+
const expectedValueCount = operation === FIELD_SEARCH_RANGE ? 2 : 1;
|
|
1985
|
+
if (values.length !== expectedValueCount) {
|
|
1986
|
+
throw new Error(`日期筛选“${fieldName}”的操作 ${operation} 必须提供 ${expectedValueCount} 个时间值。`);
|
|
1987
|
+
}
|
|
1988
|
+
const normalizedValues = values.map((value) => typeof value === "string" ? value.trim() : "");
|
|
1989
|
+
if (normalizedValues.some((value) => !isValidDateValue(value, formType))) {
|
|
1990
|
+
const format = formType === "datetime" ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
|
|
1991
|
+
throw new Error(`日期筛选“${fieldName}”的 values 必须使用 ${format} 格式,并且是有效日期。`);
|
|
1992
|
+
}
|
|
1993
|
+
if (operation === FIELD_SEARCH_RANGE && normalizedValues[0] > normalizedValues[1]) {
|
|
1994
|
+
throw new Error(`日期筛选“${fieldName}”的开始值不能晚于结束值。`);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
function isValidDateValue(value, formType) {
|
|
1998
|
+
const match = formType === "datetime"
|
|
1999
|
+
? /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/.exec(value)
|
|
2000
|
+
: /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
2001
|
+
if (!match) {
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
const [, yearText, monthText, dayText, hourText = "0", minuteText = "0", secondText = "0"] = match;
|
|
2005
|
+
const year = Number(yearText);
|
|
2006
|
+
const month = Number(monthText);
|
|
2007
|
+
const day = Number(dayText);
|
|
2008
|
+
const hour = Number(hourText);
|
|
2009
|
+
const minute = Number(minuteText);
|
|
2010
|
+
const second = Number(secondText);
|
|
2011
|
+
const maxDay = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;
|
|
2012
|
+
return day >= 1 && day <= maxDay && hour <= 23 && minute <= 59 && second <= 59;
|
|
2013
|
+
}
|
|
2014
|
+
function isFiniteNumberValue(value) {
|
|
2015
|
+
if (typeof value === "number") {
|
|
2016
|
+
return Number.isFinite(value);
|
|
2017
|
+
}
|
|
2018
|
+
return typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value));
|
|
2019
|
+
}
|
|
1862
2020
|
function argumentValues(args, keys) {
|
|
1863
2021
|
return keys.flatMap((key) => valueArray(args[key]));
|
|
1864
2022
|
}
|
|
@@ -1922,7 +2080,9 @@ async function resolveUserSearchFilters(client, searchList, companyId) {
|
|
|
1922
2080
|
}
|
|
1923
2081
|
resolvedFilters.push({
|
|
1924
2082
|
...filter,
|
|
1925
|
-
formType: filter.formType ?? "
|
|
2083
|
+
formType: USER_FILTER_FORM_TYPES.has(String(filter.formType ?? "").trim().toLowerCase())
|
|
2084
|
+
? String(filter.formType).trim().toLowerCase()
|
|
2085
|
+
: "user",
|
|
1926
2086
|
values: [...new Set(values)]
|
|
1927
2087
|
});
|
|
1928
2088
|
}
|
|
@@ -2051,7 +2211,7 @@ function toArray(value) {
|
|
|
2051
2211
|
return [];
|
|
2052
2212
|
}
|
|
2053
2213
|
async function resolveUniqueRecord(client, moduleDefinition, args) {
|
|
2054
|
-
const searchList = await resolveUserSearchFilters(client, buildSearchList(args, moduleDefinition), args.companyId);
|
|
2214
|
+
const searchList = await resolveUserSearchFilters(client, validateSearchFilters(buildSearchList(args, moduleDefinition)), args.companyId);
|
|
2055
2215
|
if (searchList.length === 0) {
|
|
2056
2216
|
throw new Error(`请提供可定位${moduleDefinition.name}的 customerId/id、phone/mobile、name/customerName 或 keyword。`);
|
|
2057
2217
|
}
|