weaver-work-cli 0.1.1 → 0.1.4
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 +187 -76
- package/dist/cmd/setup/index.js +5 -5
- package/dist/cmd/skills/index.js +1 -1
- package/dist/core/hooks.js +7 -0
- package/dist/index.js +0 -0
- package/dist/internal/e10/auth/commands.js +160 -55
- package/dist/internal/e10/auth/crypto.js +7 -22
- package/dist/internal/e10/auth/session.js +1 -1
- package/dist/internal/e10/auth/xiaoe.js +268 -0
- package/dist/internal/e10/context.js +16 -2
- package/dist/internal/e10/index.js +1 -0
- package/dist/internal/e10/request-runtime.js +344 -0
- package/dist/internal/skills/install.js +48 -6
- package/dist/internal/skills/reader.js +27 -0
- package/dist/shortcuts/archive/continuation.js +70 -0
- package/dist/shortcuts/archive/errors.js +53 -0
- package/dist/shortcuts/archive/host.js +197 -0
- package/dist/shortcuts/archive/index.js +363 -0
- package/dist/shortcuts/archive/manifest.js +45 -0
- package/dist/shortcuts/archive/operations/archive-info.js +55 -0
- package/dist/shortcuts/archive/operations/borrow-car.js +146 -0
- package/dist/shortcuts/archive/operations/borrow-list.js +200 -0
- package/dist/shortcuts/archive/operations/confirm.js +46 -0
- package/dist/shortcuts/archive/operations/download.js +238 -0
- package/dist/shortcuts/archive/operations/flow-url.js +240 -0
- package/dist/shortcuts/archive/operations/fonds.js +95 -0
- package/dist/shortcuts/archive/operations/getPageUrl.js +137 -0
- package/dist/shortcuts/archive/operations/registry.js +43 -0
- package/dist/shortcuts/archive/operations/search.js +88 -0
- package/dist/shortcuts/archive/operations/shared.js +120 -0
- package/dist/shortcuts/archive/operations/types.js +23 -0
- package/dist/shortcuts/archive/operations/upload.js +182 -0
- package/dist/shortcuts/archive/operations.js +13 -0
- package/dist/shortcuts/archive/render/search-format.js +276 -0
- package/dist/shortcuts/archive/render/search-view.js +125 -0
- package/dist/shortcuts/asset/continuation.js +70 -0
- package/dist/shortcuts/asset/errors.js +67 -0
- package/dist/shortcuts/asset/host.js +438 -0
- package/dist/shortcuts/asset/index.js +255 -0
- package/dist/shortcuts/asset/manifest.js +471 -0
- package/dist/shortcuts/asset/operations/query.js +249 -0
- package/dist/shortcuts/asset/operations/registry.js +85 -0
- package/dist/shortcuts/asset/operations/shared.js +100 -0
- package/dist/shortcuts/asset/operations/type-cache.js +49 -0
- package/dist/shortcuts/asset/operations/types.js +1 -0
- package/dist/shortcuts/asset/operations/viewlink.js +117 -0
- package/dist/shortcuts/asset/operations/write.js +435 -0
- package/dist/shortcuts/asset/operations.js +9 -0
- package/dist/shortcuts/calendar/continuation.js +70 -0
- package/dist/shortcuts/calendar/errors.js +53 -0
- package/dist/shortcuts/calendar/host.js +148 -0
- package/dist/shortcuts/calendar/index.js +125 -0
- package/dist/shortcuts/calendar/manifest.js +26 -0
- package/dist/shortcuts/calendar/operations/read.js +259 -0
- package/dist/shortcuts/calendar/operations/registry.js +31 -0
- package/dist/shortcuts/calendar/operations/shared.js +132 -0
- package/dist/shortcuts/calendar/operations/types.js +25 -0
- package/dist/shortcuts/calendar/operations/write.js +439 -0
- package/dist/shortcuts/calendar/operations.js +8 -0
- package/dist/shortcuts/ehr/continuation.js +70 -0
- package/dist/shortcuts/ehr/errors.js +53 -0
- package/dist/shortcuts/ehr/host.js +213 -0
- package/dist/shortcuts/ehr/index.js +205 -0
- package/dist/shortcuts/ehr/manifest.js +47 -0
- package/dist/shortcuts/ehr/operations/attend-read.js +373 -0
- package/dist/shortcuts/ehr/operations/attend-write.js +514 -0
- package/dist/shortcuts/ehr/operations/hr-contact.js +363 -0
- package/dist/shortcuts/ehr/operations/hr-ds.js +298 -0
- package/dist/shortcuts/ehr/operations/hr-report.js +386 -0
- package/dist/shortcuts/ehr/operations/recruit-write.js +1629 -0
- package/dist/shortcuts/ehr/operations/recruit.js +803 -0
- package/dist/shortcuts/ehr/operations/registry.js +43 -0
- package/dist/shortcuts/ehr/operations/salary.js +319 -0
- package/dist/shortcuts/ehr/operations/shared.js +246 -0
- package/dist/shortcuts/ehr/operations/types.js +25 -0
- package/dist/shortcuts/ehr/operations.js +25 -0
- package/dist/shortcuts/esb/continuation.js +70 -0
- package/dist/shortcuts/esb/errors.js +65 -0
- package/dist/shortcuts/esb/host.js +187 -0
- package/dist/shortcuts/esb/index.js +112 -0
- package/dist/shortcuts/esb/manifest.js +91 -0
- package/dist/shortcuts/esb/operations/input-format.js +31 -0
- package/dist/shortcuts/esb/operations/registry.js +48 -0
- package/dist/shortcuts/esb/operations/shared.js +152 -0
- package/dist/shortcuts/esb/operations/trigger.js +76 -0
- package/dist/shortcuts/esb/operations/types.js +21 -0
- package/dist/shortcuts/esb/operations.js +15 -0
- package/dist/shortcuts/fna/continuation.js +70 -0
- package/dist/shortcuts/fna/errors.js +59 -0
- package/dist/shortcuts/fna/host.js +146 -0
- package/dist/shortcuts/fna/index.js +111 -0
- package/dist/shortcuts/fna/manifest.js +41 -0
- package/dist/shortcuts/fna/operations/budget.js +124 -0
- package/dist/shortcuts/fna/operations/expense-center.js +148 -0
- package/dist/shortcuts/fna/operations/invoice-writeoff.js +212 -0
- package/dist/shortcuts/fna/operations/loan.js +195 -0
- package/dist/shortcuts/fna/operations/registry.js +35 -0
- package/dist/shortcuts/fna/operations/shared.js +210 -0
- package/dist/shortcuts/fna/operations/types.js +25 -0
- package/dist/shortcuts/fna/operations.js +19 -0
- package/dist/shortcuts/hrm/errors.js +38 -0
- package/dist/shortcuts/hrm/host.js +156 -0
- package/dist/shortcuts/hrm/index.js +111 -0
- package/dist/shortcuts/hrm/manifest.js +32 -0
- package/dist/shortcuts/hrm/operations/employee.js +338 -0
- package/dist/shortcuts/hrm/operations/org.js +110 -0
- package/dist/shortcuts/hrm/operations/position.js +176 -0
- package/dist/shortcuts/hrm/operations/registry.js +33 -0
- package/dist/shortcuts/hrm/operations/shared.js +143 -0
- package/dist/shortcuts/hrm/operations/types.js +23 -0
- package/dist/shortcuts/hrm/operations.js +19 -0
- package/dist/shortcuts/index.js +20 -0
- package/dist/shortcuts/invoice/host.js +99 -94
- package/dist/shortcuts/invoice/index.js +65 -61
- package/dist/shortcuts/invoice/manifest.js +222 -14
- package/dist/shortcuts/invoice/operations/browse-field.js +17 -0
- package/dist/shortcuts/invoice/operations/issuing.js +198 -0
- package/dist/shortcuts/invoice/operations/ocr-preview.js +6 -3
- package/dist/shortcuts/invoice/operations/registry.js +17 -0
- package/dist/shortcuts/invoice/operations/reim.js +401 -0
- package/dist/shortcuts/invoice/operations/shared.js +271 -6
- package/dist/shortcuts/invoice/operations/validate-preview.js +1 -1
- package/dist/shortcuts/jiuchuanhui/cache.js +441 -0
- package/dist/shortcuts/jiuchuanhui/continuation.js +70 -0
- package/dist/shortcuts/jiuchuanhui/errors.js +59 -0
- package/dist/shortcuts/jiuchuanhui/host.js +159 -0
- package/dist/shortcuts/jiuchuanhui/index.js +109 -0
- package/dist/shortcuts/jiuchuanhui/manifest.js +469 -0
- package/dist/shortcuts/jiuchuanhui/operations/datajson.js +214 -0
- package/dist/shortcuts/jiuchuanhui/operations/esb.js +157 -0
- package/dist/shortcuts/jiuchuanhui/operations/generic.js +146 -0
- package/dist/shortcuts/jiuchuanhui/operations/registry.js +133 -0
- package/dist/shortcuts/jiuchuanhui/operations/shared.js +158 -0
- package/dist/shortcuts/jiuchuanhui/operations/types.js +1 -0
- package/dist/shortcuts/jiuchuanhui/operations.js +8 -0
- package/dist/shortcuts/mail/continuation.js +70 -0
- package/dist/shortcuts/mail/errors.js +53 -0
- package/dist/shortcuts/mail/host.js +182 -0
- package/dist/shortcuts/mail/index.js +519 -0
- package/dist/shortcuts/mail/manifest.js +38 -0
- package/dist/shortcuts/mail/operations/mail-blacklist.js +79 -0
- package/dist/shortcuts/mail/operations/mail-contacts.js +195 -0
- package/dist/shortcuts/mail/operations/mail-manage.js +335 -0
- package/dist/shortcuts/mail/operations/mail-read.js +508 -0
- package/dist/shortcuts/mail/operations/mail-send.js +474 -0
- package/dist/shortcuts/mail/operations/mail-sign.js +203 -0
- package/dist/shortcuts/mail/operations/mail-template.js +170 -0
- package/dist/shortcuts/mail/operations/registry.js +41 -0
- package/dist/shortcuts/mail/operations/shared.js +64 -0
- package/dist/shortcuts/mail/operations/types.js +27 -0
- package/dist/shortcuts/mail/operations.js +19 -0
- package/dist/shortcuts/plan/continuation.js +70 -0
- package/dist/shortcuts/plan/errors.js +70 -0
- package/dist/shortcuts/plan/host.js +180 -0
- package/dist/shortcuts/plan/index.js +150 -0
- package/dist/shortcuts/plan/manifest.js +53 -0
- package/dist/shortcuts/plan/operations/link.js +256 -0
- package/dist/shortcuts/plan/operations/read.js +259 -0
- package/dist/shortcuts/plan/operations/registry.js +33 -0
- package/dist/shortcuts/plan/operations/shared.js +246 -0
- package/dist/shortcuts/plan/operations/types.js +49 -0
- package/dist/shortcuts/plan/operations/version.js +49 -0
- package/dist/shortcuts/plan/operations/write.js +710 -0
- package/dist/shortcuts/plan/operations.js +8 -0
- package/docs/SKILL.md +11 -7
- package/docs/_catalog.md +23 -5
- package/docs/agent-invoice.md +31 -10
- package/docs/agent-skill-install.md +211 -0
- package/docs/calendar.md +87 -0
- package/docs/e10-auth.md +28 -9
- package/docs/ehr.md +186 -0
- package/docs/esb.md +64 -0
- package/docs/fna.md +72 -0
- package/docs/hrm.md +89 -0
- package/docs/invoice.md +66 -20
- package/docs/jiuchuanhui.md +131 -0
- package/docs/operation-manual.md +443 -0
- package/docs/plan.md +82 -0
- package/docs/wenshuding.md +129 -0
- package/docs/ziguanjia.md +89 -0
- package/package.json +4 -4
- package/scripts/package-skill.mjs +127 -34
- package/scripts/postbuild.mjs +13 -0
- package/skill-template/business-info.json +13 -2
- package/skill-template/domains/calendar.md +15 -0
- package/skill-template/domains/esb.md +13 -0
- package/skill-template/domains/hrm.md +16 -0
- package/skill-template/domains/jiuchuanhui.md +23 -0
- package/skill-template/domains/jucailin.md +22 -0
- package/skill-template/domains/mail.md +21 -0
- package/skill-template/domains/plan.md +17 -0
- package/skill-template/domains/qiyecheng.md +17 -0
- package/skill-template/domains/shared.md +9 -3
- package/skill-template/domains/skill-maker.md +10 -0
- package/skill-template/domains/wenshuding.md +22 -0
- package/skill-template/domains/{invoice.md → yepiaotong.md} +4 -0
- package/skill-template/domains/ziguanjia.md +21 -0
- package/skill-template/master-skill-template.md +12 -6
- package/skill-template/skill-template.md +40 -9
- package/skills/weaver-e10-calendar/SKILL.md +101 -0
- package/skills/weaver-e10-calendar/product.json +8 -0
- package/skills/weaver-e10-calendar/references/calendar-app-check.md +65 -0
- package/skills/weaver-e10-calendar/references/calendar-dict.md +55 -0
- package/skills/weaver-e10-calendar/references/calendar-query.md +70 -0
- package/skills/weaver-e10-calendar/references/calendar-write.md +97 -0
- package/skills/weaver-e10-calendar/references/source-manifest.json +54 -0
- package/skills/weaver-e10-esb/SKILL.md +102 -0
- package/skills/weaver-e10-esb/product.json +8 -0
- package/skills/weaver-e10-esb/references/input-format.md +52 -0
- package/skills/weaver-e10-esb/references/source-manifest.json +242 -0
- package/skills/weaver-e10-esb/references/trigger.md +99 -0
- package/skills/weaver-e10-hrm/SKILL.md +87 -0
- package/skills/weaver-e10-hrm/product.json +8 -0
- package/skills/weaver-e10-hrm/references/employee.md +109 -0
- package/skills/weaver-e10-hrm/references/org.md +95 -0
- package/skills/weaver-e10-hrm/references/position.md +95 -0
- package/skills/weaver-e10-hrm/references/source-manifest.json +121 -0
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +174 -0
- package/skills/weaver-e10-jiuchuanhui/product.json +8 -0
- package/skills/weaver-e10-jiuchuanhui/references/cache.md +110 -0
- package/skills/weaver-e10-jiuchuanhui/references/clue.md +61 -0
- package/skills/weaver-e10-jiuchuanhui/references/contact-plan.md +56 -0
- package/skills/weaver-e10-jiuchuanhui/references/contact-records.md +48 -0
- package/skills/weaver-e10-jiuchuanhui/references/contact.md +56 -0
- package/skills/weaver-e10-jiuchuanhui/references/customer.md +69 -0
- package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +24 -0
- package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +60 -0
- package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +59 -0
- package/skills/weaver-e10-jiuchuanhui/references/jiuchuanhui-entry.md +60 -0
- package/skills/weaver-e10-jiuchuanhui/references/sale.md +72 -0
- package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +2632 -0
- package/skills/weaver-e10-jucailin/SKILL.md +101 -0
- package/skills/weaver-e10-jucailin/product.json +8 -0
- package/skills/weaver-e10-jucailin/references/attend-read.md +73 -0
- package/skills/weaver-e10-jucailin/references/attend-write.md +82 -0
- package/skills/weaver-e10-jucailin/references/hr-contact.md +92 -0
- package/skills/weaver-e10-jucailin/references/hr-datasource.md +104 -0
- package/skills/weaver-e10-jucailin/references/hr-report.md +76 -0
- package/skills/weaver-e10-jucailin/references/recruit-read.md +76 -0
- package/skills/weaver-e10-jucailin/references/recruit-write.md +70 -0
- package/skills/weaver-e10-jucailin/references/safety-boundaries.md +35 -0
- package/skills/weaver-e10-jucailin/references/salary.md +110 -0
- package/skills/weaver-e10-jucailin/references/source-manifest.json +152 -0
- package/skills/weaver-e10-mail/SKILL.md +151 -0
- package/skills/weaver-e10-mail/product.json +8 -0
- package/skills/weaver-e10-mail/references/mail-agent-entry.md +47 -0
- package/skills/weaver-e10-mail/references/mail-attachment.md +30 -0
- package/skills/weaver-e10-mail/references/mail-blacklist.md +27 -0
- package/skills/weaver-e10-mail/references/mail-contacts.md +33 -0
- package/skills/weaver-e10-mail/references/mail-manage.md +44 -0
- package/skills/weaver-e10-mail/references/mail-read.md +76 -0
- package/skills/weaver-e10-mail/references/mail-send.md +43 -0
- package/skills/weaver-e10-mail/references/mail-sign.md +33 -0
- package/skills/weaver-e10-mail/references/mail-template.md +31 -0
- package/skills/weaver-e10-mail/references/safety-boundaries.md +32 -0
- package/skills/weaver-e10-plan/SKILL.md +139 -0
- package/skills/weaver-e10-plan/product.json +8 -0
- package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +123 -0
- package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +138 -0
- package/skills/weaver-e10-plan/references/plan-report-query.md +154 -0
- package/skills/weaver-e10-plan/references/plan-report-remind.md +104 -0
- package/skills/weaver-e10-plan/references/plan-report-write.md +162 -0
- package/skills/weaver-e10-plan/references/source-manifest.json +858 -0
- package/skills/weaver-e10-qiyecheng/SKILL.md +118 -0
- package/skills/weaver-e10-qiyecheng/product.json +8 -0
- package/skills/weaver-e10-qiyecheng/references/budget.md +65 -0
- package/skills/weaver-e10-qiyecheng/references/catalog.md +56 -0
- package/skills/weaver-e10-qiyecheng/references/expense-center.md +51 -0
- package/skills/weaver-e10-qiyecheng/references/invoice-writeoff.md +57 -0
- package/skills/weaver-e10-qiyecheng/references/loan-management.md +59 -0
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +698 -0
- package/skills/weaver-e10-shared/SKILL.md +86 -0
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +127 -0
- package/skills/weaver-e10-shared/references/json-output-contract.md +120 -0
- package/skills/weaver-e10-shared/references/weaver-e10-installation.md +110 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +77 -0
- package/skills/weaver-e10-skill-maker/product.json +8 -0
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +124 -0
- package/skills/weaver-e10-skill-maker/references/detector-validation.md +101 -0
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +230 -0
- package/skills/weaver-e10-skill-maker/references/post-generation-install.md +105 -0
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +261 -0
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +141 -0
- package/skills/weaver-e10-wenshuding/SKILL.md +157 -0
- package/skills/weaver-e10-wenshuding/product.json +8 -0
- package/skills/weaver-e10-wenshuding/references/archive-agent-entry.md +63 -0
- package/skills/weaver-e10-wenshuding/references/archive-borrow-car.md +52 -0
- package/skills/weaver-e10-wenshuding/references/archive-borrow-list.md +52 -0
- package/skills/weaver-e10-wenshuding/references/archive-disabled-capabilities.md +22 -0
- package/skills/weaver-e10-wenshuding/references/archive-download.md +48 -0
- package/skills/weaver-e10-wenshuding/references/archive-flow.md +46 -0
- package/skills/weaver-e10-wenshuding/references/archive-fonds.md +34 -0
- package/skills/weaver-e10-wenshuding/references/archive-info.md +33 -0
- package/skills/weaver-e10-wenshuding/references/archive-search.md +52 -0
- package/skills/weaver-e10-wenshuding/references/archive-upload.md +41 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +517 -0
- package/skills/weaver-e10-yepiaotong/SKILL.md +184 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-add.md +1 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +47 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-browse-field-data.md +204 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-delete.md +1 -1
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-detail.md +13 -5
- package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +28 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-download.md +31 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +45 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-file-upload.md +15 -4
- package/skills/weaver-e10-yepiaotong/references/invoice-import.md +45 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +120 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-field-rules.md +161 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +99 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-ocr-preview.md +14 -4
- package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +58 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-red.md +261 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +97 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-update.md +1 -1
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-validation-preview.md +10 -3
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +1340 -0
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +845 -0
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +94 -0
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +449 -0
- package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +266 -0
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +494 -0
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1103 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +162 -0
- package/skills/weaver-e10-ziguanjia/product.json +8 -0
- package/skills/weaver-e10-ziguanjia/references/source-manifest.json +147 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +65 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-create.md +45 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-depre.md +48 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-disabled-capabilities.md +22 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-purch.md +57 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-query.md +62 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-repair.md +83 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-resolve.md +49 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-return.md +70 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-update.md +45 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-use.md +60 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-viewlink.md +81 -0
- package/skills/weaver-work-cli-invoice/SKILL.md +0 -103
- package/skills/weaver-work-cli-invoice/references/invoice-agent-entry.md +0 -38
- package/skills/weaver-work-cli-invoice/references/invoice-disabled-capabilities.md +0 -26
- package/skills/weaver-work-cli-invoice/references/invoice-download.md +0 -22
- package/skills/weaver-work-cli-invoice/references/invoice-enterprise-list.md +0 -35
- package/skills/weaver-work-cli-invoice/references/invoice-import.md +0 -28
- package/skills/weaver-work-cli-invoice/references/invoice-personal-list.md +0 -47
- package/skills/weaver-work-cli-shared/SKILL.md +0 -53
- package/skills/weaver-work-cli-shared/references/e10-auth-and-session.md +0 -83
- package/skills/weaver-work-cli-shared/references/json-output-contract.md +0 -61
- package/skills/weaver-work-cli-shared/references/weaver-work-cli-installation.md +0 -61
- /package/skills/{weaver-work-cli-shared → weaver-e10-shared}/references/high-risk-write.md +0 -0
- /package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/product.json +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ArchiveCliError } from '../errors.js';
|
|
2
|
+
import { archiveValidationError } from '../errors.js';
|
|
3
|
+
import { READ, WRITE, archiveSuccess, schemaOf, } from './types.js';
|
|
4
|
+
import { positiveInt, toStringId, toStringIdList, unwrapDatajson } from './shared.js';
|
|
5
|
+
import { BORROW_CAR_TAG, fetchArchiveListDatas, resolveArchiveListTarget } from './getPageUrl.js';
|
|
6
|
+
const CAR_ADD = 'archive.borrow-car.add';
|
|
7
|
+
function namesOf(value) {
|
|
8
|
+
if (!Array.isArray(value))
|
|
9
|
+
return [];
|
|
10
|
+
return value
|
|
11
|
+
.map((item) => (item && typeof item === 'object' ? String(item.name ?? '') : ''))
|
|
12
|
+
.filter((name) => name !== '');
|
|
13
|
+
}
|
|
14
|
+
function normalizeBorrowCarRow(mainTable, index) {
|
|
15
|
+
const row = (mainTable && typeof mainTable === 'object' ? mainTable : {});
|
|
16
|
+
const arcDangan = Array.isArray(row.arc_dangan)
|
|
17
|
+
? row.arc_dangan
|
|
18
|
+
.filter((item) => item && typeof item === 'object')
|
|
19
|
+
.map((item) => ({ name: String(item.name ?? ''), id: String(item.id ?? '') }))
|
|
20
|
+
: [];
|
|
21
|
+
return {
|
|
22
|
+
index: index + 1,
|
|
23
|
+
dataId: String(row.id ?? ''),
|
|
24
|
+
title: String(row.folder_title ?? ''),
|
|
25
|
+
className: String(row.class_name ?? ''),
|
|
26
|
+
archivalCode: String(row.archival_code ?? ''),
|
|
27
|
+
fondsCode: String(row.fonds_code ?? ''),
|
|
28
|
+
categoryNames: namesOf(row.category_code),
|
|
29
|
+
fileDate: String(row.file_date ?? ''),
|
|
30
|
+
retentionPeriod: String(row.retention_period ?? ''),
|
|
31
|
+
state: String(row.state ?? ''),
|
|
32
|
+
arcDangan,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** 首选自定义接口 myBorrowCar;接口未发布/报错时返回 null,由调用方回退。 */
|
|
36
|
+
async function listByCustomApi(host, operator, pageNo, pageSize) {
|
|
37
|
+
try {
|
|
38
|
+
const response = await host.request({
|
|
39
|
+
method: 'POST',
|
|
40
|
+
path: '/api/ebuilder/form/formdata/v2/getFormDataList/myBorrowCar',
|
|
41
|
+
body: {
|
|
42
|
+
datajson: {
|
|
43
|
+
mainTable: {},
|
|
44
|
+
pageInfo: { pageNo: String(pageNo), pageSize: String(pageSize) },
|
|
45
|
+
header: { operator },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
const box = unwrapDatajson(response);
|
|
50
|
+
const datas = Array.isArray(box?.datas) ? box.datas : [];
|
|
51
|
+
const rows = datas.map((item, index) => normalizeBorrowCarRow(item && typeof item === 'object' ? item.mainTable : undefined, index));
|
|
52
|
+
return { count: rows.length, rows };
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
// 接口未发布 / 报错 / 返回业务失败:交给回退链路
|
|
56
|
+
if (error instanceof ArchiveCliError && error.type === 'api')
|
|
57
|
+
return null;
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const borrowCarList = {
|
|
62
|
+
name: 'archive.borrow-car.list',
|
|
63
|
+
resource: 'borrow-car',
|
|
64
|
+
method: 'list',
|
|
65
|
+
action: 'archive.borrow-car.list',
|
|
66
|
+
summary: '查询当前用户的借阅车列表;优先自定义接口 myBorrowCar,不可用时回退 ebuilder 列表链路(getPageUrl → getListDatas)',
|
|
67
|
+
capability: 'archive',
|
|
68
|
+
risk: READ,
|
|
69
|
+
inputSchema: schemaOf({
|
|
70
|
+
pageNo: { type: 'integer', minimum: 1, description: '页码,默认 1' },
|
|
71
|
+
pageSize: { type: 'integer', minimum: 1, description: '每页条数,默认 10' },
|
|
72
|
+
}),
|
|
73
|
+
handler: async (args, { host }) => {
|
|
74
|
+
const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
|
|
75
|
+
const pageSize = positiveInt(args.pageSize, 'pageSize', 10);
|
|
76
|
+
const session = await host.getSession();
|
|
77
|
+
const custom = await listByCustomApi(host, session.userId, pageNo, pageSize);
|
|
78
|
+
if (custom) {
|
|
79
|
+
return archiveSuccess('archive.borrow-car.list', { source: 'myBorrowCar', pageNo, pageSize, count: custom.count, rows: custom.rows }, { source: 'myBorrowCar' });
|
|
80
|
+
}
|
|
81
|
+
const target = await resolveArchiveListTarget(host, { sign: BORROW_CAR_TAG, viewMode: 'default' });
|
|
82
|
+
const page = await fetchArchiveListDatas(host, target, pageNo, 'borrowCart');
|
|
83
|
+
const rows = page.rows.map((row, index) => ({
|
|
84
|
+
index: index + 1,
|
|
85
|
+
dataId: String(row.id ?? ''),
|
|
86
|
+
fields: row,
|
|
87
|
+
...(page.displayRows[index] ? { display: page.displayRows[index] } : {}),
|
|
88
|
+
}));
|
|
89
|
+
return archiveSuccess('archive.borrow-car.list', { source: 'ebuilder', pageNo: page.pageNo, count: rows.length, rows }, { source: 'ebuilder', viewSource: target.viewSource });
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
/** 加入借阅车入参:按表单分组的档案 ID。 */
|
|
93
|
+
function parseFormAndIds(value) {
|
|
94
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
95
|
+
throw archiveValidationError('form_and_ids_required', 'formAndIds 必填且至少一组');
|
|
96
|
+
}
|
|
97
|
+
return value.map((item, index) => {
|
|
98
|
+
if (!item || typeof item !== 'object') {
|
|
99
|
+
throw archiveValidationError('form_and_ids_invalid', `formAndIds[${index}] 必须是对象`);
|
|
100
|
+
}
|
|
101
|
+
const group = item;
|
|
102
|
+
const formId = toStringId(group.formId, `formAndIds[${index}].formId`);
|
|
103
|
+
const ids = toStringIdList(group.ids, `formAndIds[${index}].ids`);
|
|
104
|
+
if (!formId)
|
|
105
|
+
throw archiveValidationError('form_id_required', `formAndIds[${index}].formId 必填`);
|
|
106
|
+
if (!ids || ids.length === 0)
|
|
107
|
+
throw archiveValidationError('ids_required', `formAndIds[${index}].ids 至少一项`);
|
|
108
|
+
return { formId, ids };
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 加入借阅车:低风险写操作,直接执行,不需要 prepare / apply 确认链。
|
|
113
|
+
* 全宗等字段仅预归档上传需要,加入借阅车只依赖档案 ID({档案ID}_{表单ID})与所属表单。
|
|
114
|
+
*/
|
|
115
|
+
const borrowCarAdd = {
|
|
116
|
+
name: CAR_ADD,
|
|
117
|
+
resource: 'borrow-car',
|
|
118
|
+
method: 'add',
|
|
119
|
+
action: CAR_ADD,
|
|
120
|
+
summary: '加入借阅车(写操作,无需确认):把档案直接加入当前用户借阅车',
|
|
121
|
+
capability: 'archive',
|
|
122
|
+
risk: WRITE,
|
|
123
|
+
inputSchema: schemaOf({
|
|
124
|
+
formAndIds: {
|
|
125
|
+
type: 'array',
|
|
126
|
+
minItems: 1,
|
|
127
|
+
description: '按表单分组:[{ formId, ids: [档案ID...] }]',
|
|
128
|
+
items: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: { formId: { type: 'string' }, ids: { type: 'array', items: { type: 'string' } } },
|
|
131
|
+
required: ['formId', 'ids'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}, { required: ['formAndIds'], description: '加入借阅车' }),
|
|
135
|
+
handler: async (args, env) => {
|
|
136
|
+
const formAndIds = parseFormAndIds(args.formAndIds);
|
|
137
|
+
const response = await env.host.request({
|
|
138
|
+
method: 'POST',
|
|
139
|
+
path: '/api/archive/core/borrow/addBorrowCar',
|
|
140
|
+
body: { formAndIds, logFlag: 'archiveSet' },
|
|
141
|
+
});
|
|
142
|
+
const total = formAndIds.reduce((sum, group) => sum + group.ids.length, 0);
|
|
143
|
+
return archiveSuccess(CAR_ADD, { accepted: true, groups: formAndIds.length, total, message: response ?? '已加入借阅车' }, { wrote: true });
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
export const borrowCarOperations = [borrowCarList, borrowCarAdd];
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { ArchiveCliError, archiveValidationError } from '../errors.js';
|
|
2
|
+
import { READ, archiveSuccess, schemaOf, } from './types.js';
|
|
3
|
+
import { positiveInt, unwrapDatajson } from './shared.js';
|
|
4
|
+
import { BORROWING_TAG, fetchArchiveListDatas, resolveArchiveListTarget } from './getPageUrl.js';
|
|
5
|
+
/** 兼容「纯 ID 字符串」与「对象数组」两种字段形态(列表接口两者都会出现)。 */
|
|
6
|
+
function textOrName(value) {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
const first = value.find((item) => item !== undefined && item !== null);
|
|
9
|
+
if (first === undefined)
|
|
10
|
+
return '';
|
|
11
|
+
if (first && typeof first === 'object') {
|
|
12
|
+
const record = first;
|
|
13
|
+
return String(record.name ?? record.id ?? '');
|
|
14
|
+
}
|
|
15
|
+
return String(first);
|
|
16
|
+
}
|
|
17
|
+
if (value === undefined || value === null)
|
|
18
|
+
return '';
|
|
19
|
+
if (typeof value === 'object') {
|
|
20
|
+
const record = value;
|
|
21
|
+
return String(record.name ?? record.id ?? '');
|
|
22
|
+
}
|
|
23
|
+
return String(value);
|
|
24
|
+
}
|
|
25
|
+
/** 取 ID:对象数组取首元素 id,字符串形态直接返回(借阅单详情的 workflow_id 是纯字符串)。 */
|
|
26
|
+
function idOf(value) {
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
const first = value.find((item) => item !== undefined && item !== null);
|
|
29
|
+
if (first === undefined)
|
|
30
|
+
return '';
|
|
31
|
+
if (first && typeof first === 'object')
|
|
32
|
+
return String(first.id ?? '');
|
|
33
|
+
return String(first);
|
|
34
|
+
}
|
|
35
|
+
if (value === undefined || value === null)
|
|
36
|
+
return '';
|
|
37
|
+
if (typeof value === 'object')
|
|
38
|
+
return String(value.id ?? '');
|
|
39
|
+
return String(value);
|
|
40
|
+
}
|
|
41
|
+
/** 对象数组统一转成 { name, id },字符串形态返回空数组。 */
|
|
42
|
+
function entriesOf(value) {
|
|
43
|
+
if (!Array.isArray(value))
|
|
44
|
+
return [];
|
|
45
|
+
return value
|
|
46
|
+
.filter((item) => item && typeof item === 'object')
|
|
47
|
+
.map((item) => {
|
|
48
|
+
const record = item;
|
|
49
|
+
return { name: String(record.name ?? ''), id: String(record.id ?? '') };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function parseDateTime(value) {
|
|
53
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
54
|
+
if (!text)
|
|
55
|
+
return undefined;
|
|
56
|
+
const normalized = text.includes('T') ? text : text.replace(' ', 'T');
|
|
57
|
+
const parsed = Date.parse(normalized);
|
|
58
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
59
|
+
}
|
|
60
|
+
/** 借阅单是否已超期(无归还时间时不判定)。 */
|
|
61
|
+
function isExpired(returnDateTime, now = Date.now()) {
|
|
62
|
+
const due = parseDateTime(returnDateTime);
|
|
63
|
+
return due === undefined ? false : now > due;
|
|
64
|
+
}
|
|
65
|
+
function normalizeBorrowListRow(mainTable, index) {
|
|
66
|
+
const row = (mainTable && typeof mainTable === 'object' ? mainTable : {});
|
|
67
|
+
return {
|
|
68
|
+
index: index + 1,
|
|
69
|
+
dataId: String(row.id ?? ''),
|
|
70
|
+
name: String(row.name ?? ''),
|
|
71
|
+
borrowDateTime: String(row.borrow_date_time ?? ''),
|
|
72
|
+
returnDateTime: String(row.return_date_time ?? ''),
|
|
73
|
+
reason: String(row.reason ?? ''),
|
|
74
|
+
backStatus: String(row.back_status ?? ''),
|
|
75
|
+
returnStatus: String(row.return_status ?? ''),
|
|
76
|
+
waterMark: String(row.water_mark ?? ''),
|
|
77
|
+
workflowId: idOf(row.workflow_id),
|
|
78
|
+
workflowName: Array.isArray(row.workflow_id) ? textOrName(row.workflow_id) : '',
|
|
79
|
+
borrowPerson: textOrName(row.borrow_person),
|
|
80
|
+
state: String(row.data_status ?? ''),
|
|
81
|
+
createTime: String(row.create_time ?? ''),
|
|
82
|
+
updateTime: String(row.update_time ?? ''),
|
|
83
|
+
expired: isExpired(row.return_date_time),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async function listByCustomApi(host, operator, pageNo, pageSize) {
|
|
87
|
+
try {
|
|
88
|
+
const response = await host.request({
|
|
89
|
+
method: 'POST',
|
|
90
|
+
path: '/api/ebuilder/form/formdata/v2/getFormDataList/myBorrowList',
|
|
91
|
+
body: {
|
|
92
|
+
datajson: {
|
|
93
|
+
mainTable: {},
|
|
94
|
+
pageInfo: { pageNo: String(pageNo), pageSize: String(pageSize) },
|
|
95
|
+
header: { operator },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const box = unwrapDatajson(response);
|
|
100
|
+
const datas = Array.isArray(box?.datas) ? box.datas : [];
|
|
101
|
+
const rows = datas.map((item, index) => normalizeBorrowListRow(item && typeof item === 'object' ? item.mainTable : undefined, index));
|
|
102
|
+
return { count: rows.length, rows };
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error instanceof ArchiveCliError && error.type === 'api')
|
|
106
|
+
return null;
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const borrowListList = {
|
|
111
|
+
name: 'archive.borrow-list.list',
|
|
112
|
+
resource: 'borrow-list',
|
|
113
|
+
method: 'list',
|
|
114
|
+
action: 'archive.borrow-list.list',
|
|
115
|
+
summary: '查询当前用户的借阅单列表;优先自定义接口 myBorrowList,不可用时回退「我的借阅」视图链路',
|
|
116
|
+
capability: 'archive',
|
|
117
|
+
risk: READ,
|
|
118
|
+
inputSchema: schemaOf({
|
|
119
|
+
pageNo: { type: 'integer', minimum: 1, description: '页码,默认 1' },
|
|
120
|
+
pageSize: { type: 'integer', minimum: 1, description: '每页条数,默认 10' },
|
|
121
|
+
}),
|
|
122
|
+
handler: async (args, { host }) => {
|
|
123
|
+
const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
|
|
124
|
+
const pageSize = positiveInt(args.pageSize, 'pageSize', 10);
|
|
125
|
+
const session = await host.getSession();
|
|
126
|
+
const custom = await listByCustomApi(host, session.userId, pageNo, pageSize);
|
|
127
|
+
if (custom) {
|
|
128
|
+
return archiveSuccess('archive.borrow-list.list', { source: 'myBorrowList', pageNo, pageSize, count: custom.count, rows: custom.rows }, { source: 'myBorrowList' });
|
|
129
|
+
}
|
|
130
|
+
const target = await resolveArchiveListTarget(host, { sign: BORROWING_TAG, viewMode: 'myBorrow' });
|
|
131
|
+
const page = await fetchArchiveListDatas(host, target, pageNo);
|
|
132
|
+
const rows = page.rows.map((row, index) => ({
|
|
133
|
+
index: index + 1,
|
|
134
|
+
dataId: String(row.id ?? ''),
|
|
135
|
+
fields: row,
|
|
136
|
+
...(page.displayRows[index] ? { display: page.displayRows[index] } : {}),
|
|
137
|
+
}));
|
|
138
|
+
return archiveSuccess('archive.borrow-list.list', { source: 'ebuilder', pageNo: page.pageNo, count: rows.length, rows }, { source: 'ebuilder', viewSource: target.viewSource });
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
/** 借阅单详情(myBorrowDetail),下载前的两项前置校验也基于本接口。 */
|
|
142
|
+
export async function fetchBorrowDetail(host, operator, dataId) {
|
|
143
|
+
const response = await host.request({
|
|
144
|
+
method: 'POST',
|
|
145
|
+
path: '/api/ebuilder/form/formdata/v2/getFormDataByPk/myBorrowDetail',
|
|
146
|
+
body: { datajson: { mainTable: { id: dataId }, header: { operator } } },
|
|
147
|
+
});
|
|
148
|
+
const box = unwrapDatajson(response);
|
|
149
|
+
const datas = Array.isArray(box?.datas) ? box.datas : [];
|
|
150
|
+
if (datas.length === 0) {
|
|
151
|
+
throw archiveValidationError('borrow_not_found', `未找到借阅单(dataId=${dataId})`);
|
|
152
|
+
}
|
|
153
|
+
const record = (datas[0] && typeof datas[0] === 'object' ? datas[0] : {});
|
|
154
|
+
const mainTable = (record.mainTable && typeof record.mainTable === 'object' ? record.mainTable : {});
|
|
155
|
+
const details = (Array.isArray(record.detail1) ? record.detail1 : []).map((item) => {
|
|
156
|
+
const detail = (item && typeof item === 'object' ? item : {});
|
|
157
|
+
return {
|
|
158
|
+
id: String(detail.id ?? ''),
|
|
159
|
+
arcDangan: entriesOf(detail.arc_dangan),
|
|
160
|
+
archivalCode: String(detail.archival_code ?? ''),
|
|
161
|
+
fondsCode: String(detail.fonds_code ?? ''),
|
|
162
|
+
fileDate: String(detail.file_date ?? ''),
|
|
163
|
+
borrowTypes: entriesOf(detail.borrow_type),
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
dataId: String(mainTable.id ?? dataId),
|
|
168
|
+
name: String(mainTable.name ?? ''),
|
|
169
|
+
borrowDateTime: String(mainTable.borrow_date_time ?? ''),
|
|
170
|
+
returnDateTime: String(mainTable.return_date_time ?? ''),
|
|
171
|
+
reason: String(mainTable.reason ?? ''),
|
|
172
|
+
waterMark: String(mainTable.water_mark ?? ''),
|
|
173
|
+
workflowId: idOf(mainTable.workflow_id),
|
|
174
|
+
borrowPerson: textOrName(mainTable.borrow_person),
|
|
175
|
+
state: String(mainTable.data_status ?? ''),
|
|
176
|
+
expired: isExpired(mainTable.return_date_time),
|
|
177
|
+
downloadable: details.some((detail) => detail.borrowTypes.some((type) => type.id === '2')),
|
|
178
|
+
details,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const borrowListDetail = {
|
|
182
|
+
name: 'archive.borrow-list.detail',
|
|
183
|
+
resource: 'borrow-list',
|
|
184
|
+
method: 'detail',
|
|
185
|
+
action: 'archive.borrow-list.detail',
|
|
186
|
+
summary: '查看借阅单详情(含借阅明细与原文借阅方式),下载前的前置校验也依赖本接口',
|
|
187
|
+
capability: 'archive',
|
|
188
|
+
risk: READ,
|
|
189
|
+
inputSchema: schemaOf({ dataId: { type: 'string', pattern: '^\\d+$', description: '借阅单主表数据 ID' } }, { required: ['dataId'], description: '借阅单详情' }),
|
|
190
|
+
handler: async (args, { host }) => {
|
|
191
|
+
const dataId = args.dataId;
|
|
192
|
+
if (dataId === undefined || dataId === null || !/^\d+$/.test(String(dataId))) {
|
|
193
|
+
throw archiveValidationError('data_id_invalid', 'dataId 必填且为数字 ID');
|
|
194
|
+
}
|
|
195
|
+
const session = await host.getSession();
|
|
196
|
+
const detail = await fetchBorrowDetail(host, session.userId, String(dataId));
|
|
197
|
+
return archiveSuccess('archive.borrow-list.detail', detail, { count: detail.details.length });
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
export const borrowListOperations = [borrowListList, borrowListDetail];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { archiveConfirmationError, archiveValidationError } from '../errors.js';
|
|
2
|
+
/** apply 类写操作必须显式确认。 */
|
|
3
|
+
export function requireConfirm(args, action) {
|
|
4
|
+
if (args.confirm !== true) {
|
|
5
|
+
throw archiveConfirmationError(action, {
|
|
6
|
+
hint: `执行 ${action} 必须显式确认:传入 confirm=true(CLI 用 --yes)`,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* prepare 阶段签发 continuation。
|
|
12
|
+
* 环境指纹(baseUrl + 登录账号)写进票据,apply 时校验,避免跨环境/跨账号复用。
|
|
13
|
+
*/
|
|
14
|
+
export async function issueLease(env, action, payload, ttlMs = 10 * 60_000) {
|
|
15
|
+
const session = await env.host.getSession();
|
|
16
|
+
const continuation = await env.continuation.issue({
|
|
17
|
+
operation: action,
|
|
18
|
+
baseUrl: session.baseUrl,
|
|
19
|
+
userId: session.userId,
|
|
20
|
+
...payload,
|
|
21
|
+
}, ttlMs);
|
|
22
|
+
return { continuation, expiresInMs: ttlMs };
|
|
23
|
+
}
|
|
24
|
+
/** apply 阶段校验确认 + continuation,返回 prepare 时写入的快照。 */
|
|
25
|
+
export async function verifyLease(args, action, env) {
|
|
26
|
+
requireConfirm(args, action);
|
|
27
|
+
if (typeof args.continuation !== 'string' || !args.continuation.trim()) {
|
|
28
|
+
throw archiveValidationError('continuation_required', `${action} 必须传入 prepare 返回的 continuation`);
|
|
29
|
+
}
|
|
30
|
+
const lease = await env.continuation.verify(args.continuation.trim(), action);
|
|
31
|
+
const session = await env.host.getSession();
|
|
32
|
+
if (lease.baseUrl !== session.baseUrl || lease.userId !== session.userId) {
|
|
33
|
+
throw archiveValidationError('continuation_context_mismatch', '确认上下文已变化(环境或登录账号不同),请重新执行 prepare');
|
|
34
|
+
}
|
|
35
|
+
return lease;
|
|
36
|
+
}
|
|
37
|
+
/** 校验 apply 入参与 prepare 快照一致,避免目标漂移。 */
|
|
38
|
+
export function assertLeaseTarget(lease, args, keys, action) {
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
if (args[key] === undefined || args[key] === null || args[key] === '')
|
|
41
|
+
continue;
|
|
42
|
+
if (String(lease[key] ?? '') !== String(args[key])) {
|
|
43
|
+
throw archiveValidationError('continuation_target_changed', `${action} 的参数 ${key} 与 prepare 时不一致,请重新执行 prepare`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { basename, dirname, extname, resolve } from 'node:path';
|
|
5
|
+
import { archiveValidationError, ArchiveCliError } from '../errors.js';
|
|
6
|
+
import { READ, WRITE, archiveSuccess, schemaOf, } from './types.js';
|
|
7
|
+
import { unwrapData } from './shared.js';
|
|
8
|
+
import { assertLeaseTarget, issueLease, verifyLease } from './confirm.js';
|
|
9
|
+
import { fetchBorrowDetail } from './borrow-list.js';
|
|
10
|
+
const DOWNLOAD_APPLY = 'archive.borrow-list.download.apply';
|
|
11
|
+
const EXPORT_TYPES = ['0', '1'];
|
|
12
|
+
const EXPORT_STRUCTURES = ['', '0', '1', '2'];
|
|
13
|
+
const GROUP_FIELDS = [
|
|
14
|
+
'archival_code',
|
|
15
|
+
'folder_title',
|
|
16
|
+
'fonds_code',
|
|
17
|
+
'arc_class_code',
|
|
18
|
+
'category_code',
|
|
19
|
+
'month',
|
|
20
|
+
'year',
|
|
21
|
+
'retention_period',
|
|
22
|
+
'security_classificat',
|
|
23
|
+
'owner_user',
|
|
24
|
+
'owner_dept',
|
|
25
|
+
'owner_company',
|
|
26
|
+
];
|
|
27
|
+
const POLL_INTERVAL_MS = 2_000;
|
|
28
|
+
const POLL_MAX_ATTEMPTS = 150;
|
|
29
|
+
function sleep(ms) {
|
|
30
|
+
return new Promise((done) => {
|
|
31
|
+
setTimeout(done, ms);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function optionalText(value, field) {
|
|
35
|
+
if (value === undefined || value === null || value === '')
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof value !== 'string') {
|
|
38
|
+
throw archiveValidationError('field_invalid', `${field} 必须是字符串`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function parseExportParams(args) {
|
|
43
|
+
const exportType = args.exportType === undefined || args.exportType === null || args.exportType === ''
|
|
44
|
+
? '0'
|
|
45
|
+
: String(args.exportType);
|
|
46
|
+
if (!EXPORT_TYPES.includes(exportType)) {
|
|
47
|
+
throw archiveValidationError('export_type_invalid', 'exportType 只能是 0(直接导出)或 1(分组导出)');
|
|
48
|
+
}
|
|
49
|
+
const exportStructure = optionalText(args.exportStructure, 'exportStructure') ?? '';
|
|
50
|
+
const groupField = optionalText(args.groupField, 'groupField') ?? '';
|
|
51
|
+
const documentField = optionalText(args.documentField, 'documentField') ?? '';
|
|
52
|
+
if (!EXPORT_STRUCTURES.includes(exportStructure)) {
|
|
53
|
+
throw archiveValidationError('export_structure_invalid', 'exportStructure 只能是空、0、1、2');
|
|
54
|
+
}
|
|
55
|
+
if (exportType === '0' && (exportStructure !== '' || groupField !== '' || documentField !== '')) {
|
|
56
|
+
throw archiveValidationError('export_params_conflict', '直接导出(exportType=0)时其余三个分组字段必须为空');
|
|
57
|
+
}
|
|
58
|
+
if (exportType === '1') {
|
|
59
|
+
if (exportStructure === '') {
|
|
60
|
+
throw archiveValidationError('export_structure_required', '分组导出(exportType=1)时 exportStructure 必填');
|
|
61
|
+
}
|
|
62
|
+
if (exportStructure === '0' && !groupField) {
|
|
63
|
+
throw archiveValidationError('group_field_required', '按条目导出时 groupField 必填');
|
|
64
|
+
}
|
|
65
|
+
if (exportStructure === '1' && !documentField) {
|
|
66
|
+
throw archiveValidationError('document_field_required', '按原文导出时 documentField 必填');
|
|
67
|
+
}
|
|
68
|
+
if (exportStructure === '2' && (!groupField || !documentField)) {
|
|
69
|
+
throw archiveValidationError('group_fields_required', '按条目+原文导出时 groupField 与 documentField 均必填');
|
|
70
|
+
}
|
|
71
|
+
for (const [field, value] of [['groupField', groupField], ['documentField', documentField]]) {
|
|
72
|
+
if (value && !GROUP_FIELDS.includes(value)) {
|
|
73
|
+
throw archiveValidationError('group_field_invalid', `${field} 取值不在档案基础字段候选内`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { exportType, exportStructure, groupField, documentField };
|
|
78
|
+
}
|
|
79
|
+
/** 下载前校验:未超期 + 有可下载原文。 */
|
|
80
|
+
async function assertDownloadable(host, operator, dataId) {
|
|
81
|
+
const detail = await fetchBorrowDetail(host, operator, dataId);
|
|
82
|
+
if (detail.expired) {
|
|
83
|
+
throw archiveValidationError('borrow_expired', '借阅已过期,无权限下载!');
|
|
84
|
+
}
|
|
85
|
+
if (!detail.downloadable) {
|
|
86
|
+
throw archiveValidationError('borrow_no_downloadable', '该借阅单没有可下载的电子原文,无权限下载!');
|
|
87
|
+
}
|
|
88
|
+
if (!detail.workflowId) {
|
|
89
|
+
throw archiveValidationError('workflow_id_missing', '借阅单缺少流程 requestId(workflow_id),无法打包下载');
|
|
90
|
+
}
|
|
91
|
+
return { workflowId: detail.workflowId, returnDateTime: detail.returnDateTime };
|
|
92
|
+
}
|
|
93
|
+
function defaultDownloadDir() {
|
|
94
|
+
return resolve(homedir(), 'Downloads');
|
|
95
|
+
}
|
|
96
|
+
/** 目标路径:默认落到用户「下载」目录,已存在时自动加序号避免覆盖。 */
|
|
97
|
+
function resolveOutputPath(fileName, output) {
|
|
98
|
+
const safeName = basename(String(fileName || '').replace(/[\\/]/g, '_')) || 'borrow.zip';
|
|
99
|
+
const target = output && String(output).trim() ? resolve(String(output)) : resolve(defaultDownloadDir(), safeName);
|
|
100
|
+
let candidate = target;
|
|
101
|
+
if (existsSync(candidate)) {
|
|
102
|
+
const ext = extname(safeName);
|
|
103
|
+
const stem = ext ? safeName.slice(0, -ext.length) : safeName;
|
|
104
|
+
for (let index = 1; index < 1000; index += 1) {
|
|
105
|
+
candidate = resolve(dirname(target), `${stem} (${index})${ext}`);
|
|
106
|
+
if (!existsSync(candidate))
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return candidate;
|
|
111
|
+
}
|
|
112
|
+
const downloadPrepare = {
|
|
113
|
+
name: 'archive.borrow-list.download.prepare',
|
|
114
|
+
resource: 'borrow-list',
|
|
115
|
+
method: 'download',
|
|
116
|
+
action: 'archive.borrow-list.download.prepare',
|
|
117
|
+
summary: '下载借阅单原文前的预检:校验未超期且存在可下载原文,并生成确认票据',
|
|
118
|
+
capability: 'archive',
|
|
119
|
+
risk: READ,
|
|
120
|
+
inputSchema: schemaOf({
|
|
121
|
+
dataId: { type: 'string', pattern: '^\\d+$', description: '借阅单主表数据 ID' },
|
|
122
|
+
exportType: { type: 'string', description: '0=直接导出(默认),1=分组导出' },
|
|
123
|
+
exportStructure: { type: 'string', description: '分组导出结构:0 按条目 / 1 按原文 / 2 按条目+原文' },
|
|
124
|
+
groupField: { type: 'string', description: '条目分组字段 ID' },
|
|
125
|
+
documentField: { type: 'string', description: '原文分组字段 ID' },
|
|
126
|
+
output: { type: 'string', description: '可选,zip 落盘路径;默认保存到用户「下载」目录' },
|
|
127
|
+
}, { required: ['dataId'], description: '借阅单下载预检' }),
|
|
128
|
+
handler: async (args, env) => {
|
|
129
|
+
const dataId = args.dataId;
|
|
130
|
+
if (dataId === undefined || dataId === null || !/^\d+$/.test(String(dataId))) {
|
|
131
|
+
throw archiveValidationError('data_id_invalid', 'dataId 必填且为数字 ID');
|
|
132
|
+
}
|
|
133
|
+
const exportParams = parseExportParams(args);
|
|
134
|
+
const session = await env.host.getSession();
|
|
135
|
+
const { workflowId, returnDateTime } = await assertDownloadable(env.host, session.userId, String(dataId));
|
|
136
|
+
const output = optionalText(args.output, 'output');
|
|
137
|
+
const { continuation, expiresInMs } = await issueLease(env, DOWNLOAD_APPLY, {
|
|
138
|
+
dataId: String(dataId),
|
|
139
|
+
requestId: workflowId,
|
|
140
|
+
exportParams,
|
|
141
|
+
...(output ? { output } : {}),
|
|
142
|
+
});
|
|
143
|
+
return archiveSuccess('archive.borrow-list.download.prepare', {
|
|
144
|
+
preview: {
|
|
145
|
+
dataId: String(dataId),
|
|
146
|
+
requestId: workflowId,
|
|
147
|
+
returnDateTime,
|
|
148
|
+
...exportParams,
|
|
149
|
+
...(output ? { output } : { outputDir: defaultDownloadDir() }),
|
|
150
|
+
},
|
|
151
|
+
hint: '确认后调用 archive.borrow-list.download.apply(CLI:archive borrow-list download-apply --continuation <token> --yes)',
|
|
152
|
+
continuation,
|
|
153
|
+
}, { requiresConfirmation: true, expiresInMs });
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
async function pollExport(host, bizId) {
|
|
157
|
+
const query = { moduleName: 'archive', functionName: 'downloadBorrowZip', bizId, flag: 'export' };
|
|
158
|
+
for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt += 1) {
|
|
159
|
+
await sleep(POLL_INTERVAL_MS);
|
|
160
|
+
const response = await host.request({ method: 'GET', path: '/api/ebatch/getImportOrExportRate', query });
|
|
161
|
+
const percent = Number(unwrapData(response));
|
|
162
|
+
if (process.stderr.isTTY) {
|
|
163
|
+
const value = Number.isFinite(percent) ? Math.max(0, Math.min(100, Math.round(percent))) : 0;
|
|
164
|
+
process.stderr.write(`\r${'#'.repeat(Math.floor(value / 2))} ${value}%`);
|
|
165
|
+
}
|
|
166
|
+
if (Number.isFinite(percent) && percent >= 100)
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (process.stderr.isTTY)
|
|
170
|
+
process.stderr.write('\n');
|
|
171
|
+
const response = await host.request({ method: 'GET', path: '/api/ebatch/getImportOrExportRateNum', query });
|
|
172
|
+
const data = (unwrapData(response) ?? {});
|
|
173
|
+
if (!data.fileId || data.status !== true) {
|
|
174
|
+
throw new ArchiveCliError('api', 'export_not_ready', '借阅单打包未完成,请稍后重试', {
|
|
175
|
+
details: { bizId, ...(data.fileName ? { fileName: data.fileName } : {}) },
|
|
176
|
+
exitCode: 1,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return data;
|
|
180
|
+
}
|
|
181
|
+
const downloadApply = {
|
|
182
|
+
name: DOWNLOAD_APPLY,
|
|
183
|
+
resource: 'borrow-list',
|
|
184
|
+
method: 'download',
|
|
185
|
+
action: DOWNLOAD_APPLY,
|
|
186
|
+
summary: '下载借阅单原文(写操作):提交异步打包任务 → 轮询进度 → 下载 zip 并落盘',
|
|
187
|
+
capability: 'archive',
|
|
188
|
+
risk: WRITE,
|
|
189
|
+
requiresConfirmation: true,
|
|
190
|
+
inputSchema: schemaOf({
|
|
191
|
+
continuation: { type: 'string', description: 'prepare 返回的确认票据' },
|
|
192
|
+
confirm: { type: 'boolean', description: '必须为 true(CLI --yes)' },
|
|
193
|
+
dataId: { type: 'string', description: '可选回传,用于与 prepare 快照比对' },
|
|
194
|
+
}, { required: ['continuation', 'confirm'], description: '下载借阅单原文' }),
|
|
195
|
+
handler: async (args, env) => {
|
|
196
|
+
const lease = await verifyLease(args, DOWNLOAD_APPLY, env);
|
|
197
|
+
const dataId = String(lease.dataId ?? '');
|
|
198
|
+
const requestId = String(lease.requestId ?? '');
|
|
199
|
+
if (!dataId || !requestId) {
|
|
200
|
+
throw archiveValidationError('continuation_payload_invalid', '确认票据缺少 dataId / requestId');
|
|
201
|
+
}
|
|
202
|
+
assertLeaseTarget(lease, args, ['dataId'], DOWNLOAD_APPLY);
|
|
203
|
+
const exportParams = (lease.exportParams ?? {});
|
|
204
|
+
const session = await env.host.getSession();
|
|
205
|
+
await assertDownloadable(env.host, session.userId, dataId);
|
|
206
|
+
const submit = await env.host.request({
|
|
207
|
+
method: 'POST',
|
|
208
|
+
path: '/api/archive/core/borrow/batchDownloadBorrowZip',
|
|
209
|
+
body: {
|
|
210
|
+
dataId,
|
|
211
|
+
requestId,
|
|
212
|
+
exportType: String(exportParams.exportType ?? '0'),
|
|
213
|
+
exportStructure: String(exportParams.exportStructure ?? ''),
|
|
214
|
+
groupField: String(exportParams.groupField ?? ''),
|
|
215
|
+
documentField: String(exportParams.documentField ?? ''),
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
const submitData = (unwrapData(submit) ?? {});
|
|
219
|
+
const bizId = String(submitData.bizId ?? '');
|
|
220
|
+
if (!bizId) {
|
|
221
|
+
throw new ArchiveCliError('api', 'export_submit_failed', '提交借阅单打包任务失败', { exitCode: 1 });
|
|
222
|
+
}
|
|
223
|
+
const rate = await pollExport(env.host, bizId);
|
|
224
|
+
const fileId = String(rate.fileId ?? '');
|
|
225
|
+
const fileName = String(rate.fileName ?? submitData.fileName ?? `borrow-${dataId}.zip`);
|
|
226
|
+
const binary = await env.host.requestBinary({
|
|
227
|
+
method: 'GET',
|
|
228
|
+
path: `/api/file/remotedownload/${fileId}/token/true`,
|
|
229
|
+
query: { authModule: 'ebatch' },
|
|
230
|
+
});
|
|
231
|
+
const output = typeof lease.output === 'string' && lease.output ? lease.output : undefined;
|
|
232
|
+
const target = resolveOutputPath(fileName, output);
|
|
233
|
+
await mkdir(dirname(target), { recursive: true });
|
|
234
|
+
await writeFile(target, binary.content, { flag: 'wx', mode: 0o600 });
|
|
235
|
+
return archiveSuccess(DOWNLOAD_APPLY, { savedPath: target, fileName: basename(target), fileId, bizId, size: binary.content.length }, { wrote: true, contentType: binary.contentType });
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
export const downloadOperations = [downloadPrepare, downloadApply];
|