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,611 @@
|
|
|
1
|
+
import { caizhilianValidationError } from '../../errors.js';
|
|
2
|
+
import { asList, asRecord, assertToleratedBusinessSuccess, checkDocShareAddPermission, fetchDocShareEntries, isAlreadyExistsResponse, } from '../api.js';
|
|
3
|
+
import { issueWriteLease } from '../confirm.js';
|
|
4
|
+
import { buildObjTypeList, parseCascadeRules, parseCascadeRulesOuter, parseFieldCascadeRules, parseObjTypeOptions, parseParamOptions, } from '../form.js';
|
|
5
|
+
import { fingerprint, hasNextPage, isFalsyIds, isPlainObject, parsePage, pyBool, pyGetStr, pyInt, pyStr, requireNonEmpty, requireText, } from '../shared.js';
|
|
6
|
+
import { caizhilianPrepared, caizhilianSuccess } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* 文档协作域操作(1:1 对应源 scripts/document_plugins.py 的标签 / 参与人 / 共享部分):
|
|
9
|
+
* SearchTagsPlugin、AddDocTagPlugin、RemoveDocTagPlugin、FetchDocTagsPlugin、
|
|
10
|
+
* AddDocParticipantPlugin、FetchDocParticipantsPlugin、RemoveDocParticipantPlugin、
|
|
11
|
+
* FetchDocShareFormPlugin、FetchDocShareListPlugin、AddDocSharePlugin、DeleteDocSharePlugin。
|
|
12
|
+
*
|
|
13
|
+
* 写操作按 weaver-work-cli 规范拆成 prepare(只读回查 + 组装请求体 + 签发票据)
|
|
14
|
+
* 与 apply(真正产生副作用),业务请求体与源保持逐字段一致。
|
|
15
|
+
*/
|
|
16
|
+
// ── 源常量 ────────────────────────────────────────────────
|
|
17
|
+
/** 源 SearchTagsPlugin._FIXED_BODY_PARAMS。 */
|
|
18
|
+
const TAG_QUERY_FIXED_BODY = { order: 8, search: 1, tagGroup: 0 };
|
|
19
|
+
/** 源 search_tags / fetch_doc_tags 的 type:0=公共标签,1=个人标签。 */
|
|
20
|
+
const TAG_TYPES = [0, 1];
|
|
21
|
+
/** 源 search_tags 的默认每页条数(parse_page_args default_page_size)。 */
|
|
22
|
+
const TAG_QUERY_DEFAULT_PAGE_SIZE = 50;
|
|
23
|
+
const TAG_QUERY_PATH = '/api/my/tag/query';
|
|
24
|
+
const TAG_QUERY_ENTITY_PATH = '/api/my/tag/queryEntityTag';
|
|
25
|
+
const TAG_ADD_ENTITY_PATH = '/api/my/tag/addEntityTag';
|
|
26
|
+
const TAG_REMOVE_ENTITY_PATH = '/api/my/tag/removeEntityTag';
|
|
27
|
+
const PARTICIPANT_ADD_PATH = '/api/doc/shareentry/addUser.common';
|
|
28
|
+
const PARTICIPANT_REMOVE_PATH = '/api/doc/shareentry/destroy.common';
|
|
29
|
+
/** 源 FetchDocParticipantsPlugin:/api/doc/document/{quote(doc_id, safe='')}/permissions。 */
|
|
30
|
+
const participantPermissionsPath = (docId) => `/api/doc/document/${encodeURIComponent(docId)}/permissions`;
|
|
31
|
+
const SHARE_FORM_PATH = '/api/doc/permission/data/query';
|
|
32
|
+
const SHARE_ADD_PATH = '/api/doc/shareentry/addShare';
|
|
33
|
+
const SHARE_DELETE_PATH = '/api/doc/shareentry/deleteShare';
|
|
34
|
+
/** 源共享/权限接口固定的 permissionId。 */
|
|
35
|
+
const SHARE_PERMISSION_ID = 'doc_s01';
|
|
36
|
+
/** 源 document_plugins._OBJ_TYPE_TO_TARGET_TYPE。 */
|
|
37
|
+
const OBJ_TYPE_TO_TARGET_TYPE = {
|
|
38
|
+
resource: 1,
|
|
39
|
+
department: 3,
|
|
40
|
+
subcompany: 3,
|
|
41
|
+
role: 4,
|
|
42
|
+
group: 5,
|
|
43
|
+
external: 7,
|
|
44
|
+
customer: 8,
|
|
45
|
+
customer_type: 9,
|
|
46
|
+
position: 10,
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* 源 AddDocSharePlugin.optional_params(顺序即写入顺序)。
|
|
50
|
+
* seclevel 未出现在对外契约 doc_share_operations.json 中,schema 也不放开,
|
|
51
|
+
* 这里保留以与源循环逐字段一致(实际永远不会命中)。
|
|
52
|
+
*/
|
|
53
|
+
const DOC_SHARE_OPTIONAL_PARAMS = [
|
|
54
|
+
'download',
|
|
55
|
+
'department_attribute',
|
|
56
|
+
'role_level',
|
|
57
|
+
'position_level',
|
|
58
|
+
'position_attribute',
|
|
59
|
+
'virtualCompany',
|
|
60
|
+
'createrPositionLevel',
|
|
61
|
+
'seclevel',
|
|
62
|
+
];
|
|
63
|
+
/** 源 AddDocSharePlugin.id_fields(顺序即写入顺序)。 */
|
|
64
|
+
const DOC_SHARE_ID_FIELDS = [
|
|
65
|
+
'obj_id',
|
|
66
|
+
'obj_id_department',
|
|
67
|
+
'obj_id_subcompany',
|
|
68
|
+
'obj_id_role',
|
|
69
|
+
'obj_id_group',
|
|
70
|
+
'obj_id_external',
|
|
71
|
+
'obj_id_customer',
|
|
72
|
+
'obj_id_customer_type',
|
|
73
|
+
'obj_id_position',
|
|
74
|
+
'obj_id_manager',
|
|
75
|
+
'obj_id_grade',
|
|
76
|
+
];
|
|
77
|
+
// ── 写操作 apply 名(continuation 与操作绑定,防跨操作串用)──
|
|
78
|
+
const PARTICIPANT_ADD_APPLY = 'caizhilian.participant.add.apply';
|
|
79
|
+
const PARTICIPANT_REMOVE_APPLY = 'caizhilian.participant.remove.apply';
|
|
80
|
+
const SHARE_ADD_APPLY = 'caizhilian.share.add.apply';
|
|
81
|
+
const SHARE_DELETE_APPLY = 'caizhilian.share.delete.apply';
|
|
82
|
+
function errorMessage(error) {
|
|
83
|
+
if (error instanceof Error && error.message)
|
|
84
|
+
return error.message;
|
|
85
|
+
return String(error);
|
|
86
|
+
}
|
|
87
|
+
// ── caizhilian.tag.search ─────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* 源 `int(args.get("type", 1))`:非整数报「type 必须是整数」,
|
|
90
|
+
* 非 0/1 报「type 只能是 0(公共标签)或 1(个人标签),不支持: {原值}」。
|
|
91
|
+
*/
|
|
92
|
+
function parseTagType(value) {
|
|
93
|
+
let tagType;
|
|
94
|
+
try {
|
|
95
|
+
tagType = pyInt(value, 1);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw caizhilianValidationError('number_invalid', 'type 必须是整数');
|
|
99
|
+
}
|
|
100
|
+
if (!TAG_TYPES.includes(tagType)) {
|
|
101
|
+
throw caizhilianValidationError('enum_invalid', `type 只能是 0(公共标签)或 1(个人标签),不支持: ${pyStr(value)}`);
|
|
102
|
+
}
|
|
103
|
+
return tagType;
|
|
104
|
+
}
|
|
105
|
+
async function searchTags(args, env) {
|
|
106
|
+
const action = 'caizhilian.tag.search';
|
|
107
|
+
const name = args.name ? String(args.name).trim() : '';
|
|
108
|
+
const { pageNo, pageSize } = parsePage(args, TAG_QUERY_DEFAULT_PAGE_SIZE);
|
|
109
|
+
const tagType = parseTagType(args.type);
|
|
110
|
+
const body = { ...TAG_QUERY_FIXED_BODY };
|
|
111
|
+
body.type = tagType;
|
|
112
|
+
body.name = name;
|
|
113
|
+
body.pageSize = pageSize;
|
|
114
|
+
body.pageNo = pageNo;
|
|
115
|
+
if (tagType === 0)
|
|
116
|
+
body.tagModules = ['document'];
|
|
117
|
+
const response = await env.host.request({
|
|
118
|
+
method: 'POST',
|
|
119
|
+
path: TAG_QUERY_PATH,
|
|
120
|
+
body,
|
|
121
|
+
operation: action,
|
|
122
|
+
});
|
|
123
|
+
const inner = asRecord(asRecord(response).data);
|
|
124
|
+
const items = asList(inner.data);
|
|
125
|
+
return caizhilianSuccess(action, {
|
|
126
|
+
page_no: pageNo,
|
|
127
|
+
page_size: pageSize,
|
|
128
|
+
has_next: hasNextPage(items, pageSize),
|
|
129
|
+
tags: items.filter(isPlainObject).map((tag) => ({
|
|
130
|
+
id: pyGetStr(tag, 'tagId'),
|
|
131
|
+
name: pyGetStr(tag, 'name'),
|
|
132
|
+
privacy: pyStr('type' in tag ? tag.type : 0) === '1',
|
|
133
|
+
})),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// ── caizhilian.tag.list ───────────────────────────────────
|
|
137
|
+
/** 源 FetchDocTagsPlugin:公共(0) + 个人(1) 两次查询合并。 */
|
|
138
|
+
async function listDocTags(args, env) {
|
|
139
|
+
const action = 'caizhilian.tag.list';
|
|
140
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
141
|
+
const tags = [];
|
|
142
|
+
for (const tagType of TAG_TYPES) {
|
|
143
|
+
const response = await env.host.request({
|
|
144
|
+
method: 'POST',
|
|
145
|
+
path: TAG_QUERY_ENTITY_PATH,
|
|
146
|
+
body: { entityId: docId, module: 'document', type: tagType },
|
|
147
|
+
operation: action,
|
|
148
|
+
});
|
|
149
|
+
const items = asList(asRecord(response).data);
|
|
150
|
+
for (const item of items) {
|
|
151
|
+
if (!isPlainObject(item))
|
|
152
|
+
continue;
|
|
153
|
+
tags.push({
|
|
154
|
+
id: pyGetStr(item, 'tagId'),
|
|
155
|
+
name: pyGetStr(item, 'name'),
|
|
156
|
+
privacy: tagType === 1,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return caizhilianSuccess(action, { tags });
|
|
161
|
+
}
|
|
162
|
+
/** 源 _extract_valid_tags:只接受 list[dict] 且 `str(tag.get("id") or "").strip()` 非空的项。 */
|
|
163
|
+
function extractValidTags(rawTags) {
|
|
164
|
+
const tags = [];
|
|
165
|
+
if (!Array.isArray(rawTags))
|
|
166
|
+
return tags;
|
|
167
|
+
for (const tag of rawTags) {
|
|
168
|
+
if (!isPlainObject(tag))
|
|
169
|
+
continue;
|
|
170
|
+
const tagId = (pyBool(tag.id) ? pyStr(tag.id) : '').trim();
|
|
171
|
+
if (!tagId)
|
|
172
|
+
continue;
|
|
173
|
+
tags.push({ id: tagId, privacy: pyBool(tag.privacy) });
|
|
174
|
+
}
|
|
175
|
+
return tags;
|
|
176
|
+
}
|
|
177
|
+
/** 源 _batch_tag_operation 的校验 + 分组(公共 type=0、个人 type=1,空组跳过)。 */
|
|
178
|
+
function planTagBatch(args, apiPath) {
|
|
179
|
+
const docId = requireText(args.doc_id, 'doc_id');
|
|
180
|
+
const tags = extractValidTags(args.tags);
|
|
181
|
+
if (!tags.length)
|
|
182
|
+
throw caizhilianValidationError('field_invalid', 'tags 中无有效标签');
|
|
183
|
+
const publicIds = tags.filter((tag) => !tag.privacy).map((tag) => tag.id);
|
|
184
|
+
const myIds = tags.filter((tag) => tag.privacy).map((tag) => tag.id);
|
|
185
|
+
const requests = [];
|
|
186
|
+
for (const [groupIds, groupType] of [
|
|
187
|
+
[publicIds, 0],
|
|
188
|
+
[myIds, 1],
|
|
189
|
+
]) {
|
|
190
|
+
if (!groupIds.length)
|
|
191
|
+
continue;
|
|
192
|
+
requests.push({
|
|
193
|
+
method: 'POST',
|
|
194
|
+
path: apiPath,
|
|
195
|
+
body: { tagIds: [...groupIds], entityId: docId, module: 'document', type: groupType },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return { docId, tagIds: [...publicIds, ...myIds], publicIds, myIds, requests };
|
|
199
|
+
}
|
|
200
|
+
/** add_doc_tag / remove_doc_tag 仅接口路径不同,共用一套 prepare/apply。 */
|
|
201
|
+
function tagBatchHandler(action, apiPath) {
|
|
202
|
+
const applyAction = `${action}.apply`;
|
|
203
|
+
return {
|
|
204
|
+
targetKeys: ['doc_id'],
|
|
205
|
+
async prepare(args, env) {
|
|
206
|
+
const plan = planTagBatch(args, apiPath);
|
|
207
|
+
const lease = await issueWriteLease(env, applyAction, args, {
|
|
208
|
+
doc_id: plan.docId,
|
|
209
|
+
tag_ids: plan.tagIds,
|
|
210
|
+
body_fingerprint: fingerprint(plan.requests),
|
|
211
|
+
});
|
|
212
|
+
return caizhilianPrepared(action, {
|
|
213
|
+
doc_id: plan.docId,
|
|
214
|
+
tag_ids: plan.tagIds,
|
|
215
|
+
count: plan.tagIds.length,
|
|
216
|
+
public_tag_ids: plan.publicIds,
|
|
217
|
+
my_tag_ids: plan.myIds,
|
|
218
|
+
requests: plan.requests,
|
|
219
|
+
}, lease);
|
|
220
|
+
},
|
|
221
|
+
async apply(args, env, lease) {
|
|
222
|
+
const plan = planTagBatch(args, apiPath);
|
|
223
|
+
const expected = typeof lease.body_fingerprint === 'string' ? lease.body_fingerprint : '';
|
|
224
|
+
if (expected && expected !== fingerprint(plan.requests)) {
|
|
225
|
+
throw caizhilianValidationError('plan_changed', `${applyAction} 的请求体与 prepare 时不一致,请重新执行 prepare`);
|
|
226
|
+
}
|
|
227
|
+
for (const request of plan.requests) {
|
|
228
|
+
await env.host.request({ ...request, operation: applyAction });
|
|
229
|
+
}
|
|
230
|
+
return caizhilianSuccess(applyAction, {
|
|
231
|
+
doc_id: plan.docId,
|
|
232
|
+
tag_ids: plan.tagIds,
|
|
233
|
+
count: plan.tagIds.length,
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
// ── 参与人:公共回查 ──────────────────────────────────────
|
|
239
|
+
/** 源 FetchDocParticipantsPlugin 的解析结果。 */
|
|
240
|
+
async function fetchParticipants(env, docId, action) {
|
|
241
|
+
const response = await env.host.request({
|
|
242
|
+
method: 'GET',
|
|
243
|
+
path: participantPermissionsPath(docId),
|
|
244
|
+
operation: action,
|
|
245
|
+
});
|
|
246
|
+
const raw = asRecord(response).data;
|
|
247
|
+
const entries = Array.isArray(raw) ? raw : [];
|
|
248
|
+
const participants = [];
|
|
249
|
+
for (const entry of entries) {
|
|
250
|
+
if (!isPlainObject(entry))
|
|
251
|
+
continue;
|
|
252
|
+
participants.push({
|
|
253
|
+
share_entry_id: pyGetStr(entry, 'id'),
|
|
254
|
+
user_id: pyGetStr(entry, 'sid'),
|
|
255
|
+
user_name: pyGetStr(entry, 'name'),
|
|
256
|
+
can_delete: pyBool(entry.canDelete),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return participants;
|
|
260
|
+
}
|
|
261
|
+
/** prepare 阶段的最佳努力回查:失败只降级为告警,不阻断(源无此步骤)。 */
|
|
262
|
+
async function tryFetchParticipants(env, docId, action, warnings) {
|
|
263
|
+
try {
|
|
264
|
+
return await fetchParticipants(env, docId, action);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
warnings.push(`[WARN] 参与人列表回查失败: ${errorMessage(error)}`);
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// ── caizhilian.participant.list ───────────────────────────
|
|
272
|
+
async function listParticipants(args, env) {
|
|
273
|
+
const action = 'caizhilian.participant.list';
|
|
274
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
275
|
+
return caizhilianSuccess(action, { participants: await fetchParticipants(env, docId, action) });
|
|
276
|
+
}
|
|
277
|
+
// ── caizhilian.participant.add ────────────────────────────
|
|
278
|
+
/** 源 AddDocParticipantPlugin 的固定请求体(键顺序与源一致)。 */
|
|
279
|
+
function buildParticipantAddBody(docId, userId) {
|
|
280
|
+
return {
|
|
281
|
+
entityIds: docId,
|
|
282
|
+
entryType: 'user',
|
|
283
|
+
module: 'document',
|
|
284
|
+
removeIds: '',
|
|
285
|
+
shareType: 'participants',
|
|
286
|
+
sids: userId,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const participantAddHandler = {
|
|
290
|
+
targetKeys: ['doc_id', 'user_id'],
|
|
291
|
+
async prepare(args, env) {
|
|
292
|
+
const action = 'caizhilian.participant.add';
|
|
293
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
294
|
+
const userId = requireNonEmpty(args.user_id, 'user_id 不能为空');
|
|
295
|
+
const warnings = [];
|
|
296
|
+
const current = await tryFetchParticipants(env, docId, action, warnings);
|
|
297
|
+
if (current.some((entry) => String(entry.user_id) === userId)) {
|
|
298
|
+
warnings.push(`[WARN] user_id=${userId} 已在参与人列表中,重复添加可能被服务端拒绝或产生重复条目`);
|
|
299
|
+
}
|
|
300
|
+
const lease = await issueWriteLease(env, PARTICIPANT_ADD_APPLY, args, { doc_id: docId, user_id: userId });
|
|
301
|
+
return caizhilianPrepared(action, {
|
|
302
|
+
doc_id: docId,
|
|
303
|
+
user_id: userId,
|
|
304
|
+
current_participants: current,
|
|
305
|
+
request: {
|
|
306
|
+
method: 'POST',
|
|
307
|
+
path: PARTICIPANT_ADD_PATH,
|
|
308
|
+
body: buildParticipantAddBody(docId, userId),
|
|
309
|
+
},
|
|
310
|
+
}, lease, warnings);
|
|
311
|
+
},
|
|
312
|
+
async apply(args, env) {
|
|
313
|
+
const action = PARTICIPANT_ADD_APPLY;
|
|
314
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
315
|
+
const userId = requireNonEmpty(args.user_id, 'user_id 不能为空');
|
|
316
|
+
const response = await env.host.request({
|
|
317
|
+
method: 'POST',
|
|
318
|
+
path: PARTICIPANT_ADD_PATH,
|
|
319
|
+
body: buildParticipantAddBody(docId, userId),
|
|
320
|
+
operation: action,
|
|
321
|
+
});
|
|
322
|
+
const shareEntries = asList(asRecord(asRecord(response).data).shareEntrys);
|
|
323
|
+
if (!shareEntries.length)
|
|
324
|
+
throw caizhilianValidationError('empty_response', '返回的参与人列表为空');
|
|
325
|
+
const entry = asRecord(shareEntries[0]);
|
|
326
|
+
return caizhilianSuccess(action, {
|
|
327
|
+
share_entry_id: pyGetStr(entry, 'id'),
|
|
328
|
+
doc_id: docId,
|
|
329
|
+
user_id: userId,
|
|
330
|
+
user_name: pyGetStr(entry, 'name'),
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
// ── caizhilian.participant.remove ─────────────────────────
|
|
335
|
+
/** 源 RemoveDocParticipantPlugin 的固定请求体。 */
|
|
336
|
+
function buildParticipantRemoveBody(shareEntryId, docId) {
|
|
337
|
+
return { id: shareEntryId, module: 'document', entityIds: docId };
|
|
338
|
+
}
|
|
339
|
+
const participantRemoveHandler = {
|
|
340
|
+
targetKeys: ['doc_id', 'share_entry_id'],
|
|
341
|
+
async prepare(args, env) {
|
|
342
|
+
const action = 'caizhilian.participant.remove';
|
|
343
|
+
const shareEntryId = requireNonEmpty(args.share_entry_id, 'share_entry_id 不能为空');
|
|
344
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
345
|
+
const warnings = [];
|
|
346
|
+
const current = await tryFetchParticipants(env, docId, action, warnings);
|
|
347
|
+
const matched = current.find((entry) => String(entry.share_entry_id) === shareEntryId);
|
|
348
|
+
if (!matched) {
|
|
349
|
+
warnings.push(`[WARN] 参与人列表中未找到 share_entry_id=${shareEntryId},apply 仍会按源行为提交删除请求`);
|
|
350
|
+
}
|
|
351
|
+
const lease = await issueWriteLease(env, PARTICIPANT_REMOVE_APPLY, args, {
|
|
352
|
+
doc_id: docId,
|
|
353
|
+
share_entry_id: shareEntryId,
|
|
354
|
+
});
|
|
355
|
+
return caizhilianPrepared(action, {
|
|
356
|
+
doc_id: docId,
|
|
357
|
+
share_entry_id: shareEntryId,
|
|
358
|
+
target: matched ?? null,
|
|
359
|
+
request: {
|
|
360
|
+
method: 'POST',
|
|
361
|
+
path: PARTICIPANT_REMOVE_PATH,
|
|
362
|
+
body: buildParticipantRemoveBody(shareEntryId, docId),
|
|
363
|
+
},
|
|
364
|
+
}, lease, warnings);
|
|
365
|
+
},
|
|
366
|
+
async apply(args, env) {
|
|
367
|
+
const action = PARTICIPANT_REMOVE_APPLY;
|
|
368
|
+
const shareEntryId = requireNonEmpty(args.share_entry_id, 'share_entry_id 不能为空');
|
|
369
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
370
|
+
await env.host.request({
|
|
371
|
+
method: 'POST',
|
|
372
|
+
path: PARTICIPANT_REMOVE_PATH,
|
|
373
|
+
body: buildParticipantRemoveBody(shareEntryId, docId),
|
|
374
|
+
operation: action,
|
|
375
|
+
});
|
|
376
|
+
return caizhilianSuccess(action, { share_entry_id: shareEntryId, doc_id: docId });
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
// ── caizhilian.share.form ─────────────────────────────────
|
|
380
|
+
async function shareForm(args, env) {
|
|
381
|
+
const action = 'caizhilian.share.form';
|
|
382
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
383
|
+
const response = await env.host.request({
|
|
384
|
+
method: 'POST',
|
|
385
|
+
path: SHARE_FORM_PATH,
|
|
386
|
+
body: {
|
|
387
|
+
sourceId: docId,
|
|
388
|
+
permissionId: SHARE_PERMISSION_ID,
|
|
389
|
+
sourceType: 1,
|
|
390
|
+
id: docId,
|
|
391
|
+
tableName: 'document',
|
|
392
|
+
},
|
|
393
|
+
operation: action,
|
|
394
|
+
});
|
|
395
|
+
const respData = asRecord(asRecord(response).data);
|
|
396
|
+
const fieldCascadeRules = parseFieldCascadeRules(respData);
|
|
397
|
+
const { nameMap, availableIds } = parseObjTypeOptions(respData);
|
|
398
|
+
// 源未传 permission_type / seclevel_allowed(文档共享表单不区分权限类型)。
|
|
399
|
+
const objTypes = buildObjTypeList({
|
|
400
|
+
cascadeRulesOuter: parseCascadeRulesOuter(respData),
|
|
401
|
+
cascadeRules: parseCascadeRules(respData),
|
|
402
|
+
objTypeNames: nameMap,
|
|
403
|
+
availableIds,
|
|
404
|
+
fieldCascadeRules,
|
|
405
|
+
});
|
|
406
|
+
return caizhilianSuccess(action, {
|
|
407
|
+
obj_types: objTypes,
|
|
408
|
+
param_options: parseParamOptions(respData, fieldCascadeRules),
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
// ── caizhilian.share.list ─────────────────────────────────
|
|
412
|
+
async function shareList(args, env) {
|
|
413
|
+
const action = 'caizhilian.share.list';
|
|
414
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
415
|
+
const result = await fetchDocShareEntries(env, docId, action);
|
|
416
|
+
return caizhilianSuccess(action, { add_visible: result.add_visible, shares: result.shares });
|
|
417
|
+
}
|
|
418
|
+
/** 源 AddDocSharePlugin.execute 的权限校验 + 请求体组装。 */
|
|
419
|
+
async function planDocShareAdd(args, env, action) {
|
|
420
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
421
|
+
const objType = requireNonEmpty(args.obj_type, 'obj_type 不能为空');
|
|
422
|
+
const shareLevel = requireNonEmpty(args.share_level, 'share_level 不能为空');
|
|
423
|
+
await checkDocShareAddPermission(env, docId, action);
|
|
424
|
+
const body = {
|
|
425
|
+
sourceId: docId,
|
|
426
|
+
permissionId: SHARE_PERMISSION_ID,
|
|
427
|
+
sourceType: 1,
|
|
428
|
+
docId,
|
|
429
|
+
objType,
|
|
430
|
+
share_level: shareLevel,
|
|
431
|
+
};
|
|
432
|
+
for (const key of DOC_SHARE_OPTIONAL_PARAMS) {
|
|
433
|
+
if (key in args)
|
|
434
|
+
body[key] = args[key];
|
|
435
|
+
}
|
|
436
|
+
let targetId;
|
|
437
|
+
for (const field of DOC_SHARE_ID_FIELDS) {
|
|
438
|
+
if (!(field in args))
|
|
439
|
+
continue;
|
|
440
|
+
const value = args[field];
|
|
441
|
+
if (Array.isArray(value)) {
|
|
442
|
+
body[field] = value;
|
|
443
|
+
if (value.length)
|
|
444
|
+
targetId = String(value[0]);
|
|
445
|
+
}
|
|
446
|
+
else if (typeof value === 'string' && value) {
|
|
447
|
+
body[field] = [value];
|
|
448
|
+
targetId = value;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// 源:share_level 非 "1" 时 download 自动设为可下载。
|
|
452
|
+
if (shareLevel !== '1' && !('download' in body))
|
|
453
|
+
body.download = ['download'];
|
|
454
|
+
const targetType = OBJ_TYPE_TO_TARGET_TYPE[objType];
|
|
455
|
+
if (targetType !== undefined)
|
|
456
|
+
body.targetType = targetType;
|
|
457
|
+
if (targetId)
|
|
458
|
+
body.targetId = targetId;
|
|
459
|
+
return { docId, objType, shareLevel, targetType, targetId, body };
|
|
460
|
+
}
|
|
461
|
+
const shareAddHandler = {
|
|
462
|
+
targetKeys: ['doc_id', 'obj_type', 'share_level'],
|
|
463
|
+
async prepare(args, env) {
|
|
464
|
+
const action = 'caizhilian.share.add';
|
|
465
|
+
const plan = await planDocShareAdd(args, env, action);
|
|
466
|
+
const lease = await issueWriteLease(env, SHARE_ADD_APPLY, args, {
|
|
467
|
+
doc_id: plan.docId,
|
|
468
|
+
obj_type: plan.objType,
|
|
469
|
+
share_level: plan.shareLevel,
|
|
470
|
+
body_fingerprint: fingerprint(plan.body),
|
|
471
|
+
});
|
|
472
|
+
return caizhilianPrepared(action, {
|
|
473
|
+
doc_id: plan.docId,
|
|
474
|
+
obj_type: plan.objType,
|
|
475
|
+
share_level: plan.shareLevel,
|
|
476
|
+
target_type: plan.targetType ?? null,
|
|
477
|
+
target_id: plan.targetId ?? null,
|
|
478
|
+
request: { method: 'POST', path: SHARE_ADD_PATH, body: plan.body },
|
|
479
|
+
}, lease);
|
|
480
|
+
},
|
|
481
|
+
async apply(args, env, lease) {
|
|
482
|
+
const action = SHARE_ADD_APPLY;
|
|
483
|
+
const plan = await planDocShareAdd(args, env, 'caizhilian.share.add');
|
|
484
|
+
const expected = typeof lease.body_fingerprint === 'string' ? lease.body_fingerprint : '';
|
|
485
|
+
if (expected && expected !== fingerprint(plan.body)) {
|
|
486
|
+
throw caizhilianValidationError('plan_changed', `${action} 的请求体与 prepare 时不一致(表单级联规则可能已变化),请重新执行 prepare`);
|
|
487
|
+
}
|
|
488
|
+
// 源直接用 client.execute(不过业务校验),因此这里容忍业务错误后自行判定。
|
|
489
|
+
const data = asRecord(await env.host.request({
|
|
490
|
+
method: 'POST',
|
|
491
|
+
path: SHARE_ADD_PATH,
|
|
492
|
+
body: plan.body,
|
|
493
|
+
operation: action,
|
|
494
|
+
tolerateBusinessError: true,
|
|
495
|
+
}));
|
|
496
|
+
if (isAlreadyExistsResponse(data)) {
|
|
497
|
+
const message = data.message || data.msg || '已存在';
|
|
498
|
+
return caizhilianSuccess(action, { added: false, message: pyStr(message) });
|
|
499
|
+
}
|
|
500
|
+
assertToleratedBusinessSuccess(data, action);
|
|
501
|
+
// 源:回读列表失败时仍算成功,只是列表为空。
|
|
502
|
+
let shares = [];
|
|
503
|
+
try {
|
|
504
|
+
shares = (await fetchDocShareEntries(env, plan.docId, action)).shares;
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
shares = [];
|
|
508
|
+
}
|
|
509
|
+
return caizhilianSuccess(action, { added: true, shares });
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
/** 源 DeleteDocSharePlugin.execute 的存在性 / can_delete 校验 + 请求体组装。 */
|
|
513
|
+
async function planDocShareDelete(args, env, action) {
|
|
514
|
+
const docId = requireNonEmpty(args.doc_id, 'doc_id 不能为空');
|
|
515
|
+
const rawIds = args.ids;
|
|
516
|
+
if (isFalsyIds(rawIds))
|
|
517
|
+
throw caizhilianValidationError('field_required', 'ids 不能为空');
|
|
518
|
+
const idList = Array.isArray(rawIds) ? rawIds.map((item) => String(item)) : [String(rawIds)];
|
|
519
|
+
const { shares } = await fetchDocShareEntries(env, docId, action);
|
|
520
|
+
const shareMap = new Map(shares.map((share) => [String(share.id), share]));
|
|
521
|
+
const notFound = [];
|
|
522
|
+
const notDeletable = [];
|
|
523
|
+
const removing = [];
|
|
524
|
+
for (const shareId of idList) {
|
|
525
|
+
const share = shareMap.get(shareId);
|
|
526
|
+
if (!share) {
|
|
527
|
+
notFound.push(shareId);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
if (!share.can_delete) {
|
|
531
|
+
notDeletable.push('object_name' in share ? pyStr(share.object_name) : shareId);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
removing.push(share);
|
|
535
|
+
}
|
|
536
|
+
if (notFound.length) {
|
|
537
|
+
throw caizhilianValidationError('share_not_found', `以下共享条目不存在: ${notFound.join(', ')}`, { notFound });
|
|
538
|
+
}
|
|
539
|
+
if (notDeletable.length) {
|
|
540
|
+
throw caizhilianValidationError('share_not_deletable', `以下共享条目无删除权限(受权限设置限制,重试不会改变结果): ${notDeletable.join(', ')}`, { notDeletable });
|
|
541
|
+
}
|
|
542
|
+
const idsStr = idList.join(',');
|
|
543
|
+
return {
|
|
544
|
+
docId,
|
|
545
|
+
idList,
|
|
546
|
+
removing,
|
|
547
|
+
body: {
|
|
548
|
+
sourceId: docId,
|
|
549
|
+
permissionId: SHARE_PERMISSION_ID,
|
|
550
|
+
sourceType: 1,
|
|
551
|
+
docId,
|
|
552
|
+
ids: idsStr,
|
|
553
|
+
datas: idList,
|
|
554
|
+
},
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
const shareDeleteHandler = {
|
|
558
|
+
targetKeys: ['doc_id'],
|
|
559
|
+
async prepare(args, env) {
|
|
560
|
+
const action = 'caizhilian.share.delete';
|
|
561
|
+
const plan = await planDocShareDelete(args, env, action);
|
|
562
|
+
const lease = await issueWriteLease(env, SHARE_DELETE_APPLY, args, {
|
|
563
|
+
doc_id: plan.docId,
|
|
564
|
+
ids: plan.idList,
|
|
565
|
+
body_fingerprint: fingerprint(plan.body),
|
|
566
|
+
});
|
|
567
|
+
return caizhilianPrepared(action, {
|
|
568
|
+
doc_id: plan.docId,
|
|
569
|
+
ids: plan.idList,
|
|
570
|
+
removing: plan.removing,
|
|
571
|
+
request: { method: 'POST', path: SHARE_DELETE_PATH, body: plan.body },
|
|
572
|
+
}, lease);
|
|
573
|
+
},
|
|
574
|
+
async apply(args, env, lease) {
|
|
575
|
+
const action = SHARE_DELETE_APPLY;
|
|
576
|
+
const plan = await planDocShareDelete(args, env, 'caizhilian.share.delete');
|
|
577
|
+
const expected = typeof lease.body_fingerprint === 'string' ? lease.body_fingerprint : '';
|
|
578
|
+
if (expected && expected !== fingerprint(plan.body)) {
|
|
579
|
+
throw caizhilianValidationError('plan_changed', `${action} 的请求体与 prepare 时不一致(共享条目可能已变化),请重新执行 prepare`);
|
|
580
|
+
}
|
|
581
|
+
await env.host.request({
|
|
582
|
+
method: 'POST',
|
|
583
|
+
path: SHARE_DELETE_PATH,
|
|
584
|
+
body: plan.body,
|
|
585
|
+
operation: action,
|
|
586
|
+
});
|
|
587
|
+
// 源:回读列表失败时仍算成功,只是列表为空。
|
|
588
|
+
let shares = [];
|
|
589
|
+
try {
|
|
590
|
+
shares = (await fetchDocShareEntries(env, plan.docId, action)).shares;
|
|
591
|
+
}
|
|
592
|
+
catch {
|
|
593
|
+
shares = [];
|
|
594
|
+
}
|
|
595
|
+
return caizhilianSuccess(action, { deleted: true, shares });
|
|
596
|
+
},
|
|
597
|
+
};
|
|
598
|
+
// ── 处理器汇总 ────────────────────────────────────────────
|
|
599
|
+
export const caizhilianCollabHandlers = {
|
|
600
|
+
'caizhilian.tag.search': { execute: searchTags },
|
|
601
|
+
'caizhilian.tag.list': { execute: listDocTags },
|
|
602
|
+
'caizhilian.tag.add': tagBatchHandler('caizhilian.tag.add', TAG_ADD_ENTITY_PATH),
|
|
603
|
+
'caizhilian.tag.remove': tagBatchHandler('caizhilian.tag.remove', TAG_REMOVE_ENTITY_PATH),
|
|
604
|
+
'caizhilian.participant.list': { execute: listParticipants },
|
|
605
|
+
'caizhilian.participant.add': participantAddHandler,
|
|
606
|
+
'caizhilian.participant.remove': participantRemoveHandler,
|
|
607
|
+
'caizhilian.share.form': { execute: shareForm },
|
|
608
|
+
'caizhilian.share.list': { execute: shareList },
|
|
609
|
+
'caizhilian.share.add': shareAddHandler,
|
|
610
|
+
'caizhilian.share.delete': shareDeleteHandler,
|
|
611
|
+
};
|