weaver-work-cli 0.1.6 → 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 +44 -31
- 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,381 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { okrConfirmationError, okrValidationError } from '../errors.js';
|
|
3
|
+
import { EB_M_STATUS_LABELS, PERIOD_TYPE_LABELS, STANDARD_STATUS_LABELS, } from './types.js';
|
|
4
|
+
import { okrUnsupportedError } from '../errors.js';
|
|
5
|
+
/** 原型污染防护:禁止出现在输入对象中的键。 */
|
|
6
|
+
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
7
|
+
/** 凭证类字段禁止出现在业务输入中:认证由 CLI 托管。 */
|
|
8
|
+
const FORBIDDEN_INPUT_KEYS = new Set([
|
|
9
|
+
'authorization',
|
|
10
|
+
'cookie',
|
|
11
|
+
'eteamsid',
|
|
12
|
+
'access_token',
|
|
13
|
+
'app_key',
|
|
14
|
+
'app_secret',
|
|
15
|
+
'token',
|
|
16
|
+
'user_agent',
|
|
17
|
+
]);
|
|
18
|
+
export function asObject(value) {
|
|
19
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
20
|
+
throw okrValidationError('input_not_object', '输入必须是 JSON 对象');
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
export function assertNoForbiddenKeys(input) {
|
|
25
|
+
for (const key of Object.keys(input)) {
|
|
26
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
27
|
+
throw okrValidationError('forbidden_key', `输入包含非法字段:${key}`);
|
|
28
|
+
}
|
|
29
|
+
if (FORBIDDEN_INPUT_KEYS.has(key.toLowerCase())) {
|
|
30
|
+
throw okrValidationError('field_forbidden', `禁止业务输入字段:${key}(认证由 CLI 托管)`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function toIdString(value, field) {
|
|
35
|
+
if (value === undefined || value === null || value === '') {
|
|
36
|
+
throw okrValidationError('id_invalid', `${field} 必须是数字 ID(雪花 ID 请用字符串传递)`);
|
|
37
|
+
}
|
|
38
|
+
const text = String(value);
|
|
39
|
+
if (!/^\d+$/.test(text)) {
|
|
40
|
+
throw okrValidationError('id_invalid', `${field} 必须是数字 ID`);
|
|
41
|
+
}
|
|
42
|
+
return text;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 人员 ID 归一化。源资料强制规则:入参不能直接传人员名称,必须先由 weaver-e10-hrm skill 解析为
|
|
46
|
+
* id 或 employeeId;这里在 CLI 边界再做一次硬校验,避免姓名被当成 ID 传下去。
|
|
47
|
+
*/
|
|
48
|
+
export function toPersonIdString(value, field) {
|
|
49
|
+
if (value === undefined || value === null || value === '') {
|
|
50
|
+
throw okrValidationError('person_id_required', `${field} 必填`);
|
|
51
|
+
}
|
|
52
|
+
const text = String(value);
|
|
53
|
+
if (!/^\d+$/.test(text)) {
|
|
54
|
+
throw okrValidationError('person_id_invalid', `${field} 必须是人员 ID(数字字符串),不能直接传人员名称;请先通过 weaver-e10-hrm skill 用姓名解析出 id 或 employeeId(禁止取 userId 字段)`, { field, value: text });
|
|
55
|
+
}
|
|
56
|
+
return text;
|
|
57
|
+
}
|
|
58
|
+
/** 人员 ID 列表归一化:接受 `"1001,1002"` 或 `["1001","1002"]`,输出英文逗号拼接字符串。 */
|
|
59
|
+
export function toPersonIdListText(value, field) {
|
|
60
|
+
const list = [];
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
for (const item of value) {
|
|
63
|
+
if (item === undefined || item === null || item === '')
|
|
64
|
+
continue;
|
|
65
|
+
list.push(String(item));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (typeof value === 'string' || typeof value === 'number') {
|
|
69
|
+
for (const part of String(value).split(',')) {
|
|
70
|
+
const trimmed = part.trim();
|
|
71
|
+
if (trimmed)
|
|
72
|
+
list.push(trimmed);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw okrValidationError('person_id_invalid', `${field} 必须是人员 ID 或人员 ID 数组`);
|
|
77
|
+
}
|
|
78
|
+
if (list.length === 0) {
|
|
79
|
+
throw okrValidationError('person_id_required', `${field} 至少需要一个人员 ID`);
|
|
80
|
+
}
|
|
81
|
+
for (const id of list) {
|
|
82
|
+
if (!/^\d+$/.test(id)) {
|
|
83
|
+
throw okrValidationError('person_id_invalid', `${field} 必须是人员 ID(数字字符串),不能直接传人员名称;请先通过 weaver-e10-hrm skill 解析`, { field, value: id });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return list.join(',');
|
|
87
|
+
}
|
|
88
|
+
/** 移除值为 undefined / null / '' 的键(ebuilder 自定义接口要求空字符串字段直接不传)。 */
|
|
89
|
+
export function compact(value) {
|
|
90
|
+
for (const key of Object.keys(value)) {
|
|
91
|
+
const item = value[key];
|
|
92
|
+
if (item === undefined || item === null || item === '')
|
|
93
|
+
delete value[key];
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
/** eb Select 字段归一化:可能是 `[{name,id}]`、`["1"]`、`"1"` 或数字,统一返回标量字符串。 */
|
|
98
|
+
export function optionValue(value) {
|
|
99
|
+
if (value === undefined || value === null || value === '')
|
|
100
|
+
return undefined;
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
const first = value[0];
|
|
103
|
+
if (first === undefined || first === null)
|
|
104
|
+
return undefined;
|
|
105
|
+
if (typeof first === 'string' || typeof first === 'number')
|
|
106
|
+
return String(first);
|
|
107
|
+
if (typeof first === 'object') {
|
|
108
|
+
const record = first;
|
|
109
|
+
const candidate = record.id ?? record.value ?? record.name;
|
|
110
|
+
if (candidate === undefined || candidate === null || candidate === '')
|
|
111
|
+
return undefined;
|
|
112
|
+
return String(candidate);
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
117
|
+
return String(value);
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
/** 从 eb datajson 响应中取出 datas[].mainTable 列表。 */
|
|
121
|
+
export function ebMainTables(value) {
|
|
122
|
+
const datas = value?.datajson?.datas;
|
|
123
|
+
if (!Array.isArray(datas))
|
|
124
|
+
return [];
|
|
125
|
+
const list = [];
|
|
126
|
+
for (const item of datas) {
|
|
127
|
+
if (item && typeof item === 'object' && item.mainTable && typeof item.mainTable === 'object') {
|
|
128
|
+
list.push(item.mainTable);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return list;
|
|
132
|
+
}
|
|
133
|
+
export function fingerprint(value) {
|
|
134
|
+
return createHash('sha256').update(JSON.stringify(value ?? null)).digest('hex').slice(0, 32);
|
|
135
|
+
}
|
|
136
|
+
export function requireConfirm(args, action) {
|
|
137
|
+
if (args.confirm !== true) {
|
|
138
|
+
throw okrConfirmationError(action, {
|
|
139
|
+
hint: '重新调用时传入 confirm=true 与 prepare 返回的 continuation',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** 校验 confirm + continuation,并确认登录上下文未变化。 */
|
|
144
|
+
export async function ensureConfirmLease(args, action, env) {
|
|
145
|
+
requireConfirm(args, action);
|
|
146
|
+
const lease = await env.continuation.verify(args.continuation, action);
|
|
147
|
+
const session = await env.host.getSession();
|
|
148
|
+
if (lease.baseUrl !== session.baseUrl || lease.profile !== session.profile || lease.userId !== session.userId) {
|
|
149
|
+
throw okrValidationError('context_mismatch', '确认上下文已变化(租户或登录账号不同),请重新执行 prepare');
|
|
150
|
+
}
|
|
151
|
+
return lease;
|
|
152
|
+
}
|
|
153
|
+
/** 断言当前链路支持该能力,否则抛 policy 错误(源资料未提供该链路的实现依据)。 */
|
|
154
|
+
export function assertLinkSupported(link, supported, capability, message, details) {
|
|
155
|
+
if (link !== supported) {
|
|
156
|
+
throw okrUnsupportedError('link_unsupported', message, {
|
|
157
|
+
capability,
|
|
158
|
+
link,
|
|
159
|
+
expectedLink: supported,
|
|
160
|
+
...(details || {}),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function pad(value) {
|
|
165
|
+
return String(value).padStart(2, '0');
|
|
166
|
+
}
|
|
167
|
+
function lastDayOfMonth(year, month) {
|
|
168
|
+
return new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
169
|
+
}
|
|
170
|
+
export function assertPeriodType(value) {
|
|
171
|
+
const text = value === undefined || value === null ? '' : String(value);
|
|
172
|
+
if (!['1', '2', '3', '4'].includes(text)) {
|
|
173
|
+
throw okrValidationError('period_type_invalid', 'period_type 取值必须是 1(年度)/ 2(季度)/ 3(月度)/ 4(自定义)');
|
|
174
|
+
}
|
|
175
|
+
return text;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 解析并校验周期参数。源资料约定:
|
|
179
|
+
* - 年度目标:period_range 为年份字符串,示例 "2026"
|
|
180
|
+
* - 季度目标:period_range 为年份字符串,同时用 period_quarter 标识季度,示例 "4"
|
|
181
|
+
* - 月度目标:period_range 为 yyyy-MM,示例 "2026-04"
|
|
182
|
+
* - 自定义周期:period_range 为「开始日期,结束日期」,示例 "2026-01-01,2026-12-31"
|
|
183
|
+
*/
|
|
184
|
+
export function resolvePeriod(args) {
|
|
185
|
+
const periodType = assertPeriodType(args.period_type);
|
|
186
|
+
const periodRange = args.period_range;
|
|
187
|
+
if (typeof periodRange !== 'string' || !periodRange.trim()) {
|
|
188
|
+
throw okrValidationError('period_range_required', 'period_range(周期范围)必填,格式随 period_type 变化');
|
|
189
|
+
}
|
|
190
|
+
const range = periodRange.trim();
|
|
191
|
+
const spec = { periodType, periodRange: range };
|
|
192
|
+
if (periodType === '1') {
|
|
193
|
+
if (!/^\d{4}$/.test(range)) {
|
|
194
|
+
throw okrValidationError('period_range_invalid', '年度目标的 period_range 必须是年份字符串,示例 "2026"');
|
|
195
|
+
}
|
|
196
|
+
return spec;
|
|
197
|
+
}
|
|
198
|
+
if (periodType === '2') {
|
|
199
|
+
if (!/^\d{4}$/.test(range)) {
|
|
200
|
+
throw okrValidationError('period_range_invalid', '季度目标的 period_range 必须是年份字符串,示例 "2026"');
|
|
201
|
+
}
|
|
202
|
+
const quarter = args.period_quarter === undefined || args.period_quarter === null || args.period_quarter === ''
|
|
203
|
+
? ''
|
|
204
|
+
: String(args.period_quarter);
|
|
205
|
+
if (!['1', '2', '3', '4'].includes(quarter)) {
|
|
206
|
+
throw okrValidationError('period_quarter_invalid', '季度目标的 period_quarter 必填,取值 1/2/3/4');
|
|
207
|
+
}
|
|
208
|
+
spec.periodQuarter = quarter;
|
|
209
|
+
return spec;
|
|
210
|
+
}
|
|
211
|
+
if (periodType === '3') {
|
|
212
|
+
if (!/^\d{4}-\d{2}$/.test(range)) {
|
|
213
|
+
throw okrValidationError('period_range_invalid', '月度目标的 period_range 必须是 yyyy-MM 格式,示例 "2026-04"');
|
|
214
|
+
}
|
|
215
|
+
return spec;
|
|
216
|
+
}
|
|
217
|
+
if (!/^\d{4}-\d{2}-\d{2},\d{4}-\d{2}-\d{2}$/.test(range)) {
|
|
218
|
+
throw okrValidationError('period_range_invalid', '自定义周期目标的 period_range 必须是「开始日期,结束日期」格式,示例 "2026-01-01,2026-12-31"');
|
|
219
|
+
}
|
|
220
|
+
const [start, end] = range.split(',');
|
|
221
|
+
if (start > end) {
|
|
222
|
+
throw okrValidationError('period_range_invalid', '自定义周期的开始日期不能晚于结束日期');
|
|
223
|
+
}
|
|
224
|
+
return spec;
|
|
225
|
+
}
|
|
226
|
+
/** 把周期转换为 ebuilder 表单字段(period_year / period_month / period_quarter / period_range_0 / period_range_1)。 */
|
|
227
|
+
export function ebPeriodFields(spec) {
|
|
228
|
+
const fields = { period_type: spec.periodType };
|
|
229
|
+
if (spec.periodType === '1') {
|
|
230
|
+
const year = Number(spec.periodRange);
|
|
231
|
+
fields.period_year = spec.periodRange;
|
|
232
|
+
fields.period_range_0 = `${year}-01-01`;
|
|
233
|
+
fields.period_range_1 = `${year}-12-31`;
|
|
234
|
+
return fields;
|
|
235
|
+
}
|
|
236
|
+
if (spec.periodType === '2') {
|
|
237
|
+
const year = Number(spec.periodRange);
|
|
238
|
+
const quarter = Number(spec.periodQuarter);
|
|
239
|
+
const startMonth = (quarter - 1) * 3 + 1;
|
|
240
|
+
const endMonth = startMonth + 2;
|
|
241
|
+
fields.period_year = spec.periodRange;
|
|
242
|
+
fields.period_quarter = String(quarter);
|
|
243
|
+
fields.period_range_0 = `${year}-${pad(startMonth)}-01`;
|
|
244
|
+
fields.period_range_1 = `${year}-${pad(endMonth)}-${pad(lastDayOfMonth(year, endMonth))}`;
|
|
245
|
+
return fields;
|
|
246
|
+
}
|
|
247
|
+
if (spec.periodType === '3') {
|
|
248
|
+
const [yearText, monthText] = spec.periodRange.split('-');
|
|
249
|
+
const year = Number(yearText);
|
|
250
|
+
const month = Number(monthText);
|
|
251
|
+
fields.period_year = yearText;
|
|
252
|
+
fields.period_month = spec.periodRange;
|
|
253
|
+
fields.period_range_0 = `${yearText}-${monthText}-01`;
|
|
254
|
+
fields.period_range_1 = `${yearText}-${monthText}-${pad(lastDayOfMonth(year, month))}`;
|
|
255
|
+
return fields;
|
|
256
|
+
}
|
|
257
|
+
const [start, end] = spec.periodRange.split(',');
|
|
258
|
+
fields.period_range_0 = start;
|
|
259
|
+
fields.period_range_1 = end;
|
|
260
|
+
return fields;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* ebuilder 列表筛选字段:只传源资料列出的筛选字段,空值不传。
|
|
264
|
+
* 周期筛选同时给到 period_type + 派生字段(period_year / period_month / period_quarter)
|
|
265
|
+
* 与起止日期字段 period_range_0 / period_range_1,保证按周期命中的目标集合精确可预期。
|
|
266
|
+
*/
|
|
267
|
+
export function ebPeriodFilters(spec) {
|
|
268
|
+
const fields = ebPeriodFields(spec);
|
|
269
|
+
const filters = {
|
|
270
|
+
period_type: spec.periodType,
|
|
271
|
+
period_range_0: fields.period_range_0,
|
|
272
|
+
period_range_1: fields.period_range_1,
|
|
273
|
+
};
|
|
274
|
+
if (fields.period_year)
|
|
275
|
+
filters.period_year = fields.period_year;
|
|
276
|
+
if (spec.periodType === '2')
|
|
277
|
+
filters.period_quarter = String(spec.periodQuarter);
|
|
278
|
+
else if (fields.period_month)
|
|
279
|
+
filters.period_month = fields.period_month;
|
|
280
|
+
return filters;
|
|
281
|
+
}
|
|
282
|
+
function asText(value) {
|
|
283
|
+
if (value === undefined || value === null)
|
|
284
|
+
return null;
|
|
285
|
+
const text = String(value);
|
|
286
|
+
return text === '' ? null : text;
|
|
287
|
+
}
|
|
288
|
+
function asNumber(value) {
|
|
289
|
+
if (value === undefined || value === null || value === '')
|
|
290
|
+
return null;
|
|
291
|
+
const numeric = Number(value);
|
|
292
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
293
|
+
}
|
|
294
|
+
function periodTypeLabelOf(periodType) {
|
|
295
|
+
return periodType ? (PERIOD_TYPE_LABELS[periodType] ?? null) : null;
|
|
296
|
+
}
|
|
297
|
+
/** 标准版目标对象 → 统一业务结构(status 用标准版状态映射)。 */
|
|
298
|
+
export function normalizeStandardGoal(item, detailUrl, keyresultList) {
|
|
299
|
+
const periodType = asText(item?.periodType);
|
|
300
|
+
const status = asText(item?.status);
|
|
301
|
+
return {
|
|
302
|
+
goalId: asText(item?.id),
|
|
303
|
+
name: asText(item?.name),
|
|
304
|
+
principalid: asText(item?.principalid),
|
|
305
|
+
principalName: asText(item?.principalName),
|
|
306
|
+
periodType,
|
|
307
|
+
periodTypeLabel: periodTypeLabelOf(periodType),
|
|
308
|
+
periodRange: asText(item?.periodRange),
|
|
309
|
+
periodStart: asText(item?.periodStart),
|
|
310
|
+
periodEnd: asText(item?.periodEnd),
|
|
311
|
+
status,
|
|
312
|
+
statusLabel: status ? (STANDARD_STATUS_LABELS[status] ?? null) : null,
|
|
313
|
+
progress: asNumber(item?.progress),
|
|
314
|
+
score: asNumber(item?.score),
|
|
315
|
+
parentId: asText(item?.parentId),
|
|
316
|
+
creator: asText(item?.creator),
|
|
317
|
+
createTime: asText(item?.createTime),
|
|
318
|
+
remark: asText(item?.remark),
|
|
319
|
+
detailUrl,
|
|
320
|
+
...(keyresultList === undefined ? {} : { keyresultList }),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/** ebuilder mainTable → 统一业务结构(m_status 用 ebuilder 进展映射)。 */
|
|
324
|
+
export function normalizeEbGoal(mainTable, detailUrl) {
|
|
325
|
+
const periodType = optionValue(mainTable?.period_type) ?? null;
|
|
326
|
+
const mStatus = optionValue(mainTable?.m_status) ?? null;
|
|
327
|
+
const start = asText(optionValue(mainTable?.period_range_0));
|
|
328
|
+
const end = asText(optionValue(mainTable?.period_range_1));
|
|
329
|
+
const principal = mainTable?.principalid;
|
|
330
|
+
const principalName = Array.isArray(principal)
|
|
331
|
+
? asText(principal[0]?.name ?? principal[0]?.value)
|
|
332
|
+
: asText(principal?.name);
|
|
333
|
+
return {
|
|
334
|
+
goalId: asText(mainTable?.id),
|
|
335
|
+
name: asText(mainTable?.obj_name),
|
|
336
|
+
principalid: Array.isArray(principal)
|
|
337
|
+
? asText(principal[0]?.id ?? principal[0])
|
|
338
|
+
: (asText(typeof principal === 'object' && principal ? principal.id : principal)),
|
|
339
|
+
principalName,
|
|
340
|
+
periodType,
|
|
341
|
+
periodTypeLabel: periodTypeLabelOf(periodType),
|
|
342
|
+
periodRange: start && end ? `${start} ~ ${end}` : (start ?? end),
|
|
343
|
+
periodStart: start,
|
|
344
|
+
periodEnd: end,
|
|
345
|
+
status: mStatus,
|
|
346
|
+
statusLabel: mStatus ? (EB_M_STATUS_LABELS[mStatus] ?? null) : null,
|
|
347
|
+
progress: asNumber(optionValue(mainTable?.progress)),
|
|
348
|
+
score: asNumber(optionValue(mainTable?.score)),
|
|
349
|
+
parentId: asText(optionValue(mainTable?.parent_id)),
|
|
350
|
+
creator: asText(optionValue(mainTable?.creator)),
|
|
351
|
+
createTime: asText(mainTable?.create_time),
|
|
352
|
+
remark: asText(mainTable?.remark),
|
|
353
|
+
detailUrl,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 分页目标列表的表格输出。源资料强制规则:固定列「目标名称、目标责任人名称、目标周期类型、
|
|
358
|
+
* 目标周期范围」,且目标名称可点击直接跳转目标详情。
|
|
359
|
+
*/
|
|
360
|
+
export function buildGoalTable(goals) {
|
|
361
|
+
const columns = ['目标名称', '目标责任人名称', '目标周期类型', '目标周期范围'];
|
|
362
|
+
const rows = goals.map((goal) => ({
|
|
363
|
+
name: goal.name ?? '',
|
|
364
|
+
principalName: goal.principalName ?? '',
|
|
365
|
+
periodType: goal.periodTypeLabel ?? '',
|
|
366
|
+
periodRange: goal.periodRange ?? '',
|
|
367
|
+
view_link: goal.detailUrl,
|
|
368
|
+
anchor: goal.name ?? '',
|
|
369
|
+
}));
|
|
370
|
+
const header = `| ${columns.join(' | ')} |`;
|
|
371
|
+
const divider = `| ${columns.map(() => '---').join(' | ')} |`;
|
|
372
|
+
const body = rows.map((row) => {
|
|
373
|
+
const nameCell = row.view_link ? `[${row.anchor}](${row.view_link})` : row.anchor;
|
|
374
|
+
return `| ${nameCell} | ${row.principalName} | ${row.periodType} | ${row.periodRange} |`;
|
|
375
|
+
});
|
|
376
|
+
return {
|
|
377
|
+
columns,
|
|
378
|
+
rows,
|
|
379
|
+
markdown: [header, divider, ...body].join('\n'),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export function okrSuccess(action, data, meta) {
|
|
2
|
+
const payload = { ok: true, action, data };
|
|
3
|
+
if (meta && Object.keys(meta).length > 0)
|
|
4
|
+
payload.meta = meta;
|
|
5
|
+
return payload;
|
|
6
|
+
}
|
|
7
|
+
/** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
|
|
8
|
+
export function schemaOf(properties, options = {}) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'object',
|
|
11
|
+
additionalProperties: false,
|
|
12
|
+
properties,
|
|
13
|
+
...(options.required?.length ? { required: options.required } : {}),
|
|
14
|
+
...(options.oneOf?.length ? { oneOf: options.oneOf } : {}),
|
|
15
|
+
...(options.anyOf?.length ? { anyOf: options.anyOf } : {}),
|
|
16
|
+
...(options.description ? { description: options.description } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** 目标 / 关键成果 ID:雪花 ID 语义,接受数字或数字字符串。 */
|
|
20
|
+
export const SCHEMA_ID = {
|
|
21
|
+
oneOf: [{ type: 'integer' }, { type: 'string', pattern: '^\\d+$' }],
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 人员 ID:必须保持字符串形态(源资料强制规则:雪花 ID 传参必须加引号包裹为字符串,避免精度丢失)。
|
|
25
|
+
*/
|
|
26
|
+
export const SCHEMA_PERSON_ID = { type: 'string', minLength: 1, pattern: '^\\d+$' };
|
|
27
|
+
/** 多个人员 ID(英文逗号拼接)。 */
|
|
28
|
+
export const SCHEMA_ID_LIST_TEXT = { type: 'string', pattern: '^\\d+(,\\d+)*$' };
|
|
29
|
+
/** 日期 yyyy-MM-dd。 */
|
|
30
|
+
export const SCHEMA_DATE = { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' };
|
|
31
|
+
/** 周期类型:1-年度 2-季度 3-月度 4-自定义。 */
|
|
32
|
+
export const PERIOD_TYPE_VALUES = ['1', '2', '3', '4'];
|
|
33
|
+
export const SCHEMA_PERIOD_TYPE = { type: 'string', enum: [...PERIOD_TYPE_VALUES] };
|
|
34
|
+
/** 季度:1-4。 */
|
|
35
|
+
export const QUARTER_VALUES = ['1', '2', '3', '4'];
|
|
36
|
+
export const SCHEMA_QUARTER = { type: 'string', enum: [...QUARTER_VALUES] };
|
|
37
|
+
/** 目标级别:1-个人 2-部门。 */
|
|
38
|
+
export const SCOPE_VALUES = ['1', '2'];
|
|
39
|
+
export const SCHEMA_SCOPE = { type: 'string', enum: [...SCOPE_VALUES] };
|
|
40
|
+
/** 周期类型文案。 */
|
|
41
|
+
export const PERIOD_TYPE_LABELS = {
|
|
42
|
+
'1': '年度',
|
|
43
|
+
'2': '季度',
|
|
44
|
+
'3': '月度',
|
|
45
|
+
'4': '自定义',
|
|
46
|
+
};
|
|
47
|
+
/** 目标级别文案(两个链路语义一致)。 */
|
|
48
|
+
export const SCOPE_LABELS = {
|
|
49
|
+
'1': '个人目标',
|
|
50
|
+
'2': '部门目标',
|
|
51
|
+
};
|
|
52
|
+
/** e10 标准版目标状态:0-草稿 1-进行中 2-已完成 3-已撤销 4-目标制定待审批 5-目标更新待审批 6-目标完成待审批。 */
|
|
53
|
+
export const STANDARD_STATUS_LABELS = {
|
|
54
|
+
'0': '草稿',
|
|
55
|
+
'1': '进行中',
|
|
56
|
+
'2': '已完成',
|
|
57
|
+
'3': '已撤销',
|
|
58
|
+
'4': '目标制定待审批',
|
|
59
|
+
'5': '目标更新待审批',
|
|
60
|
+
'6': '目标完成待审批',
|
|
61
|
+
};
|
|
62
|
+
/** e10 标准版新建/编辑接口的 status:0-保存草稿 1-正式提交。 */
|
|
63
|
+
export const STANDARD_SAVE_STATUS_LABELS = {
|
|
64
|
+
'0': '保存草稿',
|
|
65
|
+
'1': '正式提交',
|
|
66
|
+
};
|
|
67
|
+
/** ebuilder 目标进展 m_status:1-进行中 2-已完成 3-已撤销 4-审批中 5-未开始。 */
|
|
68
|
+
export const EB_M_STATUS_LABELS = {
|
|
69
|
+
'1': '进行中',
|
|
70
|
+
'2': '已完成',
|
|
71
|
+
'3': '已撤销',
|
|
72
|
+
'4': '审批中',
|
|
73
|
+
'5': '未开始',
|
|
74
|
+
};
|
|
75
|
+
/** ebuilder 目标紧急状态 wb_state:1-正常 2-紧急 3-非常紧急。 */
|
|
76
|
+
export const WB_STATE_LABELS = {
|
|
77
|
+
'1': '正常',
|
|
78
|
+
'2': '紧急',
|
|
79
|
+
'3': '非常紧急',
|
|
80
|
+
};
|
|
81
|
+
/** ebuilder 关键成果风险指数 risk:1-正常 2-有风险 3-已延期。 */
|
|
82
|
+
export const KR_RISK_LABELS = {
|
|
83
|
+
'1': '正常',
|
|
84
|
+
'2': '有风险',
|
|
85
|
+
'3': '已延期',
|
|
86
|
+
};
|
|
87
|
+
/** 关键成果列表固定展示列(源资料强制规则:目标名称、目标责任人名称、目标周期类型、目标周期范围)。 */
|
|
88
|
+
export const GOAL_TABLE_COLUMNS = ['目标名称', '目标责任人名称', '目标周期类型', '目标周期范围'];
|