weaver-work-cli 0.1.8 → 0.1.20
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 +11 -11
- package/dist/cmd/action.js +76 -0
- package/dist/cmd/index.js +2 -0
- package/dist/cmd/refs/index.js +103 -0
- package/dist/cmd/register.js +31 -0
- package/dist/cmd/root.js +1 -0
- package/dist/cmd/setup/index.js +25 -5
- package/dist/cmd/skills/index.js +37 -5
- package/dist/core/connector.js +54 -0
- package/dist/core/context.js +2 -1
- package/dist/core/input.js +48 -0
- package/dist/core/output.js +298 -3
- package/dist/core/refs.js +432 -0
- package/dist/core/state.js +26 -0
- package/dist/internal/e10/auth/commands.js +2 -2
- package/dist/internal/e10/auth/session.js +14 -0
- package/dist/internal/e10/auth/xiaoe.js +116 -51
- package/dist/internal/e10/request-runtime.js +5 -0
- package/dist/internal/skills/install.js +148 -28
- package/dist/internal/skills/reader.js +12 -9
- package/dist/shortcuts/blog/continuation.js +74 -0
- package/dist/shortcuts/blog/errors.js +60 -0
- package/dist/shortcuts/blog/host.js +175 -0
- package/dist/shortcuts/blog/index.js +324 -0
- package/dist/shortcuts/blog/manifest.js +415 -0
- package/dist/shortcuts/blog/operations/comment.js +125 -0
- package/dist/shortcuts/blog/operations/comments.js +31 -0
- package/dist/shortcuts/blog/operations/edit.js +131 -0
- package/dist/shortcuts/blog/operations/export.js +46 -0
- package/dist/shortcuts/blog/operations/like.js +80 -0
- package/dist/shortcuts/blog/operations/publish.js +157 -0
- package/dist/shortcuts/blog/operations/query-user.js +58 -0
- package/dist/shortcuts/blog/operations/query.js +96 -0
- package/dist/shortcuts/blog/operations/registry.js +54 -0
- package/dist/shortcuts/blog/operations/remind.js +176 -0
- package/dist/shortcuts/blog/operations/shared.js +330 -0
- package/dist/shortcuts/blog/operations/types.js +16 -0
- package/dist/shortcuts/blog/operations.js +9 -0
- package/dist/shortcuts/caizhilian/continuation.js +70 -0
- package/dist/shortcuts/caizhilian/errors.js +53 -0
- package/dist/shortcuts/caizhilian/host.js +190 -0
- package/dist/shortcuts/caizhilian/index.js +186 -0
- package/dist/shortcuts/caizhilian/manifest.js +42 -0
- package/dist/shortcuts/caizhilian/operations/api.js +801 -0
- package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
- package/dist/shortcuts/caizhilian/operations/files.js +306 -0
- package/dist/shortcuts/caizhilian/operations/form.js +170 -0
- package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
- package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
- package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +346 -0
- package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
- package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
- package/dist/shortcuts/caizhilian/operations/handlers/folder.js +969 -0
- package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
- package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
- package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
- package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
- package/dist/shortcuts/caizhilian/operations/specs.js +623 -0
- package/dist/shortcuts/caizhilian/operations/types.js +57 -0
- package/dist/shortcuts/caizhilian/operations.js +21 -0
- package/dist/shortcuts/contract/continuation.js +70 -0
- package/dist/shortcuts/contract/errors.js +62 -0
- package/dist/shortcuts/contract/host.js +205 -0
- package/dist/shortcuts/contract/index.js +318 -0
- package/dist/shortcuts/contract/manifest.js +596 -0
- package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
- package/dist/shortcuts/contract/operations/form-info.js +371 -0
- package/dist/shortcuts/contract/operations/read.js +118 -0
- package/dist/shortcuts/contract/operations/registry.js +43 -0
- package/dist/shortcuts/contract/operations/request.js +300 -0
- package/dist/shortcuts/contract/operations/shared.js +133 -0
- package/dist/shortcuts/contract/operations/subject.js +157 -0
- package/dist/shortcuts/contract/operations/template.js +81 -0
- package/dist/shortcuts/contract/operations/types.js +7 -0
- package/dist/shortcuts/contract/operations/workflow.js +52 -0
- package/dist/shortcuts/contract/operations/write.js +131 -0
- package/dist/shortcuts/contract/operations.js +8 -0
- package/dist/shortcuts/hrm/index.js +83 -2
- package/dist/shortcuts/hrm/operations/employee.js +160 -30
- package/dist/shortcuts/hrm/operations/org.js +17 -3
- package/dist/shortcuts/hrm/operations/position.js +18 -2
- package/dist/shortcuts/index.js +5 -1
- package/dist/shortcuts/invoice/index.js +74 -36
- package/dist/shortcuts/invoice/manifest.js +674 -4
- package/dist/shortcuts/invoice/markdown.js +172 -0
- package/dist/shortcuts/invoice/operations/add.js +3 -3
- package/dist/shortcuts/invoice/operations/delete.js +2 -2
- package/dist/shortcuts/invoice/operations/get.js +10 -2
- package/dist/shortcuts/invoice/operations/import.js +2 -2
- package/dist/shortcuts/invoice/operations/registry.js +8 -1
- package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
- package/dist/shortcuts/invoice/operations/reim.js +85 -2
- package/dist/shortcuts/invoice/operations/shared.js +75 -8
- package/dist/shortcuts/invoice/operations/update.js +2 -2
- package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
- package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
- package/dist/shortcuts/invoice/projection.js +328 -0
- package/dist/shortcuts/meeting/host.js +18 -1
- package/dist/shortcuts/meeting/manifest.js +30 -18
- package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
- package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
- package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
- package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
- package/dist/shortcuts/meeting/operations/env.js +8 -1
- package/dist/shortcuts/meeting/operations/registry.js +2 -0
- package/dist/shortcuts/meeting/operations/signs.js +216 -2
- package/dist/shortcuts/workflow/endpoints.js +2 -1
- package/dist/shortcuts/workflow/errors.js +8 -2
- package/dist/shortcuts/workflow/host.js +6 -3
- package/dist/shortcuts/workflow/operations/form.js +931 -589
- package/dist/shortcuts/workflow/operations/read.js +128 -205
- package/dist/shortcuts/workflow/operations/shared.js +21 -0
- package/dist/shortcuts/workflow/operations/write.js +750 -52
- package/dist/shortcuts/workflow/operations.js +36 -10
- package/docs/_catalog.md +6 -0
- package/docs/agent-invoice.md +5 -1
- package/docs/agent-io-channel-design.md +468 -0
- package/docs/agent-skill-install.md +9 -4
- package/docs/blog.md +123 -0
- package/docs/caizhilian.md +304 -0
- package/docs/contract.md +91 -0
- package/docs/e10-auth.md +2 -2
- package/docs/hrm.md +19 -9
- package/docs/invoice.md +5 -1
- package/docs/meeting.md +1 -1
- package/docs/operation-manual.md +15 -3
- package/docs/skill-review-2026-09-19.md +2 -2
- package/package.json +2 -2
- package/scripts/package-skill.mjs +32 -20
- package/skill-template/business-info.json +113 -14
- package/skill-template/domains/blog.md +24 -0
- package/skill-template/domains/caizhilian.md +22 -0
- package/skill-template/domains/jinchengda.md +23 -0
- package/skill-template/domains/meeting.md +1 -1
- package/skill-template/domains/workflow.md +1 -1
- package/skill-template/skill-template.md +1 -1
- package/skills/weaver-e10-blog/SKILL.md +111 -0
- package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
- package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
- package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
- package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
- package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
- package/skills/weaver-e10-blog/references/blog-export.md +42 -0
- package/skills/weaver-e10-blog/references/blog-like.md +50 -0
- package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
- package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
- package/skills/weaver-e10-blog/references/blog-query.md +49 -0
- package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
- package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
- package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
- package/skills/weaver-e10-caizhilian/SKILL.md +216 -0
- package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
- package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
- package/skills/weaver-e10-caizhilian/references/document.md +111 -0
- package/skills/weaver-e10-caizhilian/references/file-collection.md +136 -0
- package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
- package/skills/weaver-e10-caizhilian/references/files.md +120 -0
- package/skills/weaver-e10-caizhilian/references/folder.md +118 -0
- package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
- package/skills/weaver-e10-caizhilian/references/setting-attach.md +410 -0
- package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
- package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
- package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
- package/skills/weaver-e10-caizhilian/references/source-manifest.json +5079 -0
- package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
- package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
- package/skills/weaver-e10-calendar/SKILL.md +4 -3
- package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
- package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
- package/skills/weaver-e10-esb/SKILL.md +2 -2
- package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
- package/skills/weaver-e10-hrm/SKILL.md +54 -6
- package/skills/weaver-e10-hrm/references/employee.md +89 -15
- package/skills/weaver-e10-hrm/references/org.md +7 -4
- package/skills/weaver-e10-hrm/references/position.md +14 -7
- package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
- package/skills/weaver-e10-jinchengda/SKILL.md +166 -0
- package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
- package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
- package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
- package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
- package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
- package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
- package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
- package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
- package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
- package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
- package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
- package/skills/weaver-e10-jucailin/SKILL.md +4 -3
- package/skills/weaver-e10-mail/SKILL.md +4 -11
- package/skills/weaver-e10-meeting/SKILL.md +66 -32
- package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
- package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
- package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
- package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
- package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
- package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
- package/skills/weaver-e10-okr/SKILL.md +6 -11
- package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
- package/skills/weaver-e10-plan/SKILL.md +3 -3
- package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
- package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
- package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
- package/skills/weaver-e10-shared/SKILL.md +36 -25
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +10 -31
- package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
- package/skills/weaver-e10-shijingran/SKILL.md +2 -1
- package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
- package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
- package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
- package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
- package/skills/weaver-e10-workflow/SKILL.md +32 -37
- package/skills/weaver-e10-workflow/references/source-manifest.json +39 -75
- package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
- package/skills/weaver-e10-workflow/references/workflow-create.md +79 -109
- package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
- package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
- package/skills/weaver-e10-workflow/references/workflow-query.md +38 -20
- package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
- package/skills/weaver-e10-workflow/references/workflow-view.md +3 -4
- package/skills/weaver-e10-yepiaotong/SKILL.md +75 -39
- package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +89 -9
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
- package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
- package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
- package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
- package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
- package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
- package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
- package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
- package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
- package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
- package/scripts/sync-connector-skills.mjs +0 -172
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
import { CaizhilianCliError, caizhilianValidationError } from '../errors.js';
|
|
2
|
+
import { caizhilianBusinessMessage, isCaizhilianBusinessSuccess } from '../host.js';
|
|
3
|
+
import { timestampToDateStr } from './shared.js';
|
|
4
|
+
/**
|
|
5
|
+
* 采知连领域 API 组合层:1:1 移植源 Skill `scripts/_plugin_helpers.py` 与
|
|
6
|
+
* `scripts/response_templates.py` 中与业务合约相关的部分
|
|
7
|
+
* (权限校验、文件夹默认值兜底、共享条目解析、文档创建请求体组装、
|
|
8
|
+
* 文档基础字段更新、文档详情响应模板)。
|
|
9
|
+
*
|
|
10
|
+
* 与源的唯一系统性差异是错误传递方式:源用 `(data, err)` 元组回传 PluginOutput,
|
|
11
|
+
* CLI 侧统一抛 CaizhilianCliError,并保留源的 message 文案与 error_type。
|
|
12
|
+
*/
|
|
13
|
+
export function asRecord(value) {
|
|
14
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
15
|
+
}
|
|
16
|
+
export function asList(value) {
|
|
17
|
+
return Array.isArray(value) ? value : [];
|
|
18
|
+
}
|
|
19
|
+
/** 源 get_nested_value(data, "a.b") 的对象版本:路径不存在或类型不符时返回 {}。 */
|
|
20
|
+
export function nestedRecord(value, ...keys) {
|
|
21
|
+
let current = value;
|
|
22
|
+
for (const key of keys) {
|
|
23
|
+
if (!current || typeof current !== 'object' || Array.isArray(current))
|
|
24
|
+
return {};
|
|
25
|
+
current = current[key];
|
|
26
|
+
}
|
|
27
|
+
return asRecord(current);
|
|
28
|
+
}
|
|
29
|
+
/** 源 get_nested_value(data, "data.list") 的列表版本:取不到时返回 []。 */
|
|
30
|
+
export function nestedList(value, ...keys) {
|
|
31
|
+
let current = value;
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
if (!current || typeof current !== 'object')
|
|
34
|
+
return [];
|
|
35
|
+
current = current[key];
|
|
36
|
+
}
|
|
37
|
+
return asList(current);
|
|
38
|
+
}
|
|
39
|
+
/** 源「权限校验请求失败」分支:error_type=permission_check_failed + 固定文案。 */
|
|
40
|
+
function permissionCheckFailedError(message, details, cause) {
|
|
41
|
+
return caizhilianValidationError('permission_check_failed', message, {
|
|
42
|
+
error_type: 'permission_check_failed',
|
|
43
|
+
...details,
|
|
44
|
+
cause: cause instanceof Error ? cause.message : String(cause),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// ── fetch_doc_base_info_by_docid(响应模板 FetchDocBaseInfoByDocid)──────────
|
|
48
|
+
/** 源 response_templates.PERMISSION_FIELDS(18 项白名单)。 */
|
|
49
|
+
export const DOC_PERMISSION_FIELDS = [
|
|
50
|
+
'withEditPermission',
|
|
51
|
+
'canEditTitle',
|
|
52
|
+
'canEditContent',
|
|
53
|
+
'canDelete',
|
|
54
|
+
'watched',
|
|
55
|
+
'canDownload',
|
|
56
|
+
'maxSize',
|
|
57
|
+
'canUploadFile',
|
|
58
|
+
'canMove',
|
|
59
|
+
'canQuote',
|
|
60
|
+
'canEditPrimary',
|
|
61
|
+
'canSaveAsDraft',
|
|
62
|
+
'canEditSharer',
|
|
63
|
+
'canSubmit',
|
|
64
|
+
'canPublish',
|
|
65
|
+
'canFile',
|
|
66
|
+
'canCancel',
|
|
67
|
+
'canWatch',
|
|
68
|
+
];
|
|
69
|
+
/** 源 response_templates.PARENT_FOLDER_FIELDS。 */
|
|
70
|
+
export const PARENT_FOLDER_FIELDS = ['id', 'name', 'type', 'namePath'];
|
|
71
|
+
/** 源 response_templates.ATTACHMENT_FIELDS。 */
|
|
72
|
+
export const ATTACHMENT_FIELDS = ['id', 'name', 'createTime', 'extName', 'size'];
|
|
73
|
+
/** 源 response_templates.OMS_KEEP_FIELDS。 */
|
|
74
|
+
export const OMS_KEEP_FIELDS = ['operateMenu', 'operateMenuId'];
|
|
75
|
+
/** 源 OMS_FILTER_SHOW / OMS_FILTER_OPEN_STATUS。 */
|
|
76
|
+
export const OMS_FILTER_SHOW = true;
|
|
77
|
+
export const OMS_FILTER_OPEN_STATUS = 1;
|
|
78
|
+
function pickPresent(source, keys) {
|
|
79
|
+
const result = {};
|
|
80
|
+
for (const key of keys) {
|
|
81
|
+
if (key in source)
|
|
82
|
+
result[key] = source[key];
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
function omsVisible(item) {
|
|
87
|
+
const record = asRecord(item);
|
|
88
|
+
return record.show === OMS_FILTER_SHOW && record.openStatus === OMS_FILTER_OPEN_STATUS;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* GET /api/doc/document/queryInfo?docId&splitInfo=1 + FetchDocBaseInfoByDocid 模板。
|
|
92
|
+
* 供 caizhilian.document.get 直接使用,也被 replace_document_content / 文档字段修改的
|
|
93
|
+
* prepare 阶段当作只读回查复用(源 replace_document_content 走 engine.execute 复用同一模板)。
|
|
94
|
+
*/
|
|
95
|
+
export async function fetchDocBaseInfo(env, docId, operation) {
|
|
96
|
+
const response = await env.host.request({
|
|
97
|
+
method: 'GET',
|
|
98
|
+
path: '/api/doc/document/queryInfo',
|
|
99
|
+
query: { docId, splitInfo: '1' },
|
|
100
|
+
operation,
|
|
101
|
+
});
|
|
102
|
+
const apiData = nestedRecord(response, 'data');
|
|
103
|
+
const doc = asRecord(apiData.document);
|
|
104
|
+
// 服务端对不存在的文档返回空对象:核心标识(名称/ID)都缺失即判定为文档不存在。
|
|
105
|
+
if (!doc.name && !doc.id) {
|
|
106
|
+
throw caizhilianValidationError('doc_not_found', `文档不存在或已被删除(文档 ID: ${docId})`);
|
|
107
|
+
}
|
|
108
|
+
const result = {};
|
|
109
|
+
// 1. 文档基础字段
|
|
110
|
+
const author = asRecord(doc.author);
|
|
111
|
+
const creator = asRecord(doc.creator);
|
|
112
|
+
result.docName = doc.name ?? null;
|
|
113
|
+
result.abstractText = doc.abstractText ?? null;
|
|
114
|
+
result.authorId = author.id ?? null;
|
|
115
|
+
result.authorName = author.username ?? null;
|
|
116
|
+
result.creatorId = creator.id ?? null;
|
|
117
|
+
result.creatorName = creator.username ?? null;
|
|
118
|
+
result.docStatus = doc.docStatus ?? null;
|
|
119
|
+
result.keywords = doc.keywords ?? null;
|
|
120
|
+
result.summary = doc.summary ?? null;
|
|
121
|
+
result.createTime = timestampToDateStr(doc.createTime);
|
|
122
|
+
result.lastUpdateTime = timestampToDateStr(doc.lastUpdateTime);
|
|
123
|
+
result.contentMainType = doc.contentMainType ?? null;
|
|
124
|
+
result.content = doc.content ?? null;
|
|
125
|
+
result.contentType = doc.contentType ?? null;
|
|
126
|
+
result.coordinate = doc.coordinate ?? null;
|
|
127
|
+
// 2. 父文件夹列表(仅保留 id/name/type/namePath)
|
|
128
|
+
result.parentFolders = asList(doc.parentFolders)
|
|
129
|
+
.filter((item) => !!item && typeof item === 'object' && !Array.isArray(item))
|
|
130
|
+
.map((item) => pickPresent(asRecord(item), PARENT_FOLDER_FIELDS));
|
|
131
|
+
// 3. 文档权限标志(18 字段白名单)
|
|
132
|
+
const docPermission = asRecord(apiData.documentPermission);
|
|
133
|
+
const permission = {};
|
|
134
|
+
for (const key of DOC_PERMISSION_FIELDS) {
|
|
135
|
+
if (key in docPermission)
|
|
136
|
+
permission[key] = docPermission[key];
|
|
137
|
+
}
|
|
138
|
+
// 4. 附件列表(仅保留 id/name/createTime/extName/size)
|
|
139
|
+
result.attachments = asList(doc.attachments)
|
|
140
|
+
.filter((item) => !!item && typeof item === 'object' && !Array.isArray(item))
|
|
141
|
+
.map((item) => pickPresent(asRecord(item), ATTACHMENT_FIELDS));
|
|
142
|
+
// 5. operateMenuSettings:show===true 且 openStatus===1,仅保留 operateMenu/operateMenuId
|
|
143
|
+
const oms = asList(apiData.operateMenuSettings);
|
|
144
|
+
result.operateMenuSettings = oms
|
|
145
|
+
.filter((item) => !!item && typeof item === 'object' && !Array.isArray(item))
|
|
146
|
+
.filter(omsVisible)
|
|
147
|
+
.map((item) => pickPresent(asRecord(item), OMS_KEEP_FIELDS));
|
|
148
|
+
// 6. 派生 replaceMainText 权限
|
|
149
|
+
permission.replaceMainText = oms.some((item) => omsVisible(item) && asRecord(item).operateMenuId === 'replaceMainText');
|
|
150
|
+
result.permission = permission;
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
// ── 当前登录人(源 fetch_current_user_info / fetch_current_user_info_cached)──
|
|
154
|
+
/** GET /api/doc/common/userInfo,返回原始响应体(源缓存版返回的也是原始响应体)。 */
|
|
155
|
+
export async function fetchCurrentUserInfoRaw(env, operation) {
|
|
156
|
+
return env.host.request({ method: 'GET', path: '/api/doc/common/userInfo', operation });
|
|
157
|
+
}
|
|
158
|
+
/** caizhilian.user.current 的输出:employeeId / employeeName。 */
|
|
159
|
+
export async function fetchCurrentUserInfo(env, operation) {
|
|
160
|
+
const response = await fetchCurrentUserInfoRaw(env, operation);
|
|
161
|
+
const employee = nestedRecord(response, 'data', 'employee');
|
|
162
|
+
return {
|
|
163
|
+
employeeId: employee.id === undefined || employee.id === null ? '' : String(employee.id),
|
|
164
|
+
employeeName: employee.name === undefined || employee.name === null ? '' : String(employee.name),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// ── 权限校验(源 fetch_folder_permissions / check_*)────────────────────────
|
|
168
|
+
/**
|
|
169
|
+
* POST /api/doc/folderpermission/querySpecifiedPermission。
|
|
170
|
+
* 返回 { [folderId]: { <field>: boolean, allowDocumentCreateTypes?: string[] } }。
|
|
171
|
+
*/
|
|
172
|
+
export async function fetchFolderPermissions(env, folderIds, permissionFields, operation) {
|
|
173
|
+
const fields = [...(permissionFields ?? [])];
|
|
174
|
+
if (!fields.length) {
|
|
175
|
+
throw caizhilianValidationError('permission_fields_required', '权限查询字段列表不能为空');
|
|
176
|
+
}
|
|
177
|
+
const ids = folderIds.map((item) => String(item));
|
|
178
|
+
const response = await env.host.request({
|
|
179
|
+
method: 'POST',
|
|
180
|
+
path: '/api/doc/folderpermission/querySpecifiedPermission',
|
|
181
|
+
query: { folderIdStr: ids.join(','), typeStr: fields.join(',') },
|
|
182
|
+
operation,
|
|
183
|
+
});
|
|
184
|
+
const resultMap = asRecord(nestedRecord(response, 'data').resultMap);
|
|
185
|
+
const permissionsMap = {};
|
|
186
|
+
for (const folderId of ids) {
|
|
187
|
+
const rawPerms = asRecord(resultMap[folderId]);
|
|
188
|
+
const perms = {};
|
|
189
|
+
for (const field of fields) {
|
|
190
|
+
if (field === 'documentCreateSetting') {
|
|
191
|
+
perms.allowDocumentCreateTypes = asList(asRecord(rawPerms.documentCreateSetting).allowDocumentCreateTypes);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
perms[field] = rawPerms[field] ?? false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
permissionsMap[folderId] = perms;
|
|
198
|
+
}
|
|
199
|
+
return permissionsMap;
|
|
200
|
+
}
|
|
201
|
+
function folderIdText(value) {
|
|
202
|
+
return value === undefined || value === null ? '' : String(value).trim();
|
|
203
|
+
}
|
|
204
|
+
/** 源 check_create_document_permission:isCreateDoc + 允许创建的文档类型。 */
|
|
205
|
+
export async function checkCreateDocumentPermission(env, folderId, docType, operation) {
|
|
206
|
+
const id = folderIdText(folderId);
|
|
207
|
+
if (!id)
|
|
208
|
+
return;
|
|
209
|
+
let perms;
|
|
210
|
+
try {
|
|
211
|
+
const map = await fetchFolderPermissions(env, [id], ['isCreateDoc', 'documentCreateSetting'], operation);
|
|
212
|
+
perms = map[id] ?? {};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
throw permissionCheckFailedError('无法校验文件夹权限,请稍后重试', { folder_id: id }, error);
|
|
216
|
+
}
|
|
217
|
+
if (!perms.isCreateDoc) {
|
|
218
|
+
throw caizhilianValidationError('folder_no_create_permission', '该文件夹没有新建文档权限', {
|
|
219
|
+
error_type: 'folder_no_create_permission',
|
|
220
|
+
folder_id: id,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (docType) {
|
|
224
|
+
const allowedTypes = asList(perms.allowDocumentCreateTypes);
|
|
225
|
+
const matched = allowedTypes.some((item) => typeof item === 'string' && item.toLowerCase() === docType.toLowerCase());
|
|
226
|
+
if (allowedTypes.length && !matched) {
|
|
227
|
+
throw caizhilianValidationError('doc_type_not_allowed', `该文件夹不支持创建 ${docType} 类型文档,支持的类型:${allowedTypes.map((item) => String(item)).join(', ')}`, { error_type: 'doc_type_not_allowed', requested_type: docType, allowed_types: allowedTypes });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/** 源 check_create_folder_permission:isCreateFolder。 */
|
|
232
|
+
export async function checkCreateFolderPermission(env, folderId, operation) {
|
|
233
|
+
const id = folderIdText(folderId);
|
|
234
|
+
if (!id)
|
|
235
|
+
return;
|
|
236
|
+
let perms;
|
|
237
|
+
try {
|
|
238
|
+
const map = await fetchFolderPermissions(env, [id], ['isCreateFolder'], operation);
|
|
239
|
+
perms = map[id] ?? {};
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
throw permissionCheckFailedError('无法校验文件夹权限,请稍后重试', { folder_id: id }, error);
|
|
243
|
+
}
|
|
244
|
+
if (!perms.isCreateFolder) {
|
|
245
|
+
throw caizhilianValidationError('folder_no_create_folder_permission', '该文件夹没有新建文件夹权限', {
|
|
246
|
+
error_type: 'folder_no_create_folder_permission',
|
|
247
|
+
folder_id: id,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** 源 check_set_folder_permission:isSetting。 */
|
|
252
|
+
export async function checkSetFolderPermission(env, folderId, operation) {
|
|
253
|
+
const id = folderIdText(folderId);
|
|
254
|
+
if (!id)
|
|
255
|
+
return;
|
|
256
|
+
let perms;
|
|
257
|
+
try {
|
|
258
|
+
const map = await fetchFolderPermissions(env, [id], ['isSetting'], operation);
|
|
259
|
+
perms = map[id] ?? {};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
throw permissionCheckFailedError('无法校验文件夹权限,请稍后重试', { folder_id: id }, error);
|
|
263
|
+
}
|
|
264
|
+
if (!perms.isSetting) {
|
|
265
|
+
throw caizhilianValidationError('folder_no_set_default_share_permission', '该文件夹没有设置默认共享权限', {
|
|
266
|
+
error_type: 'folder_no_set_default_share_permission',
|
|
267
|
+
folder_id: id,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/** 源 check_doc_share_add_permission:shareentry/list 的 addVisible。 */
|
|
272
|
+
export async function checkDocShareAddPermission(env, docId, operation) {
|
|
273
|
+
const id = folderIdText(docId);
|
|
274
|
+
if (!id)
|
|
275
|
+
return;
|
|
276
|
+
let inner;
|
|
277
|
+
try {
|
|
278
|
+
const response = await env.host.request({
|
|
279
|
+
method: 'GET',
|
|
280
|
+
path: '/api/doc/shareentry/list',
|
|
281
|
+
query: { sourceId: id, permissionId: 'doc_s01', sourceType: '1', docId: id },
|
|
282
|
+
operation,
|
|
283
|
+
});
|
|
284
|
+
inner = nestedRecord(response, 'data');
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
throw permissionCheckFailedError('无法校验文档共享权限,请稍后重试', { doc_id: id }, error);
|
|
288
|
+
}
|
|
289
|
+
if (!inner.addVisible) {
|
|
290
|
+
throw caizhilianValidationError('doc_no_add_share_permission', '该文档没有添加共享权限', {
|
|
291
|
+
error_type: 'doc_no_add_share_permission',
|
|
292
|
+
doc_id: id,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// ── 文件夹默认值兜底(源 apply_folder_default_fallbacks)───────────────────
|
|
297
|
+
/** 源 _plugin_helpers.FOLDER_DEFAULT_FALLBACK_MAP。 */
|
|
298
|
+
export const FOLDER_DEFAULT_FALLBACK_MAP = [
|
|
299
|
+
{ param: 'virtual_folder_ids', source: 'defaultVirtualFolder', field: 'id' },
|
|
300
|
+
{ param: 'participant_ids', source: 'folderDefaultParticipants', field: 'id' },
|
|
301
|
+
];
|
|
302
|
+
/** 源 _plugin_helpers.FOLDER_DEFAULT_SETTINGS_TYPE。 */
|
|
303
|
+
export const FOLDER_DEFAULT_SETTINGS_TYPE = 'folderDefaultShare,folderDefaultParticipants,defaultVirtualFolder';
|
|
304
|
+
/**
|
|
305
|
+
* 用文件夹默认设置补齐未传参数(participant_ids / virtual_folder_ids),
|
|
306
|
+
* 并附带 folder_default_share_new(去 avatar)。
|
|
307
|
+
* 源语义:查询失败静默返回原参数副本,不阻断创建流程。
|
|
308
|
+
*/
|
|
309
|
+
export async function applyFolderDefaultFallbacks(env, args, operation) {
|
|
310
|
+
const result = { ...args };
|
|
311
|
+
const forceQueryShareNew = Boolean(args.force_query_folder_default_share_new);
|
|
312
|
+
const missing = FOLDER_DEFAULT_FALLBACK_MAP.filter((item) => !args[item.param]).map((item) => item.param);
|
|
313
|
+
if (!missing.length && !forceQueryShareNew)
|
|
314
|
+
return result;
|
|
315
|
+
const folderId = folderIdText(args.folder_id);
|
|
316
|
+
if (!folderId)
|
|
317
|
+
return result;
|
|
318
|
+
let dataSection;
|
|
319
|
+
try {
|
|
320
|
+
const response = await env.host.request({
|
|
321
|
+
method: 'POST',
|
|
322
|
+
path: '/api/doc/folderSetting/getFolderSettings',
|
|
323
|
+
body: { id: folderId, type: FOLDER_DEFAULT_SETTINGS_TYPE },
|
|
324
|
+
operation,
|
|
325
|
+
});
|
|
326
|
+
dataSection = nestedRecord(response, 'data');
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
for (const item of FOLDER_DEFAULT_FALLBACK_MAP) {
|
|
332
|
+
if (!missing.includes(item.param))
|
|
333
|
+
continue;
|
|
334
|
+
const entries = dataSection[item.source];
|
|
335
|
+
if (!Array.isArray(entries))
|
|
336
|
+
continue;
|
|
337
|
+
const values = entries
|
|
338
|
+
.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry))
|
|
339
|
+
.map((entry) => asRecord(entry)[item.field])
|
|
340
|
+
.filter((value) => value !== undefined && value !== null)
|
|
341
|
+
.map((value) => String(value));
|
|
342
|
+
if (values.length)
|
|
343
|
+
result[item.param] = values;
|
|
344
|
+
}
|
|
345
|
+
const shareNew = dataSection.folderDefaultShareNew;
|
|
346
|
+
if (Array.isArray(shareNew)) {
|
|
347
|
+
const cleaned = shareNew
|
|
348
|
+
.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry))
|
|
349
|
+
.map((entry) => {
|
|
350
|
+
const copy = { ...asRecord(entry) };
|
|
351
|
+
delete copy.avatar;
|
|
352
|
+
return copy;
|
|
353
|
+
});
|
|
354
|
+
if (cleaned.length)
|
|
355
|
+
result.folder_default_share_new = cleaned;
|
|
356
|
+
}
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* 查询目标文件夹的文档默认共享,用于新建文档请求体的 docShareList(源 apply_doc_share_fallback)。
|
|
361
|
+
* 直接取原始条目(仅去掉 objectAction),因为创建接口需要原始格式;查询失败静默。
|
|
362
|
+
*/
|
|
363
|
+
export async function applyDocShareFallback(env, args, operation) {
|
|
364
|
+
const result = { ...args };
|
|
365
|
+
const folderId = folderIdText(args.folder_id);
|
|
366
|
+
if (!folderId)
|
|
367
|
+
return result;
|
|
368
|
+
let entries;
|
|
369
|
+
try {
|
|
370
|
+
const response = await env.host.request({
|
|
371
|
+
method: 'GET',
|
|
372
|
+
path: '/api/doc/shareentry/list',
|
|
373
|
+
query: { folderId },
|
|
374
|
+
operation,
|
|
375
|
+
});
|
|
376
|
+
entries = nestedRecord(response, 'data').data;
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
if (!Array.isArray(entries))
|
|
382
|
+
return result;
|
|
383
|
+
const shares = entries
|
|
384
|
+
.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry))
|
|
385
|
+
.map((entry) => {
|
|
386
|
+
const copy = { ...asRecord(entry) };
|
|
387
|
+
delete copy.objectAction;
|
|
388
|
+
return copy;
|
|
389
|
+
});
|
|
390
|
+
if (shares.length)
|
|
391
|
+
result.doc_share_list = shares;
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* 并发执行文档创建前置:权限校验(失败即错)+ 当前用户信息(可选)+ 文件夹默认值兜底 + 默认共享兜底。
|
|
396
|
+
*
|
|
397
|
+
* ⚠️ 1:1 保留源行为:当 includeShareFallback=true 时,`new_args` 取「共享兜底」的返回值,
|
|
398
|
+
* 「文件夹默认值兜底」的结果被丢弃(源 _plugin_helpers.run_create_preamble 的覆盖写法),
|
|
399
|
+
* 因此 create-html / create-markdown / create-office 实际不会回填 participant_ids /
|
|
400
|
+
* virtual_folder_ids;只有 create-by-upload(includeShareFallback=false)会生效。
|
|
401
|
+
*/
|
|
402
|
+
export async function runCreatePreamble(env, args, options) {
|
|
403
|
+
const includeShareFallback = options.includeShareFallback ?? true;
|
|
404
|
+
const folderId = folderIdText(args.folder_id);
|
|
405
|
+
const fallbackArgs = { ...args };
|
|
406
|
+
if (options.forceQueryShareNew)
|
|
407
|
+
fallbackArgs.force_query_folder_default_share_new = true;
|
|
408
|
+
const tasks = [
|
|
409
|
+
checkCreateDocumentPermission(env, folderId, options.docType, options.operation),
|
|
410
|
+
];
|
|
411
|
+
if (options.needUserInfo)
|
|
412
|
+
tasks.push(fetchCurrentUserInfoRaw(env, options.operation));
|
|
413
|
+
tasks.push(applyFolderDefaultFallbacks(env, fallbackArgs, options.operation));
|
|
414
|
+
if (includeShareFallback)
|
|
415
|
+
tasks.push(applyDocShareFallback(env, fallbackArgs, options.operation));
|
|
416
|
+
const settled = await Promise.allSettled(tasks);
|
|
417
|
+
let index = 0;
|
|
418
|
+
const permission = settled[index];
|
|
419
|
+
index += 1;
|
|
420
|
+
if (permission.status === 'rejected')
|
|
421
|
+
throw permission.reason;
|
|
422
|
+
let employeeId;
|
|
423
|
+
if (options.needUserInfo) {
|
|
424
|
+
const user = settled[index];
|
|
425
|
+
index += 1;
|
|
426
|
+
if (user.status === 'rejected')
|
|
427
|
+
throw user.reason;
|
|
428
|
+
employeeId = String(nestedRecord(user.value, 'data', 'employee').id ?? '');
|
|
429
|
+
}
|
|
430
|
+
const folderFallback = settled[index];
|
|
431
|
+
index += 1;
|
|
432
|
+
if (folderFallback.status === 'rejected')
|
|
433
|
+
throw folderFallback.reason;
|
|
434
|
+
const folderFallbackArgs = folderFallback.value;
|
|
435
|
+
let newArgs = folderFallbackArgs;
|
|
436
|
+
if (includeShareFallback) {
|
|
437
|
+
const shareFallback = settled[index];
|
|
438
|
+
index += 1;
|
|
439
|
+
if (shareFallback.status === 'rejected')
|
|
440
|
+
throw shareFallback.reason;
|
|
441
|
+
// ⚠️ 源行为:共享兜底结果覆盖文件夹兜底结果。
|
|
442
|
+
newArgs = shareFallback.value;
|
|
443
|
+
}
|
|
444
|
+
if (options.needUserInfo && !employeeId) {
|
|
445
|
+
throw caizhilianValidationError('user_info_unavailable', '未传 author_id 且无法获取当前用户信息');
|
|
446
|
+
}
|
|
447
|
+
return { args: newArgs, employeeId, folderFallbackArgs };
|
|
448
|
+
}
|
|
449
|
+
// ── 文档创建请求体(源 build_create_document_body)─────────────────────────
|
|
450
|
+
/** 组装 POST /api/doc/document/createDetail 请求体。 */
|
|
451
|
+
export function buildCreateDocumentBody(args, contentType) {
|
|
452
|
+
const document = {
|
|
453
|
+
contentType,
|
|
454
|
+
name: args.name,
|
|
455
|
+
author: { id: String(args.author_id) },
|
|
456
|
+
docStatusName: '草稿',
|
|
457
|
+
employeePartTimeId: '',
|
|
458
|
+
};
|
|
459
|
+
if (args.summary)
|
|
460
|
+
document.summary = args.summary;
|
|
461
|
+
if (args.abstract_text)
|
|
462
|
+
document.abstractText = args.abstract_text;
|
|
463
|
+
if (args.keywords)
|
|
464
|
+
document.keywords = args.keywords;
|
|
465
|
+
if (args.auto_invalidate_date)
|
|
466
|
+
document.autoInvalidateDate = args.auto_invalidate_date;
|
|
467
|
+
if (args.cover_picture_id)
|
|
468
|
+
document.coverPictureId = String(args.cover_picture_id);
|
|
469
|
+
if (args.doc_category !== undefined && args.doc_category !== null) {
|
|
470
|
+
const parsed = Number(args.doc_category);
|
|
471
|
+
if (!Number.isInteger(parsed)) {
|
|
472
|
+
throw caizhilianValidationError('doc_category_invalid', 'doc_category 必须是整数');
|
|
473
|
+
}
|
|
474
|
+
document.docCategory = parsed;
|
|
475
|
+
}
|
|
476
|
+
if (args.tags)
|
|
477
|
+
document.tags = args.tags;
|
|
478
|
+
if (args.virtual_folder_ids) {
|
|
479
|
+
document.virtualFolders = asList(args.virtual_folder_ids).map((id) => ({ id: String(id) }));
|
|
480
|
+
}
|
|
481
|
+
const body = {
|
|
482
|
+
document,
|
|
483
|
+
operateType: 'save',
|
|
484
|
+
folderId: String(args.folder_id),
|
|
485
|
+
folderType: String(args.folder_type),
|
|
486
|
+
mainlineId: '',
|
|
487
|
+
moduleFolderId: '',
|
|
488
|
+
effectiveContent: true,
|
|
489
|
+
};
|
|
490
|
+
if (args.participant_ids) {
|
|
491
|
+
body.shareList = asList(args.participant_ids).map((pid) => ({
|
|
492
|
+
entryType: 'user',
|
|
493
|
+
granting: true,
|
|
494
|
+
mask: 3,
|
|
495
|
+
shareType: 'participants',
|
|
496
|
+
sid: String(pid),
|
|
497
|
+
}));
|
|
498
|
+
}
|
|
499
|
+
if (args.doc_share_list)
|
|
500
|
+
body.docShareList = args.doc_share_list;
|
|
501
|
+
return body;
|
|
502
|
+
}
|
|
503
|
+
/** POST /api/doc/document/update/{docId},返回 { doc_id, [argKey]: value }。 */
|
|
504
|
+
export async function updateDocBasicField(env, input, operation) {
|
|
505
|
+
const docId = folderIdText(input.docId);
|
|
506
|
+
if (!docId)
|
|
507
|
+
throw caizhilianValidationError('field_required', '缺少必填参数: doc_id');
|
|
508
|
+
const raw = input.fixedValue !== undefined ? input.fixedValue : input.value ?? '';
|
|
509
|
+
const value = raw === null || raw === undefined ? '' : raw;
|
|
510
|
+
if (!input.allowEmpty && !String(value).trim()) {
|
|
511
|
+
throw caizhilianValidationError('field_empty', `${input.argKey} 不能为空字符串`);
|
|
512
|
+
}
|
|
513
|
+
await env.host.request({
|
|
514
|
+
method: 'POST',
|
|
515
|
+
path: `/api/doc/document/update/${encodeURIComponent(docId)}`,
|
|
516
|
+
body: { id: docId, filed: input.filed, document: { [input.docField]: value, shareOut: false } },
|
|
517
|
+
operation,
|
|
518
|
+
});
|
|
519
|
+
return { doc_id: docId, [input.argKey]: value };
|
|
520
|
+
}
|
|
521
|
+
/** 源 object_name 提取顺序:objectAction → fullObject → objectOnly → object。 */
|
|
522
|
+
function shareObjectName(item) {
|
|
523
|
+
let objectName = '';
|
|
524
|
+
const objectAction = item.objectAction;
|
|
525
|
+
if (Array.isArray(objectAction)) {
|
|
526
|
+
const parts = [];
|
|
527
|
+
for (const entry of objectAction) {
|
|
528
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
529
|
+
continue;
|
|
530
|
+
const nameValue = asRecord(entry).name;
|
|
531
|
+
if (nameValue && typeof nameValue === 'object' && !Array.isArray(nameValue)) {
|
|
532
|
+
const part = asRecord(nameValue).linkName || asRecord(nameValue).name || '';
|
|
533
|
+
if (part)
|
|
534
|
+
parts.push(String(part));
|
|
535
|
+
}
|
|
536
|
+
else if (typeof nameValue === 'string' && nameValue) {
|
|
537
|
+
parts.push(nameValue);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
objectName = parts.join('');
|
|
541
|
+
}
|
|
542
|
+
else if (objectAction && typeof objectAction === 'object') {
|
|
543
|
+
const names = asRecord(objectAction).name || asRecord(objectAction).names || [];
|
|
544
|
+
objectName = Array.isArray(names) ? names.map((name) => String(name)).join(', ') : String(names);
|
|
545
|
+
}
|
|
546
|
+
if (!objectName) {
|
|
547
|
+
objectName = String(item.fullObject ?? '') || String(item.objectOnly ?? '') || String(item.object ?? '') || '';
|
|
548
|
+
}
|
|
549
|
+
return objectName;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* 解析共享条目列表(文档共享 / 文件夹默认共享 / 文件夹权限设置共用)。
|
|
553
|
+
* can_delete = operates 中 id=='delete' && visible 的索引 → operatesPermission[idx][deleteIdx].visible。
|
|
554
|
+
*/
|
|
555
|
+
export function parseShareItems(items, operates, operatesPermission, options = {}) {
|
|
556
|
+
let deleteOpIndex = null;
|
|
557
|
+
if (options.includeCanDelete) {
|
|
558
|
+
for (let i = 0; i < operates.length; i += 1) {
|
|
559
|
+
const op = asRecord(operates[i]);
|
|
560
|
+
if (operates[i] && typeof operates[i] === 'object' && op.id === 'delete' && op.visible) {
|
|
561
|
+
deleteOpIndex = i;
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const shares = [];
|
|
567
|
+
for (let idx = 0; idx < items.length; idx += 1) {
|
|
568
|
+
const raw = items[idx];
|
|
569
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
570
|
+
continue;
|
|
571
|
+
const item = asRecord(raw);
|
|
572
|
+
const secLevel = item.secLevel;
|
|
573
|
+
const share = {
|
|
574
|
+
id: String(item.id ?? ''),
|
|
575
|
+
object_type: item.objectType ?? '',
|
|
576
|
+
object_name: shareObjectName(item),
|
|
577
|
+
share_level: item.shareLevel ?? '',
|
|
578
|
+
sec_level: secLevel !== undefined && secLevel !== null && secLevel !== '' ? secLevel : '--',
|
|
579
|
+
};
|
|
580
|
+
if (options.includeDownload)
|
|
581
|
+
share.download = item.download ?? '';
|
|
582
|
+
if (options.includeCanDelete) {
|
|
583
|
+
let canDelete = false;
|
|
584
|
+
if (deleteOpIndex !== null && idx < operatesPermission.length) {
|
|
585
|
+
const permList = operatesPermission[idx];
|
|
586
|
+
if (Array.isArray(permList) && deleteOpIndex < permList.length) {
|
|
587
|
+
const permItem = asRecord(permList[deleteOpIndex]);
|
|
588
|
+
if (Array.isArray(permList) && permList[deleteOpIndex] && typeof permList[deleteOpIndex] === 'object' && permItem.visible) {
|
|
589
|
+
canDelete = true;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
share.can_delete = canDelete;
|
|
594
|
+
}
|
|
595
|
+
shares.push(share);
|
|
596
|
+
}
|
|
597
|
+
return shares;
|
|
598
|
+
}
|
|
599
|
+
function shareEntryParts(response) {
|
|
600
|
+
const inner = nestedRecord(response, 'data');
|
|
601
|
+
return {
|
|
602
|
+
inner,
|
|
603
|
+
items: asList(inner.data),
|
|
604
|
+
operates: asList(inner.operates),
|
|
605
|
+
operatesPermission: asList(inner.operatesPermission),
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/** 源 fetch_doc_share_entries:GET /api/doc/shareentry/list(含 download + can_delete)。 */
|
|
609
|
+
export async function fetchDocShareEntries(env, docId, operation) {
|
|
610
|
+
const id = folderIdText(docId);
|
|
611
|
+
if (!id)
|
|
612
|
+
throw caizhilianValidationError('field_empty', 'doc_id 不能为空');
|
|
613
|
+
const response = await env.host.request({
|
|
614
|
+
method: 'GET',
|
|
615
|
+
path: '/api/doc/shareentry/list',
|
|
616
|
+
query: { sourceId: id, permissionId: 'doc_s01', sourceType: '1', docId: id },
|
|
617
|
+
operation,
|
|
618
|
+
});
|
|
619
|
+
const parts = shareEntryParts(response);
|
|
620
|
+
return {
|
|
621
|
+
add_visible: Boolean(parts.inner.addVisible),
|
|
622
|
+
shares: parseShareItems(parts.items, parts.operates, parts.operatesPermission, {
|
|
623
|
+
includeDownload: true,
|
|
624
|
+
includeCanDelete: true,
|
|
625
|
+
}),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
/** 源 fetch_folder_default_share_entries:GET /api/doc/shareentry/list?folderId(解析后格式)。 */
|
|
629
|
+
export async function fetchFolderDefaultShareEntries(env, folderId, operation) {
|
|
630
|
+
const id = folderIdText(folderId);
|
|
631
|
+
if (!id)
|
|
632
|
+
throw caizhilianValidationError('field_empty', 'folder_id 不能为空');
|
|
633
|
+
const response = await env.host.request({
|
|
634
|
+
method: 'GET',
|
|
635
|
+
path: '/api/doc/shareentry/list',
|
|
636
|
+
query: { folderId: id },
|
|
637
|
+
operation,
|
|
638
|
+
});
|
|
639
|
+
const parts = shareEntryParts(response);
|
|
640
|
+
return { shares: parseShareItems(parts.items, parts.operates, parts.operatesPermission) };
|
|
641
|
+
}
|
|
642
|
+
/** 源 fetch_folder_permission_entries:GET /api/doc/folderSetting/share/list。 */
|
|
643
|
+
export async function fetchFolderPermissionEntries(env, folderId, permissionType, operation) {
|
|
644
|
+
const id = folderIdText(folderId);
|
|
645
|
+
if (!id)
|
|
646
|
+
throw caizhilianValidationError('field_empty', 'folder_id 不能为空');
|
|
647
|
+
const response = await env.host.request({
|
|
648
|
+
method: 'GET',
|
|
649
|
+
path: '/api/doc/folderSetting/share/list',
|
|
650
|
+
query: {
|
|
651
|
+
sourceId: id,
|
|
652
|
+
permissionId: 'doc_s01',
|
|
653
|
+
sourceType: '1',
|
|
654
|
+
folderId: id,
|
|
655
|
+
docSettingType: permissionType,
|
|
656
|
+
isTemplate: 'false',
|
|
657
|
+
},
|
|
658
|
+
operation,
|
|
659
|
+
});
|
|
660
|
+
const parts = shareEntryParts(response);
|
|
661
|
+
return {
|
|
662
|
+
add_visible: Boolean(parts.inner.addVisible),
|
|
663
|
+
shares: parseShareItems(parts.items, parts.operates, parts.operatesPermission),
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
// ── 文件夹权限设置(源 _build_folder_permission_add_body 等)───────────────
|
|
667
|
+
/** 源 _plugin_helpers._FOLDER_SETTING_TARGET_TYPE。 */
|
|
668
|
+
export const FOLDER_SETTING_TARGET_TYPE = {
|
|
669
|
+
resource: 1,
|
|
670
|
+
department: 3,
|
|
671
|
+
subcompany: 6,
|
|
672
|
+
role: 4,
|
|
673
|
+
position: 5,
|
|
674
|
+
group: 2,
|
|
675
|
+
customer: 7,
|
|
676
|
+
customer_type: 8,
|
|
677
|
+
};
|
|
678
|
+
/** 源 _build_folder_permission_add_body 的可选级联参数。 */
|
|
679
|
+
const PERMISSION_OPTIONAL_PARAMS = ['department_attribute', 'role_level', 'position_level', 'position_attribute'];
|
|
680
|
+
/** 源 _build_folder_permission_add_body 的 obj_id 系列字段(顺序影响 targetId 取值)。 */
|
|
681
|
+
const PERMISSION_ID_FIELDS = [
|
|
682
|
+
'obj_id',
|
|
683
|
+
'obj_id_department',
|
|
684
|
+
'obj_id_subcompany',
|
|
685
|
+
'obj_id_role',
|
|
686
|
+
'obj_id_group',
|
|
687
|
+
'obj_id_position',
|
|
688
|
+
'obj_id_customer',
|
|
689
|
+
'obj_id_customer_type',
|
|
690
|
+
'position_level_department',
|
|
691
|
+
'position_level_subcompany',
|
|
692
|
+
];
|
|
693
|
+
/** 构建文件夹文档权限设置 add 请求体。 */
|
|
694
|
+
export function buildFolderPermissionAddBody(folderId, permissionType, objType, args) {
|
|
695
|
+
const body = {
|
|
696
|
+
sourceId: folderId,
|
|
697
|
+
permissionId: 'doc_s01',
|
|
698
|
+
sourceType: 1,
|
|
699
|
+
folderId,
|
|
700
|
+
docSettingType: permissionType,
|
|
701
|
+
isTemplate: false,
|
|
702
|
+
objType,
|
|
703
|
+
};
|
|
704
|
+
for (const key of PERMISSION_OPTIONAL_PARAMS) {
|
|
705
|
+
if (key in args)
|
|
706
|
+
body[key] = args[key];
|
|
707
|
+
}
|
|
708
|
+
let targetId = null;
|
|
709
|
+
for (const field of PERMISSION_ID_FIELDS) {
|
|
710
|
+
if (!(field in args))
|
|
711
|
+
continue;
|
|
712
|
+
const value = args[field];
|
|
713
|
+
if (Array.isArray(value)) {
|
|
714
|
+
body[field] = value;
|
|
715
|
+
if (value.length)
|
|
716
|
+
targetId = String(value[0]);
|
|
717
|
+
}
|
|
718
|
+
else if (typeof value === 'string' && value) {
|
|
719
|
+
body[field] = [value];
|
|
720
|
+
targetId = value;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
const targetType = FOLDER_SETTING_TARGET_TYPE[objType];
|
|
724
|
+
if (targetType !== undefined)
|
|
725
|
+
body.targetType = targetType;
|
|
726
|
+
if (targetId)
|
|
727
|
+
body.targetId = targetId;
|
|
728
|
+
return body;
|
|
729
|
+
}
|
|
730
|
+
/** 源 fetch_folder_permission_auth:GET /api/doc/folderBackstageSetting/docPermission。 */
|
|
731
|
+
export async function fetchFolderPermissionAuth(env, folderId, permissionType, operation) {
|
|
732
|
+
const id = folderIdText(folderId);
|
|
733
|
+
if (!id)
|
|
734
|
+
throw caizhilianValidationError('field_empty', 'folder_id 不能为空');
|
|
735
|
+
const response = await env.host.request({
|
|
736
|
+
method: 'GET',
|
|
737
|
+
path: '/api/doc/folderBackstageSetting/docPermission',
|
|
738
|
+
query: { folderId: id, type: permissionType, isTemplate: 'false' },
|
|
739
|
+
operation,
|
|
740
|
+
});
|
|
741
|
+
const inner = nestedRecord(response, 'data');
|
|
742
|
+
// 响应字段名随 permission_type 变化(downloadAuth / deleteAuth ...),源取第一个值。
|
|
743
|
+
for (const value of Object.values(inner)) {
|
|
744
|
+
if (typeof value === 'boolean')
|
|
745
|
+
return String(value).toLowerCase();
|
|
746
|
+
if (value !== undefined && value !== null)
|
|
747
|
+
return String(value);
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
/** 源 update_folder_permission_auth 的 _VALUE_FIELD_MAP。 */
|
|
753
|
+
export const PERMISSION_AUTH_VALUE_FIELD_MAP = {
|
|
754
|
+
downloadPremisstion: 'folderDocIsDown',
|
|
755
|
+
deletePremisstion: 'folderDocIsDelete',
|
|
756
|
+
settingPermissionIncludeMaintainPermission: 'settingPermissionIncludeMaintainPermission',
|
|
757
|
+
};
|
|
758
|
+
/** 源 PERMISSION_AUTH 取值范围。 */
|
|
759
|
+
export const PERMISSION_AUTH_VALUES = ['true', 'user', 'false'];
|
|
760
|
+
/** POST /api/doc/folderBackstageSetting/docPermission(设置文件夹权限类型值)。 */
|
|
761
|
+
export async function updateFolderPermissionAuth(env, folderId, permissionType, value, operation) {
|
|
762
|
+
const id = folderIdText(folderId);
|
|
763
|
+
if (!id)
|
|
764
|
+
throw caizhilianValidationError('field_empty', 'folder_id 不能为空');
|
|
765
|
+
if (!PERMISSION_AUTH_VALUES.includes(value)) {
|
|
766
|
+
throw caizhilianValidationError('enum_invalid', `value 必须为 true / user / false,收到: ${JSON.stringify(value)}`);
|
|
767
|
+
}
|
|
768
|
+
const valueField = PERMISSION_AUTH_VALUE_FIELD_MAP[permissionType] ?? 'folderDocIsDown';
|
|
769
|
+
await env.host.request({
|
|
770
|
+
method: 'POST',
|
|
771
|
+
path: '/api/doc/folderBackstageSetting/docPermission',
|
|
772
|
+
body: {
|
|
773
|
+
folderId: id,
|
|
774
|
+
permissionItemName: permissionType,
|
|
775
|
+
isTemplate: false,
|
|
776
|
+
[valueField]: value,
|
|
777
|
+
folderPermissionItem: '{"USER":"","DEPARTMENT":"","ALL":"","GROUP":"","SUBCOMPANY":""}',
|
|
778
|
+
synSubordinateFolders: false,
|
|
779
|
+
keepOldShare: false,
|
|
780
|
+
},
|
|
781
|
+
operation,
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
// ── 「已存在」类响应(源 add_doc_share / add_folder_set_default_share)──────
|
|
785
|
+
/** 源 `code == 500 and status` → 「已存在」,视为成功但 added=false。 */
|
|
786
|
+
export function isAlreadyExistsResponse(data) {
|
|
787
|
+
return data.code === 500 && Boolean(data.status);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* 源这两个 plugin 直接用 client.execute(不过业务成功校验),
|
|
791
|
+
* CLI 侧在「已存在」分支之外补上业务成功校验,避免把业务失败当成 added=true。
|
|
792
|
+
*/
|
|
793
|
+
export function assertToleratedBusinessSuccess(data, action) {
|
|
794
|
+
if (isCaizhilianBusinessSuccess(data))
|
|
795
|
+
return;
|
|
796
|
+
throw new CaizhilianCliError('api', 'business_error', caizhilianBusinessMessage(data), {
|
|
797
|
+
code: data.code ?? data.status,
|
|
798
|
+
details: { action, code: data.code ?? data.status, message: caizhilianBusinessMessage(data) },
|
|
799
|
+
exitCode: 1,
|
|
800
|
+
});
|
|
801
|
+
}
|