weaver-work-cli 0.1.5 → 0.1.7
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 +14 -9
- package/dist/cmd/skills/index.js +60 -0
- package/dist/internal/e10/auth/commands.js +48 -32
- package/dist/internal/e10/auth/session.js +44 -3
- package/dist/internal/e10/auth/xiaoe.js +363 -11
- package/dist/internal/e10/context.js +4 -2
- package/dist/internal/skills/detector.js +42 -0
- package/dist/internal/skills/install.js +50 -3
- package/dist/shortcuts/archive/render/search-format.js +12 -3
- package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
- package/dist/shortcuts/ebuilder-form/errors.js +54 -0
- package/dist/shortcuts/ebuilder-form/host.js +1238 -0
- package/dist/shortcuts/ebuilder-form/index.js +108 -0
- package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
- package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
- package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
- package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
- package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
- package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
- package/dist/shortcuts/ebuilder-form/operations.js +8 -0
- package/dist/shortcuts/ehr/operations/salary.js +2 -140
- package/dist/shortcuts/im/continuation.js +70 -0
- package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
- package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
- package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
- package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
- package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
- package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
- package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
- package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
- package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
- package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
- package/dist/shortcuts/im/operations/shared.js +442 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
- package/dist/shortcuts/im/operations/user-remind.js +201 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
- package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
- package/dist/shortcuts/index.js +10 -2
- package/dist/shortcuts/invoice/manifest.js +113 -10
- package/dist/shortcuts/invoice/operations/import.js +39 -14
- package/dist/shortcuts/invoice/operations/reim.js +38 -18
- package/dist/shortcuts/invoice/operations/shared.js +35 -6
- package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
- package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
- package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
- package/dist/shortcuts/okr/errors.js +70 -0
- package/dist/shortcuts/okr/host.js +169 -0
- package/dist/shortcuts/okr/index.js +155 -0
- package/dist/shortcuts/okr/manifest.js +70 -0
- package/dist/shortcuts/okr/operations/link.js +305 -0
- package/dist/shortcuts/okr/operations/read.js +481 -0
- package/dist/shortcuts/okr/operations/registry.js +31 -0
- package/dist/shortcuts/okr/operations/shared.js +381 -0
- package/dist/shortcuts/okr/operations/types.js +88 -0
- package/dist/shortcuts/okr/operations/write.js +838 -0
- package/dist/shortcuts/okr/operations.js +8 -0
- package/dist/shortcuts/project/continuation.js +70 -0
- package/dist/shortcuts/project/errors.js +53 -0
- package/dist/shortcuts/project/host.js +160 -0
- package/dist/shortcuts/project/index.js +106 -0
- package/dist/shortcuts/project/manifest.js +36 -0
- package/dist/shortcuts/project/operations/read.js +551 -0
- package/dist/shortcuts/project/operations/registry.js +31 -0
- package/dist/shortcuts/project/operations/shared.js +68 -0
- package/dist/shortcuts/project/operations/types.js +39 -0
- package/dist/shortcuts/project/operations/write.js +266 -0
- package/dist/shortcuts/project/operations.js +8 -0
- package/dist/shortcuts/workflow/continuation.js +508 -0
- package/dist/shortcuts/workflow/endpoints.js +149 -0
- package/dist/shortcuts/workflow/errors.js +112 -0
- package/dist/shortcuts/workflow/host.js +224 -0
- package/dist/shortcuts/workflow/index.js +116 -0
- package/dist/shortcuts/workflow/manifest.js +30 -0
- package/dist/shortcuts/workflow/operations/form.js +4159 -0
- package/dist/shortcuts/workflow/operations/read.js +3778 -0
- package/dist/shortcuts/workflow/operations/registry.js +33 -0
- package/dist/shortcuts/workflow/operations/shared.js +167 -0
- package/dist/shortcuts/workflow/operations/types.js +44 -0
- package/dist/shortcuts/workflow/operations/write.js +1991 -0
- package/dist/shortcuts/workflow/operations.js +119 -0
- package/dist/shortcuts/workflow/state.js +182 -0
- package/docs/SKILL.md +5 -3
- package/docs/_catalog.md +10 -1
- package/docs/agent-invoice.md +2 -2
- package/docs/agent-skill-install.md +2 -2
- package/docs/e10-auth.md +3 -1
- package/docs/ebuilder-form.md +38 -0
- package/docs/im.md +104 -0
- package/docs/invoice.md +5 -5
- package/docs/okr.md +122 -0
- package/docs/operation-manual.md +10 -7
- package/docs/project.md +69 -0
- package/docs/skill-review-2026-09-19.md +68 -0
- package/package.json +3 -2
- package/scripts/package-skill.mjs +38 -11
- package/scripts/sync-connector-skills.mjs +172 -0
- package/skill-template/business-info.json +357 -16
- package/skill-template/domains/ebuilder-form.md +5 -0
- package/skill-template/domains/okr.md +18 -0
- package/skill-template/domains/shijingran.md +3 -0
- package/skill-template/domains/skill-maker.md +4 -4
- package/skill-template/domains/workflow.md +22 -0
- package/skill-template/domains/yepiaotong.md +4 -2
- package/skill-template/domains/yimiaoban.md +13 -10
- package/skill-template/product.json +5 -0
- package/skill-template/skill-template.md +1 -0
- package/skills/weaver-e10-calendar/SKILL.md +5 -1
- package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
- package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
- package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
- package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
- package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
- package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
- package/skills/weaver-e10-esb/SKILL.md +1 -1
- package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
- package/skills/weaver-e10-hrm/SKILL.md +1 -1
- package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
- package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
- package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
- package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
- package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
- package/skills/weaver-e10-jucailin/SKILL.md +2 -2
- package/skills/weaver-e10-mail/SKILL.md +22 -6
- package/skills/weaver-e10-meeting/SKILL.md +22 -1
- package/skills/weaver-e10-okr/SKILL.md +139 -0
- package/skills/weaver-e10-okr/references/okr-align.md +59 -0
- package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
- package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
- package/skills/weaver-e10-okr/references/okr-query.md +105 -0
- package/skills/weaver-e10-okr/references/okr-write.md +94 -0
- package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
- package/skills/weaver-e10-plan/SKILL.md +3 -3
- package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
- package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
- package/skills/weaver-e10-shared/SKILL.md +79 -11
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
- package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
- package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
- package/skills/weaver-e10-shijingran/SKILL.md +83 -0
- package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
- package/skills/weaver-e10-shijingran/references/operations.md +154 -0
- package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
- package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
- package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
- package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
- package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
- package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
- package/skills/weaver-e10-workflow/SKILL.md +250 -0
- package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
- package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
- package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
- package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
- package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
- package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
- package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
- package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
- package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
- package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
- package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
- package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
- package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
- package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
- package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
- package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
- package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
- package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
- package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
- package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
- package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
- package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
- package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
- package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
- package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
- package/docs/yimiaoban.md +0 -86
- package/skills/weaver-e10-calendar/product.json +0 -8
- package/skills/weaver-e10-esb/product.json +0 -8
- package/skills/weaver-e10-hrm/product.json +0 -8
- package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
- package/skills/weaver-e10-jucailin/product.json +0 -8
- package/skills/weaver-e10-mail/product.json +0 -8
- package/skills/weaver-e10-meeting/product.json +0 -8
- package/skills/weaver-e10-plan/product.json +0 -8
- package/skills/weaver-e10-qiyecheng/product.json +0 -8
- package/skills/weaver-e10-skill-maker/product.json +0 -8
- package/skills/weaver-e10-wenshuding/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
- package/skills/weaver-e10-yimiaoban/product.json +0 -8
- package/skills/weaver-e10-ziguanjia/product.json +0 -8
- /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import { assertDatajsonSuccess, assertWeaverSuccess, } from '../host.js';
|
|
2
|
+
import { SCHEMA_FILTERS, SCHEMA_FIELDS, SCHEMA_ID_STRING, SCHEMA_PAGE_NO, SCHEMA_PAGE_SIZE, projectSuccess, schemaOf, } from './types.js';
|
|
3
|
+
import { compactFilters, idString, operatorId, positiveInt, queryText, } from './shared.js';
|
|
4
|
+
const APP_TAG = 'weaver-project-eb';
|
|
5
|
+
const DEFAULT_OBJ_IDS = { task: '1020270486210527232', project: '1020275562853482532' };
|
|
6
|
+
const MIN_VERSION = [20, 6, 11];
|
|
7
|
+
function buildCardUrl(baseUrl, objId, dataId) {
|
|
8
|
+
if (!baseUrl || !objId || !dataId || /[\s/\\?#]/u.test(dataId))
|
|
9
|
+
return null;
|
|
10
|
+
return `${baseUrl.replace(/\/+$/u, '')}/sp/ebdfpage/card/0/${objId}/${dataId}`;
|
|
11
|
+
}
|
|
12
|
+
const CURRENT_USER_FILTERS = new Set([
|
|
13
|
+
'manager',
|
|
14
|
+
'participant',
|
|
15
|
+
'participants',
|
|
16
|
+
'tester',
|
|
17
|
+
'customer_manager',
|
|
18
|
+
'acceptance_manager',
|
|
19
|
+
]);
|
|
20
|
+
const SCHEMA_OUTPUT_FIELDS = {
|
|
21
|
+
type: 'array',
|
|
22
|
+
minItems: 1,
|
|
23
|
+
maxItems: 50,
|
|
24
|
+
uniqueItems: true,
|
|
25
|
+
items: { type: 'string', minLength: 1, maxLength: 100, pattern: '^[A-Za-z][A-Za-z0-9_]*' },
|
|
26
|
+
description: '可选;只保留指定 mainTable 字段。id 和记录名称字段始终保留;fields 不含 detail_url 时不会解析详情链接。',
|
|
27
|
+
};
|
|
28
|
+
async function resolveCurrentUserFilters(filters, host) {
|
|
29
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
30
|
+
if (value === '@me' && CURRENT_USER_FILTERS.has(key)) {
|
|
31
|
+
filters[key] = await operatorId(host);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function outputFields(value) {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
return null;
|
|
38
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
39
|
+
throw new Error('fields 必须是非空字符串数组');
|
|
40
|
+
}
|
|
41
|
+
const fields = [];
|
|
42
|
+
for (const item of value) {
|
|
43
|
+
if (typeof item !== 'string' || !/^[A-Za-z]/.test(item) || /[^A-Za-z0-9_]/.test(item)) {
|
|
44
|
+
throw new Error('fields 包含非法字段名');
|
|
45
|
+
}
|
|
46
|
+
fields.push(item);
|
|
47
|
+
}
|
|
48
|
+
return [...new Set(fields)];
|
|
49
|
+
}
|
|
50
|
+
function compactDatajson(datajson, fields, requiredFields) {
|
|
51
|
+
if (!fields)
|
|
52
|
+
return datajson;
|
|
53
|
+
const keep = new Set([...fields, 'id', 'detail_url', ...requiredFields]);
|
|
54
|
+
const value = datajson;
|
|
55
|
+
const rows = Array.isArray(value?.datas) ? value.datas : [];
|
|
56
|
+
for (const row of rows) {
|
|
57
|
+
const mainTable = row?.mainTable;
|
|
58
|
+
if (!mainTable || typeof mainTable !== 'object' || Array.isArray(mainTable))
|
|
59
|
+
continue;
|
|
60
|
+
row.mainTable = Object.fromEntries(Object.entries(mainTable).filter(([key]) => keep.has(key)));
|
|
61
|
+
}
|
|
62
|
+
return datajson;
|
|
63
|
+
}
|
|
64
|
+
function normalizeFieldNames(value, inputName) {
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
return [];
|
|
67
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
68
|
+
throw new Error(`${inputName} 必须是非空字符串数组`);
|
|
69
|
+
}
|
|
70
|
+
const names = [];
|
|
71
|
+
for (const item of value) {
|
|
72
|
+
if (typeof item !== 'string' || item.trim() === '' || item.length > 100) {
|
|
73
|
+
throw new Error(`${inputName} 包含非法字段名`);
|
|
74
|
+
}
|
|
75
|
+
names.push(item);
|
|
76
|
+
}
|
|
77
|
+
return [...new Set(names)];
|
|
78
|
+
}
|
|
79
|
+
function filterFormFieldResponse(response, fieldNames, fieldKeyword) {
|
|
80
|
+
if (fieldNames.length === 0 && !fieldKeyword)
|
|
81
|
+
return response;
|
|
82
|
+
if (!response || typeof response !== 'object')
|
|
83
|
+
return response;
|
|
84
|
+
const groups = response?.data?.data;
|
|
85
|
+
if (!Array.isArray(groups))
|
|
86
|
+
return response;
|
|
87
|
+
const keyword = fieldKeyword?.toLowerCase() ?? null;
|
|
88
|
+
const nextGroups = groups.map((group) => {
|
|
89
|
+
if (!group || typeof group !== 'object' || !Array.isArray(group.fields))
|
|
90
|
+
return group;
|
|
91
|
+
return {
|
|
92
|
+
...group,
|
|
93
|
+
fields: group.fields.filter((field) => {
|
|
94
|
+
if (!field || typeof field !== 'object')
|
|
95
|
+
return false;
|
|
96
|
+
const name = typeof field.name === 'string' ? field.name : '';
|
|
97
|
+
const text = typeof field.text === 'string' ? field.text : '';
|
|
98
|
+
if (fieldNames.length > 0)
|
|
99
|
+
return fieldNames.includes(name);
|
|
100
|
+
return keyword ? name.toLowerCase().includes(keyword) || text.toLowerCase().includes(keyword) : true;
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
return { ...response, data: { ...response.data, data: nextGroups } };
|
|
105
|
+
}
|
|
106
|
+
function operationTable(name) {
|
|
107
|
+
return name === 'project.project.list' || name === 'project.project.detail' ? 'project' : 'task';
|
|
108
|
+
}
|
|
109
|
+
async function decorateDetailUrls(host, operation, datajson) {
|
|
110
|
+
const value = datajson;
|
|
111
|
+
const rows = Array.isArray(value?.datas) ? value.datas : [];
|
|
112
|
+
const targets = rows.map((row) => row?.mainTable).filter((row) => row?.id !== undefined && row?.id !== null);
|
|
113
|
+
if (targets.length === 0)
|
|
114
|
+
return datajson;
|
|
115
|
+
try {
|
|
116
|
+
const objId = await getObject(host, operationTable(operation));
|
|
117
|
+
const session = await host.getSession();
|
|
118
|
+
for (const row of targets) {
|
|
119
|
+
const url = buildCardUrl(session.baseUrl, objId, String(row.id));
|
|
120
|
+
if (url)
|
|
121
|
+
row.detail_url = url;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (value && typeof value === 'object') {
|
|
126
|
+
value.detail_url_warning = `详情链接暂不可用:${error instanceof Error ? error.message : String(error)};主查询已成功,不要因此重试。`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return datajson;
|
|
130
|
+
}
|
|
131
|
+
function table(value) {
|
|
132
|
+
if (value !== 'task' && value !== 'project') {
|
|
133
|
+
throw new Error('table must be task or project');
|
|
134
|
+
}
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
function versionAtLeast(version) {
|
|
138
|
+
const parts = version.split('.').map((item) => Number(item) || 0);
|
|
139
|
+
for (let index = 0; index < Math.max(parts.length, MIN_VERSION.length); index += 1) {
|
|
140
|
+
const left = parts[index] ?? 0;
|
|
141
|
+
const right = MIN_VERSION[index] ?? 0;
|
|
142
|
+
if (left > right)
|
|
143
|
+
return true;
|
|
144
|
+
if (left < right)
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
async function getAppId(host) {
|
|
150
|
+
const response = await host.request({
|
|
151
|
+
method: 'GET',
|
|
152
|
+
path: '/api/bs/ebuilder/app/tags/getAppIds',
|
|
153
|
+
query: { tag: APP_TAG },
|
|
154
|
+
operation: 'project.app.ids',
|
|
155
|
+
});
|
|
156
|
+
assertWeaverSuccess(response, 'project.app.ids');
|
|
157
|
+
const data = response?.data;
|
|
158
|
+
const appId = Array.isArray(data) && data.length > 0 ? String(data[0]) : '';
|
|
159
|
+
if (!appId)
|
|
160
|
+
throw new Error('当前环境未安装事井然项目管理应用');
|
|
161
|
+
return appId;
|
|
162
|
+
}
|
|
163
|
+
async function getObject(host, value) {
|
|
164
|
+
const response = await host.request({
|
|
165
|
+
method: 'GET',
|
|
166
|
+
path: '/api/ebproject/backendConsole/getCurrentObjId',
|
|
167
|
+
query: { objId: DEFAULT_OBJ_IDS[value] },
|
|
168
|
+
operation: 'project.object.id',
|
|
169
|
+
});
|
|
170
|
+
assertWeaverSuccess(response, 'project.object.id');
|
|
171
|
+
const object = String(response?.data ?? '');
|
|
172
|
+
if (!object)
|
|
173
|
+
throw new Error('接口未返回当前环境对象 ID');
|
|
174
|
+
return object;
|
|
175
|
+
}
|
|
176
|
+
async function datajsonRequest(host, operation, path, payload, query) {
|
|
177
|
+
const response = await host.request({
|
|
178
|
+
method: 'POST',
|
|
179
|
+
path,
|
|
180
|
+
query,
|
|
181
|
+
body: { datajson: JSON.stringify(payload) },
|
|
182
|
+
operation,
|
|
183
|
+
});
|
|
184
|
+
assertDatajsonSuccess(response, operation);
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
function listOperation(name, path, nameField, descField, statusField) {
|
|
188
|
+
return {
|
|
189
|
+
name,
|
|
190
|
+
resource: 'project',
|
|
191
|
+
method: 'list',
|
|
192
|
+
action: name,
|
|
193
|
+
summary: '分页查询项目/任务列表,默认第 1 页 10 条,未指定状态时仅查询未开始和进行中',
|
|
194
|
+
risk: 'read',
|
|
195
|
+
inputSchema: schemaOf({
|
|
196
|
+
filters: SCHEMA_FILTERS,
|
|
197
|
+
pageNo: SCHEMA_PAGE_NO,
|
|
198
|
+
pageSize: SCHEMA_PAGE_SIZE,
|
|
199
|
+
name: { type: 'string', description: `${nameField} 模糊搜索,通过 URL Query 传递` },
|
|
200
|
+
description: { type: 'string', description: `${descField} 模糊搜索,通过 URL Query 传递` },
|
|
201
|
+
allStatuses: { type: 'boolean', default: false, description: '为 true 时不传系统状态和业务状态,查询全部状态;不能与业务状态同时传' },
|
|
202
|
+
withDetailUrl: { type: 'boolean', default: true, description: '是否为每条记录补充 detail_url;传 false 可跳过链接解析' },
|
|
203
|
+
fields: SCHEMA_OUTPUT_FIELDS,
|
|
204
|
+
}),
|
|
205
|
+
handler: async (args, { host }) => {
|
|
206
|
+
const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
|
|
207
|
+
const pageSize = positiveInt(args.pageSize, 'pageSize', 10);
|
|
208
|
+
const filters = compactFilters(args.filters);
|
|
209
|
+
await resolveCurrentUserFilters(filters, host);
|
|
210
|
+
const allStatuses = args.allStatuses === true;
|
|
211
|
+
if (allStatuses && (filters.sys_status !== undefined || filters[statusField] !== undefined)) {
|
|
212
|
+
throw new Error("allStatuses 不能与 sys_status 或业务状态同时传");
|
|
213
|
+
}
|
|
214
|
+
if (allStatuses) {
|
|
215
|
+
delete filters.sys_status;
|
|
216
|
+
}
|
|
217
|
+
else if (filters[statusField] === undefined && filters.sys_status === undefined) {
|
|
218
|
+
filters.sys_status = 'todo,doing';
|
|
219
|
+
}
|
|
220
|
+
else if (filters[statusField] !== undefined) {
|
|
221
|
+
delete filters.sys_status;
|
|
222
|
+
}
|
|
223
|
+
const response = await datajsonRequest(host, name, path, {
|
|
224
|
+
mainTable: filters,
|
|
225
|
+
pageInfo: { pageNo, pageSize },
|
|
226
|
+
header: { operator: await operatorId(host) },
|
|
227
|
+
}, {
|
|
228
|
+
[nameField]: queryText(args.name, 'name'),
|
|
229
|
+
[descField]: queryText(args.description, 'description'),
|
|
230
|
+
});
|
|
231
|
+
const fields = outputFields(args.fields);
|
|
232
|
+
const shouldDecorate = args.withDetailUrl !== false && (!fields || fields.includes('detail_url'));
|
|
233
|
+
const decorated = shouldDecorate
|
|
234
|
+
? await decorateDetailUrls(host, name, response?.datajson ?? null)
|
|
235
|
+
: response?.datajson ?? null;
|
|
236
|
+
const datajson = compactDatajson(decorated, fields, [nameField]);
|
|
237
|
+
return projectSuccess(name, datajson, { pageNo, pageSize });
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function detailOperation(name, path, nameField) {
|
|
242
|
+
return {
|
|
243
|
+
name,
|
|
244
|
+
resource: 'project',
|
|
245
|
+
method: 'detail',
|
|
246
|
+
action: name,
|
|
247
|
+
summary: '按主键查询单条项目/任务详情',
|
|
248
|
+
risk: 'read',
|
|
249
|
+
inputSchema: schemaOf({
|
|
250
|
+
id: SCHEMA_ID_STRING,
|
|
251
|
+
withDetailUrl: { type: 'boolean', default: true, description: '是否为记录补充 detail_url;传 false 可跳过链接解析' },
|
|
252
|
+
fields: SCHEMA_OUTPUT_FIELDS,
|
|
253
|
+
}, { required: ['id'] }),
|
|
254
|
+
handler: async (args, { host }) => {
|
|
255
|
+
const id = idString(args.id, 'id');
|
|
256
|
+
const response = await datajsonRequest(host, name, path, {
|
|
257
|
+
mainTable: { id },
|
|
258
|
+
header: { operator: await operatorId(host) },
|
|
259
|
+
});
|
|
260
|
+
const fields = outputFields(args.fields);
|
|
261
|
+
const shouldDecorate = args.withDetailUrl !== false && (!fields || fields.includes('detail_url'));
|
|
262
|
+
const decorated = shouldDecorate
|
|
263
|
+
? await decorateDetailUrls(host, name, response?.datajson ?? null)
|
|
264
|
+
: response?.datajson ?? null;
|
|
265
|
+
const datajson = compactDatajson(decorated, fields, [nameField]);
|
|
266
|
+
return projectSuccess(name, datajson, { id });
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function dictionaryOperation(name, path, summary) {
|
|
271
|
+
return {
|
|
272
|
+
name,
|
|
273
|
+
resource: 'project',
|
|
274
|
+
method: 'dictionary',
|
|
275
|
+
action: name,
|
|
276
|
+
summary,
|
|
277
|
+
risk: 'read',
|
|
278
|
+
inputSchema: schemaOf({
|
|
279
|
+
filters: SCHEMA_FILTERS,
|
|
280
|
+
pageNo: { ...SCHEMA_PAGE_NO, default: 1 },
|
|
281
|
+
pageSize: { ...SCHEMA_PAGE_SIZE, default: 100 },
|
|
282
|
+
fields: SCHEMA_OUTPUT_FIELDS,
|
|
283
|
+
}),
|
|
284
|
+
handler: async (args, { host }) => {
|
|
285
|
+
const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
|
|
286
|
+
const pageSize = positiveInt(args.pageSize, 'pageSize', 100);
|
|
287
|
+
const response = await datajsonRequest(host, name, path, {
|
|
288
|
+
mainTable: compactFilters(args.filters),
|
|
289
|
+
pageInfo: { pageNo, pageSize },
|
|
290
|
+
header: { operator: await operatorId(host) },
|
|
291
|
+
});
|
|
292
|
+
const datajson = compactDatajson(response?.datajson ?? null, outputFields(args.fields), []);
|
|
293
|
+
return projectSuccess(name, datajson, { pageNo, pageSize });
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function hrmOperation(name, path, kind, summary) {
|
|
298
|
+
return {
|
|
299
|
+
name,
|
|
300
|
+
resource: 'project',
|
|
301
|
+
method: 'resolve',
|
|
302
|
+
action: name,
|
|
303
|
+
summary,
|
|
304
|
+
risk: 'read',
|
|
305
|
+
inputSchema: schemaOf({
|
|
306
|
+
name: { type: 'string', minLength: 1 },
|
|
307
|
+
match: { type: 'string', enum: ['exact', 'fuzzy'], default: 'exact' },
|
|
308
|
+
...(kind === 'employee' ? { status: { type: 'string', enum: ['normal', 'all'], default: 'normal' } } : {}),
|
|
309
|
+
}, { required: ['name'] }),
|
|
310
|
+
handler: async (args, { host }) => {
|
|
311
|
+
const body = kind === 'employee'
|
|
312
|
+
? {
|
|
313
|
+
browserMultiple: true,
|
|
314
|
+
authWecahtBook: false,
|
|
315
|
+
wxDepNeedConvertCount: 0,
|
|
316
|
+
wxEmpNeedConvertCount: 0,
|
|
317
|
+
wxTotalNeedConvertCount: 0,
|
|
318
|
+
cmd: 'all',
|
|
319
|
+
current: 1,
|
|
320
|
+
pageSize: 30,
|
|
321
|
+
formDatas: {
|
|
322
|
+
username: [{ usernameOpt: args.match === 'fuzzy' ? 'in' : 'eq', usernameInput: args.name }],
|
|
323
|
+
status: [{ statusSelect: args.status === 'all' ? [] : ['normal'] }],
|
|
324
|
+
},
|
|
325
|
+
quickSearchDatas: {},
|
|
326
|
+
type: 'and',
|
|
327
|
+
containsNoneAccount: false,
|
|
328
|
+
containsPartTimeEmployee: false,
|
|
329
|
+
}
|
|
330
|
+
: {
|
|
331
|
+
browserMultiple: true,
|
|
332
|
+
virtualType: 'ALL',
|
|
333
|
+
cmd: 'list',
|
|
334
|
+
current: 1,
|
|
335
|
+
pageSize: 200,
|
|
336
|
+
formDatas: {
|
|
337
|
+
name: [{
|
|
338
|
+
nameCondition: { id: args.match === 'fuzzy' ? 'in' : 'eq', content: '等于', disabled: false },
|
|
339
|
+
name: args.name,
|
|
340
|
+
}],
|
|
341
|
+
},
|
|
342
|
+
quickSearchDatas: {},
|
|
343
|
+
type: 'and',
|
|
344
|
+
stopDepartment: false,
|
|
345
|
+
};
|
|
346
|
+
const response = await host.request({ method: 'POST', path, body, operation: name });
|
|
347
|
+
assertWeaverSuccess(response, name);
|
|
348
|
+
const data = response?.data;
|
|
349
|
+
const rows = Array.isArray(data?.data) ? data.data : [];
|
|
350
|
+
return projectSuccess(name, {
|
|
351
|
+
total: typeof data?.total === 'number' ? data.total : rows.length,
|
|
352
|
+
rows: rows.map((row) => ({
|
|
353
|
+
id: row?.id === undefined || row?.id === null ? null : String(row.id),
|
|
354
|
+
name: row?.name ?? row?.title ?? null,
|
|
355
|
+
department: row?.department ?? row?.supDepartment ?? row?.supSubDepartment ?? null,
|
|
356
|
+
title: row?.title ?? null,
|
|
357
|
+
})),
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
export const projectReadOperations = [
|
|
363
|
+
{
|
|
364
|
+
name: 'project.app.ids',
|
|
365
|
+
resource: 'project',
|
|
366
|
+
method: 'metadata',
|
|
367
|
+
action: 'project.app.ids',
|
|
368
|
+
summary: '按固定应用标签获取当前环境的事井然应用 ID',
|
|
369
|
+
risk: 'read',
|
|
370
|
+
inputSchema: schemaOf({}),
|
|
371
|
+
handler: async (_args, { host }) => projectSuccess('project.app.ids', {
|
|
372
|
+
appId: await getAppId(host),
|
|
373
|
+
}),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: 'project.object.id',
|
|
377
|
+
resource: 'project',
|
|
378
|
+
method: 'metadata',
|
|
379
|
+
action: 'project.object.id',
|
|
380
|
+
summary: '将任务/项目出厂锚点转换成当前环境对象 ID',
|
|
381
|
+
risk: 'read',
|
|
382
|
+
inputSchema: schemaOf({ table: { type: 'string', enum: ['task', 'project'] } }, { required: ['table'] }),
|
|
383
|
+
handler: async (args, { host }) => {
|
|
384
|
+
const value = table(args.table);
|
|
385
|
+
return projectSuccess('project.object.id', { table: value, objId: await getObject(host, value) });
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'project.form.fields',
|
|
390
|
+
resource: 'project',
|
|
391
|
+
method: 'metadata',
|
|
392
|
+
action: 'project.form.fields',
|
|
393
|
+
summary: '获取任务/项目表单字段定义;未传 groupId/sourceId 时自动解析',
|
|
394
|
+
risk: 'read',
|
|
395
|
+
inputSchema: schemaOf({
|
|
396
|
+
table: { type: 'string', enum: ['task', 'project'] },
|
|
397
|
+
groupId: { type: 'string', description: '可选;缺省时自动调用 project.app.ids' },
|
|
398
|
+
sourceId: { type: 'string', description: '可选;缺省时按 table 自动解析当前环境 objId' },
|
|
399
|
+
detailFieldsGroup: { type: 'boolean', default: true },
|
|
400
|
+
enableMerge: { type: 'boolean', default: true },
|
|
401
|
+
fieldNames: {
|
|
402
|
+
type: 'array',
|
|
403
|
+
minItems: 1,
|
|
404
|
+
maxItems: 50,
|
|
405
|
+
uniqueItems: true,
|
|
406
|
+
items: { type: 'string', minLength: 1, maxLength: 100 },
|
|
407
|
+
description: '可选;只返回指定 name 的字段定义。与 fieldKeyword 同时传时 fieldNames 优先。',
|
|
408
|
+
},
|
|
409
|
+
fieldKeyword: {
|
|
410
|
+
type: 'string',
|
|
411
|
+
minLength: 1,
|
|
412
|
+
maxLength: 100,
|
|
413
|
+
description: '可选;按字段 name/text 模糊过滤字段定义,用于把中文字段名解析成 field name。',
|
|
414
|
+
},
|
|
415
|
+
}, { required: ['table'] }),
|
|
416
|
+
handler: async (args, { host }) => {
|
|
417
|
+
const value = table(args.table);
|
|
418
|
+
const groupId = args.groupId ? String(args.groupId) : await getAppId(host);
|
|
419
|
+
const sourceId = args.sourceId ? String(args.sourceId) : await getObject(host, value);
|
|
420
|
+
const response = await host.request({
|
|
421
|
+
method: 'POST',
|
|
422
|
+
path: '/api/ebuilder/common/ds/fields',
|
|
423
|
+
formData: {
|
|
424
|
+
groupId,
|
|
425
|
+
sourceId,
|
|
426
|
+
sourceType: 'FORM',
|
|
427
|
+
detailFieldsGroup: args.detailFieldsGroup === false ? 'false' : 'true',
|
|
428
|
+
enableMerge: args.enableMerge === false ? 'false' : 'true',
|
|
429
|
+
customParam: JSON.stringify({ terminal: '' }),
|
|
430
|
+
params: '[]',
|
|
431
|
+
},
|
|
432
|
+
operation: 'project.form.fields',
|
|
433
|
+
});
|
|
434
|
+
assertWeaverSuccess(response, 'project.form.fields');
|
|
435
|
+
const data = filterFormFieldResponse(response, normalizeFieldNames(args.fieldNames, 'fieldNames'), args.fieldKeyword === undefined || args.fieldKeyword === null || args.fieldKeyword === ''
|
|
436
|
+
? null
|
|
437
|
+
: String(args.fieldKeyword));
|
|
438
|
+
return projectSuccess('project.form.fields', data, { table: value, groupId, sourceId });
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: 'project.form.options',
|
|
443
|
+
resource: 'project',
|
|
444
|
+
method: 'metadata',
|
|
445
|
+
action: 'project.form.options',
|
|
446
|
+
summary: '获取选择类字段选项;未传 groupId/sourceId 时自动解析',
|
|
447
|
+
risk: 'read',
|
|
448
|
+
inputSchema: schemaOf({
|
|
449
|
+
table: { type: 'string', enum: ['task', 'project'] },
|
|
450
|
+
fieldName: { type: 'string', minLength: 1 },
|
|
451
|
+
groupId: { type: 'string' },
|
|
452
|
+
sourceId: { type: 'string' },
|
|
453
|
+
optionLevel: { type: 'integer', minimum: 1, default: 1 },
|
|
454
|
+
}, { required: ['table', 'fieldName'] }),
|
|
455
|
+
handler: async (args, { host }) => {
|
|
456
|
+
const value = table(args.table);
|
|
457
|
+
const groupId = args.groupId ? String(args.groupId) : await getAppId(host);
|
|
458
|
+
const sourceId = args.sourceId ? String(args.sourceId) : await getObject(host, value);
|
|
459
|
+
const response = await host.request({
|
|
460
|
+
method: 'GET',
|
|
461
|
+
path: '/api/ebuilder/common/ds/options',
|
|
462
|
+
query: {
|
|
463
|
+
fieldName: args.fieldName,
|
|
464
|
+
objId: sourceId,
|
|
465
|
+
sourceType: 'FORM',
|
|
466
|
+
groupId,
|
|
467
|
+
optionLevel: args.optionLevel ?? 1,
|
|
468
|
+
},
|
|
469
|
+
operation: 'project.form.options',
|
|
470
|
+
});
|
|
471
|
+
assertWeaverSuccess(response, 'project.form.options');
|
|
472
|
+
return projectSuccess('project.form.options', response, { table: value, groupId, sourceId });
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: 'project.form.data.page',
|
|
477
|
+
resource: 'project',
|
|
478
|
+
method: 'metadata',
|
|
479
|
+
action: 'project.form.data.page',
|
|
480
|
+
summary: '在关联表单中按标题字段分页查询记录,用于 Ebuilder 名称到 ID 解析',
|
|
481
|
+
risk: 'read',
|
|
482
|
+
inputSchema: schemaOf({
|
|
483
|
+
refObjId: SCHEMA_ID_STRING,
|
|
484
|
+
fieldKey: { type: 'string', minLength: 1 },
|
|
485
|
+
value: { type: 'string', minLength: 1 },
|
|
486
|
+
pageNo: SCHEMA_PAGE_NO,
|
|
487
|
+
pageSize: SCHEMA_PAGE_SIZE,
|
|
488
|
+
}, { required: ['refObjId', 'fieldKey', 'value'] }),
|
|
489
|
+
handler: async (args, { host }) => {
|
|
490
|
+
const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
|
|
491
|
+
const pageSize = positiveInt(args.pageSize, 'pageSize', 10);
|
|
492
|
+
const response = await host.request({
|
|
493
|
+
method: 'POST',
|
|
494
|
+
path: '/api/ebuilder/form/dataset/v2/getAllData',
|
|
495
|
+
body: {
|
|
496
|
+
datajson: {
|
|
497
|
+
header: { objId: idString(args.refObjId, 'refObjId') },
|
|
498
|
+
mainTable: { [String(args.fieldKey)]: args.value },
|
|
499
|
+
pageInfo: { pageNo, pageSize },
|
|
500
|
+
operationinfo: { isReturnDetail: 'n', fieldNoFindIgnore: 'false' },
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
operation: 'project.form.data.page',
|
|
504
|
+
});
|
|
505
|
+
assertDatajsonSuccess(response?.data, 'project.form.data.page');
|
|
506
|
+
return projectSuccess('project.form.data.page', response?.data?.datajson ?? null, { pageNo, pageSize });
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: 'project.version.check',
|
|
511
|
+
resource: 'project',
|
|
512
|
+
method: 'diagnose',
|
|
513
|
+
action: 'project.version.check',
|
|
514
|
+
summary: '查询事井然应用版本并判断是否满足 20.6.11;仅用于异常诊断或用户主动询问',
|
|
515
|
+
risk: 'read',
|
|
516
|
+
inputSchema: schemaOf({ appId: { type: 'string', description: '可选;缺省时自动解析' } }),
|
|
517
|
+
handler: async (args, { host }) => {
|
|
518
|
+
const appId = args.appId ? String(args.appId) : await getAppId(host);
|
|
519
|
+
const response = await host.request({
|
|
520
|
+
method: 'GET',
|
|
521
|
+
path: '/api/installer/public/querySpAppVersionInfo',
|
|
522
|
+
query: { appId },
|
|
523
|
+
operation: 'project.version.check',
|
|
524
|
+
});
|
|
525
|
+
assertWeaverSuccess(response, 'project.version.check');
|
|
526
|
+
const version = String(response?.data?.commitVersion ?? '');
|
|
527
|
+
if (!version)
|
|
528
|
+
throw new Error('接口未返回应用版本');
|
|
529
|
+
return projectSuccess('project.version.check', {
|
|
530
|
+
appId,
|
|
531
|
+
version,
|
|
532
|
+
minimumVersion: MIN_VERSION.join('.'),
|
|
533
|
+
supported: versionAtLeast(version),
|
|
534
|
+
});
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
listOperation('project.task.list', '/api/ebuilder/form/formdata/v2/getFormDataList/queryTasks', 'task_name', 'task_desc', 'task_status'),
|
|
538
|
+
detailOperation('project.task.detail', '/api/ebuilder/form/formdata/v2/getFormDataByPk/queryTaskDetail', 'task_name'),
|
|
539
|
+
listOperation('project.project.list', '/api/ebuilder/form/formdata/v2/getFormDataList/queryProjects', 'proj_name', 'proj_desc', 'proj_status'),
|
|
540
|
+
detailOperation('project.project.detail', '/api/ebuilder/form/formdata/v2/getFormDataByPk/queryProjDetail', 'proj_name'),
|
|
541
|
+
dictionaryOperation('project.dictionary.task-type', '/api/ebuilder/form/formdata/v2/getFormDataList/queryTaskTypes', '查询任务类型'),
|
|
542
|
+
dictionaryOperation('project.dictionary.task-priority', '/api/ebuilder/form/formdata/v2/getFormDataList/queryTaskPriorities', '查询任务紧急程度'),
|
|
543
|
+
dictionaryOperation('project.dictionary.task-status', '/api/ebuilder/form/formdata/v2/getFormDataList/queryTaskStatus', '查询任务状态'),
|
|
544
|
+
dictionaryOperation('project.dictionary.project-type', '/api/ebuilder/form/formdata/v2/getFormDataList/queryProjTypes', '查询项目类型'),
|
|
545
|
+
dictionaryOperation('project.dictionary.project-priority', '/api/ebuilder/form/formdata/v2/getFormDataList/queryProjPriorities', '查询项目紧急程度'),
|
|
546
|
+
dictionaryOperation('project.dictionary.project-status', '/api/ebuilder/form/formdata/v2/getFormDataList/queryProjStatus', '查询项目状态'),
|
|
547
|
+
hrmOperation('project.employee.resolve', '/api/hrm/common/browser/data/resource', 'employee', '按姓名精确或模糊解析员工 ID,默认仅查在职员工'),
|
|
548
|
+
hrmOperation('project.department.resolve', '/api/hrm/common/browser/data/department', 'organization', '按名称精确或模糊解析部门 ID'),
|
|
549
|
+
hrmOperation('project.subcompany.resolve', '/api/hrm/common/browser/data/subcompany', 'organization', '按名称精确或模糊解析分部 ID'),
|
|
550
|
+
];
|
|
551
|
+
export { getAppId, getObject, SCHEMA_FIELDS };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { projectValidationError } from '../errors.js';
|
|
2
|
+
import { projectReadOperations } from './read.js';
|
|
3
|
+
import { projectWriteOperations } from './write.js';
|
|
4
|
+
function buildRegistry() {
|
|
5
|
+
const registry = new Map();
|
|
6
|
+
const definitions = [
|
|
7
|
+
...projectReadOperations,
|
|
8
|
+
...projectWriteOperations,
|
|
9
|
+
];
|
|
10
|
+
for (const definition of definitions) {
|
|
11
|
+
if (registry.has(definition.name)) {
|
|
12
|
+
throw new Error(`duplicate project operation: ${definition.name}`);
|
|
13
|
+
}
|
|
14
|
+
registry.set(definition.name, definition);
|
|
15
|
+
}
|
|
16
|
+
return registry;
|
|
17
|
+
}
|
|
18
|
+
const projectOperationRegistry = buildRegistry();
|
|
19
|
+
export function projectOperationDefinitions() {
|
|
20
|
+
return [...projectOperationRegistry.values()];
|
|
21
|
+
}
|
|
22
|
+
export function getProjectOperation(operation) {
|
|
23
|
+
const definition = projectOperationRegistry.get(operation);
|
|
24
|
+
if (!definition) {
|
|
25
|
+
throw projectValidationError('operation_unknown', `事井然项目模块不支持操作:${operation}`);
|
|
26
|
+
}
|
|
27
|
+
return definition;
|
|
28
|
+
}
|
|
29
|
+
export function hasProjectOperation(operation) {
|
|
30
|
+
return projectOperationRegistry.has(operation);
|
|
31
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { projectValidationError } from '../errors.js';
|
|
2
|
+
export function asObject(value, field = 'input') {
|
|
3
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
4
|
+
throw projectValidationError('field_invalid', `${field} 必须是 JSON 对象`);
|
|
5
|
+
}
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
export function assertNoForbiddenKeys(input) {
|
|
9
|
+
for (const key of Object.keys(input)) {
|
|
10
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) {
|
|
11
|
+
throw projectValidationError('field_invalid', `不允许的字段名:${key}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function idString(value, field) {
|
|
16
|
+
if (value === undefined || value === null || value === '') {
|
|
17
|
+
throw projectValidationError('field_required', `${field} 不能为空`);
|
|
18
|
+
}
|
|
19
|
+
const text = String(value);
|
|
20
|
+
if (!/^\d+$/.test(text))
|
|
21
|
+
throw projectValidationError('field_invalid', `${field} 必须是数字 ID 字符串`);
|
|
22
|
+
return text;
|
|
23
|
+
}
|
|
24
|
+
export function optionalIdString(value, field) {
|
|
25
|
+
if (value === undefined || value === null || value === '')
|
|
26
|
+
return undefined;
|
|
27
|
+
return idString(value, field);
|
|
28
|
+
}
|
|
29
|
+
export function positiveInt(value, field, fallback, maximum = 1000) {
|
|
30
|
+
if (value === undefined || value === null || value === '')
|
|
31
|
+
return fallback;
|
|
32
|
+
const parsed = Number(value);
|
|
33
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > maximum) {
|
|
34
|
+
throw projectValidationError('field_invalid', `${field} 必须是 1-${maximum} 的整数`);
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
export function compactFilters(value) {
|
|
39
|
+
if (!value)
|
|
40
|
+
return {};
|
|
41
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== null && item !== ''));
|
|
42
|
+
}
|
|
43
|
+
export function queryText(value, field) {
|
|
44
|
+
if (value === undefined || value === null || value === '')
|
|
45
|
+
return undefined;
|
|
46
|
+
if (typeof value !== 'string')
|
|
47
|
+
throw projectValidationError('field_invalid', `${field} 必须是字符串`);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
export async function operatorId(host) {
|
|
51
|
+
const session = await host.getSession();
|
|
52
|
+
if (!session.userId)
|
|
53
|
+
throw projectValidationError('operator_missing', '当前登录会话缺少 operator,请重新登录');
|
|
54
|
+
return session.userId;
|
|
55
|
+
}
|
|
56
|
+
export function stableFingerprint(value) {
|
|
57
|
+
const text = JSON.stringify(value, (_key, item) => {
|
|
58
|
+
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
59
|
+
return Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b)));
|
|
60
|
+
}
|
|
61
|
+
return item;
|
|
62
|
+
});
|
|
63
|
+
let hash = 0;
|
|
64
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
65
|
+
hash = (hash * 31 + text.charCodeAt(index)) >>> 0;
|
|
66
|
+
}
|
|
67
|
+
return `${text.length}:${hash.toString(16)}`;
|
|
68
|
+
}
|