weaver-work-cli 0.1.42 → 0.1.44
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 +17 -0
- package/dist/chunks/{archive-J4I6MCKF.js → archive-P6TBQGKL.js} +13 -5
- package/dist/chunks/{archive-oa-BRZKKTO4.js → archive-oa-7TQ2NXOH.js} +12 -5
- package/dist/chunks/{asset-L7D4FAOZ.js → asset-3ELRG37G.js} +11 -4
- package/dist/chunks/{asset-idle-DADLLV75.js → asset-idle-WIGMUFMD.js} +7 -3
- package/dist/chunks/{attachment-content-K4C7VLWQ.js → attachment-content-E3276UOV.js} +5 -4
- package/dist/chunks/{blog-BVGQ7NZL.js → blog-C5NMZAAC.js} +11 -4
- package/dist/chunks/{caizhilian-SAADNFD6.js → caizhilian-5XSM2DFP.js} +13 -6
- package/dist/chunks/{calendar-Q7NQY5Z2.js → calendar-FI2WNSBO.js} +12 -5
- package/dist/chunks/{chunk-YRSZQUD2.js → chunk-3YBDYOC4.js} +3 -3
- package/dist/chunks/{chunk-OOUTBLZ4.js → chunk-56EZERK4.js} +1 -1
- package/dist/chunks/chunk-6N4JPNFL.js +76 -0
- package/dist/chunks/{chunk-ZUX3P6BQ.js → chunk-727FH2NI.js} +1 -1
- package/dist/chunks/chunk-7BDPM5ST.js +11 -0
- package/dist/chunks/{chunk-EKWAQSLU.js → chunk-BCVYBYYT.js} +432 -2248
- package/dist/chunks/{chunk-W7BEHOSR.js → chunk-CWYGFU5B.js} +2 -2
- package/dist/chunks/{chunk-NOODWMCL.js → chunk-D4ADSZ7H.js} +1 -1
- package/dist/chunks/{chunk-SA6VBIHY.js → chunk-ETNJSKMM.js} +214 -16
- package/dist/chunks/{chunk-BBKDXQEC.js → chunk-F4E4EM35.js} +3 -3
- package/dist/chunks/{chunk-IZ7S7QUS.js → chunk-FAZTGX7S.js} +6 -6
- package/dist/chunks/{chunk-NKZRQXST.js → chunk-FRZKBMSW.js} +52 -36
- package/dist/chunks/{chunk-BYBSCN4X.js → chunk-G7PQWD4L.js} +6 -3
- package/dist/chunks/{chunk-GEWYMQME.js → chunk-GAMZLAG2.js} +1 -1
- package/dist/chunks/{chunk-R4CHUSKC.js → chunk-GM6J7Q4B.js} +4 -4
- package/dist/chunks/{chunk-APVFJUQH.js → chunk-GRDRG6HP.js} +3 -3
- package/dist/chunks/{chunk-LDZQPUIA.js → chunk-HIT4Y6OI.js} +1 -1
- package/dist/chunks/{chunk-EZALDOQH.js → chunk-HP7JIYMT.js} +5 -2
- package/dist/chunks/{chunk-LADPYOWC.js → chunk-IAZ3UPY6.js} +3 -3
- package/dist/chunks/{chunk-ZEDWSH32.js → chunk-IC62NFM5.js} +1 -1
- package/dist/chunks/{chunk-P26ZWSKU.js → chunk-KIHNAS25.js} +5 -2
- package/dist/chunks/{chunk-BIPYLLVS.js → chunk-N4M2DDFE.js} +15 -15
- package/dist/chunks/{chunk-QKNKR4RH.js → chunk-OF4S23UB.js} +4 -4
- package/dist/chunks/{chunk-JPRO6ER2.js → chunk-PC7VKEB5.js} +57 -60
- package/dist/chunks/{chunk-4PD5NWAX.js → chunk-PUQS5MWL.js} +2 -2
- package/dist/chunks/{chunk-2LV74IWP.js → chunk-Q4OYAQYH.js} +1 -1
- package/dist/chunks/{chunk-72CKAUAG.js → chunk-QVSWRLLU.js} +7 -5
- package/dist/chunks/{chunk-4RAE6JXP.js → chunk-TA7FXXJD.js} +6 -3
- package/dist/chunks/{chunk-GPMFD4RD.js → chunk-TXUG266F.js} +1 -1
- package/dist/chunks/{chunk-FOAA27U5.js → chunk-UFIT2UU3.js} +16 -4
- package/dist/chunks/{chunk-LUSPKZKY.js → chunk-UG5G54VL.js} +1 -0
- package/dist/chunks/{chunk-NIAJ5ZCG.js → chunk-UL5XUQ25.js} +5 -2
- package/dist/chunks/{chunk-FMPJJYYI.js → chunk-WRXSAVKO.js} +1 -1
- package/dist/chunks/{chunk-G4UR6NXN.js → chunk-WSF4NNMR.js} +3 -3
- package/dist/chunks/chunk-XHK4MDR5.js +2497 -0
- package/dist/chunks/{chunk-3KTFS4IA.js → chunk-XNIK66I4.js} +19 -9
- package/dist/chunks/{chunk-3SO6QL6U.js → chunk-YWX377BY.js} +5 -2
- package/dist/chunks/{chunk-BYATLLEF.js → chunk-Z3GDFVJR.js} +1 -1
- package/dist/chunks/{chunk-GHXCDPTM.js → chunk-Z7VYWPZV.js} +1 -1
- package/dist/chunks/{conflict-F2OMZRTO.js → conflict-ARLSZJ6V.js} +4 -3
- package/dist/chunks/{continuation-2CKSBLA6.js → continuation-3S37AUSH.js} +4 -3
- package/dist/chunks/{continuation-RN6PZN2M.js → continuation-3XLYYTKV.js} +4 -3
- package/dist/chunks/{continuation-HZIZRZBX.js → continuation-HFW2YT5S.js} +4 -3
- package/dist/chunks/{continuation-NLECEJPX.js → continuation-KI7DRRZN.js} +4 -3
- package/dist/chunks/{contract-5BETHRSG.js → contract-DNIIW5VJ.js} +13 -5
- package/dist/chunks/{cowork-3MN6EQQO.js → cowork-GMLRGVIW.js} +11 -4
- package/dist/chunks/{create-flow-5FQNNMVO.js → create-flow-J22BO65E.js} +8 -7
- package/dist/chunks/{ebuilder-form-SBHOYBUD.js → ebuilder-form-ANV2FHYV.js} +12 -7
- package/dist/chunks/{ehr-KMOJULKG.js → ehr-GSTGAIY3.js} +266 -24
- package/dist/chunks/{employee-SAK2QYXX.js → employee-22BGJ3QS.js} +3 -3
- package/dist/chunks/{env-LB7NBL2O.js → env-BX3VRFS7.js} +4 -3
- package/dist/chunks/{esb-W2PJCFCX.js → esb-ATAIASX2.js} +12 -7
- package/dist/chunks/{factory-6ZFQXQ46.js → factory-PETTFME6.js} +3 -2
- package/dist/chunks/{fna-ZQPCUV4R.js → fna-YEKBSFSX.js} +14 -8
- package/dist/chunks/{group-chat-2HHAJN46.js → group-chat-BFVXAYFX.js} +5 -4
- package/dist/chunks/{host-ATKVNZAT.js → host-HRBXEPDQ.js} +5 -5
- package/dist/chunks/{host-BQH22NTQ.js → host-LR55CTHP.js} +5 -5
- package/dist/chunks/{host-X2CSLFM6.js → host-SUOY6VYQ.js} +5 -5
- package/dist/chunks/{host-4CBHGOHT.js → host-SZGV4UNT.js} +5 -5
- package/dist/chunks/{host-YTODKMSL.js → host-WIKN2EJ5.js} +5 -5
- package/dist/chunks/{hrm-FFM4UJFS.js → hrm-NRDO4W5G.js} +11 -7
- package/dist/chunks/{im-VEHKDQPB.js → im-IQ2HN23S.js} +8 -7
- package/dist/chunks/{invoice-LVIKSIIF.js → invoice-MNRW76PP.js} +636 -1768
- package/dist/chunks/{jiuchuanhui-X6DINO5P.js → jiuchuanhui-DTRBAX4N.js} +14 -6
- package/dist/chunks/{lazy-dispatch-KSWTN5L6.js → lazy-dispatch-ZVWAI25B.js} +7 -6
- package/dist/chunks/{mail-VDKRGUD3.js → mail-ZVAUXOJA.js} +16 -14
- package/dist/chunks/{meeting-CLC6HP3Z.js → meeting-FEVPZ67S.js} +21 -14
- package/dist/chunks/{okr-CHG4OO3U.js → okr-SDH3E5RA.js} +12 -5
- package/dist/chunks/{operations-5QL3AECO.js → operations-62N6EP3C.js} +4 -3
- package/dist/chunks/{plan-XWR75667.js → plan-E35JFDI7.js} +12 -5
- package/dist/chunks/{project-5BXOOKMG.js → project-73QXBAB2.js} +11 -4
- package/dist/chunks/reception-UWZVACBN.js +1899 -0
- package/dist/chunks/{recruit-stage-DGMFNNDE.js → recruit-stage-4MAOCKFY.js} +320 -57
- package/dist/chunks/registry-IM5G23LS.js +24 -0
- package/dist/chunks/{registry-RHMW6ANW.js → registry-JDUS4E7D.js} +7 -6
- package/dist/chunks/{registry-7FFOXIAW.js → registry-KNKSCJNT.js} +7 -6
- package/dist/chunks/{registry-Z43434VG.js → registry-PO7SI256.js} +4 -4
- package/dist/chunks/{registry-OZSMDVPP.js → registry-PVUZQDDQ.js} +3 -3
- package/dist/chunks/{room-recommend-62H7ZQQ6.js → room-recommend-H7KLXA5R.js} +6 -5
- package/dist/chunks/{schedule-FNXKH3DQ.js → schedule-ESZIBAUO.js} +6 -5
- package/dist/chunks/{workflow-ADHKCAJK.js → workflow-CWJFJLQU.js} +13 -8
- package/dist/cmd/action.js +6 -0
- package/dist/cmd/identity/index.js +45 -0
- package/dist/cmd/index.js +2 -0
- package/dist/core/continuation-ttl.js +18 -0
- package/dist/core/error-codes.js +85 -0
- package/dist/core/logger.js +11 -0
- package/dist/index.js +37 -31
- package/dist/internal/e10/auth/commands.js +163 -8
- package/dist/internal/e10/auth/error-log.js +182 -0
- package/dist/internal/e10/auth/session.js +17 -3
- package/dist/internal/e10/auth-recovery.js +39 -12
- package/dist/internal/e10/context.js +17 -2
- package/dist/internal/skills/install.js +9 -1
- package/dist/shortcuts/archive/continuation.js +2 -1
- package/dist/shortcuts/archive/index.js +3 -0
- package/dist/shortcuts/archive/operations/confirm.js +2 -1
- package/dist/shortcuts/archive-oa/continuation.js +2 -1
- package/dist/shortcuts/archive-oa/index.js +2 -0
- package/dist/shortcuts/archive-oa/operations/confirm.js +2 -1
- package/dist/shortcuts/asset/continuation.js +2 -1
- package/dist/shortcuts/asset/index.js +2 -0
- package/dist/shortcuts/asset-idle/index.js +2 -0
- package/dist/shortcuts/blog/continuation.js +2 -1
- package/dist/shortcuts/blog/index.js +2 -0
- package/dist/shortcuts/caizhilian/continuation.js +2 -1
- package/dist/shortcuts/caizhilian/index.js +2 -0
- package/dist/shortcuts/caizhilian/operations/confirm.js +3 -2
- package/dist/shortcuts/calendar/continuation.js +2 -1
- package/dist/shortcuts/calendar/index.js +2 -0
- package/dist/shortcuts/calendar/operations/write.js +1 -1
- package/dist/shortcuts/contract/continuation.js +2 -1
- package/dist/shortcuts/contract/index.js +2 -0
- package/dist/shortcuts/contract/operations/write.js +2 -1
- package/dist/shortcuts/cowork/continuation.js +2 -1
- package/dist/shortcuts/cowork/index.js +2 -0
- package/dist/shortcuts/ebuilder-form/continuation.js +2 -1
- package/dist/shortcuts/ebuilder-form/index.js +2 -0
- package/dist/shortcuts/ebuilder-form/operations/write.js +3 -2
- package/dist/shortcuts/ehr/continuation.js +2 -1
- package/dist/shortcuts/ehr/index.js +2 -0
- package/dist/shortcuts/ehr/operations/attend-write.js +1 -1
- package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +182 -3
- package/dist/shortcuts/ehr/operations/recruit-position.js +134 -2
- package/dist/shortcuts/ehr/operations/recruit-write.js +1 -1
- package/dist/shortcuts/esb/continuation.js +2 -1
- package/dist/shortcuts/esb/index.js +2 -0
- package/dist/shortcuts/esb/operations/trigger.js +2 -1
- package/dist/shortcuts/fna/continuation.js +2 -1
- package/dist/shortcuts/fna/index.js +2 -0
- package/dist/shortcuts/fna/operations/invoice-writeoff.js +1 -1
- package/dist/shortcuts/fna/operations/loan.js +1 -1
- package/dist/shortcuts/hrm/index.js +2 -0
- package/dist/shortcuts/im/continuation.js +2 -1
- package/dist/shortcuts/index.js +5 -0
- package/dist/shortcuts/invoice/continuation.js +2 -1
- package/dist/shortcuts/invoice/index.js +14 -15
- package/dist/shortcuts/invoice/invoice-record.js +93 -0
- package/dist/shortcuts/invoice/manifest.js +190 -393
- package/dist/shortcuts/invoice/operations/add.js +2 -1
- package/dist/shortcuts/invoice/operations/delete.js +2 -1
- package/dist/shortcuts/invoice/operations/import.js +2 -1
- package/dist/shortcuts/invoice/operations/issuing-contract-detail.js +86 -0
- package/dist/shortcuts/invoice/operations/issuing-contracts.js +210 -0
- package/dist/shortcuts/invoice/operations/registry.js +17 -17
- package/dist/shortcuts/invoice/operations/reim-auto.js +4 -569
- package/dist/shortcuts/invoice/operations/reim.js +52 -341
- package/dist/shortcuts/invoice/operations/shared.js +21 -4
- package/dist/shortcuts/invoice/operations/update.js +2 -1
- package/dist/shortcuts/invoice/projection.js +10 -62
- package/dist/shortcuts/jiuchuanhui/continuation.js +2 -1
- package/dist/shortcuts/jiuchuanhui/index.js +2 -0
- package/dist/shortcuts/jiuchuanhui/operations/datajson.js +2 -1
- package/dist/shortcuts/jiuchuanhui/operations/esb.js +2 -1
- package/dist/shortcuts/mail/continuation.js +2 -1
- package/dist/shortcuts/mail/index.js +2 -0
- package/dist/shortcuts/mail/operations/mail-blacklist.js +1 -1
- package/dist/shortcuts/mail/operations/mail-contacts.js +1 -1
- package/dist/shortcuts/mail/operations/mail-send.js +1 -1
- package/dist/shortcuts/mail/operations/mail-sign.js +1 -1
- package/dist/shortcuts/mail/operations/mail-template.js +1 -1
- package/dist/shortcuts/meeting/continuation.js +2 -1
- package/dist/shortcuts/meeting/index.js +2 -0
- package/dist/shortcuts/meeting/operations/change.js +1 -1
- package/dist/shortcuts/meeting/operations/create.js +1 -1
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +16 -7
- package/dist/shortcuts/meeting/operations/eb/decision.js +1 -1
- package/dist/shortcuts/meeting/operations/eb/factory.js +1 -1
- package/dist/shortcuts/meeting/operations/lifecycle.js +1 -1
- package/dist/shortcuts/meeting/operations/receipts.js +1 -1
- package/dist/shortcuts/meeting/operations/signs.js +1 -1
- package/dist/shortcuts/okr/continuation.js +2 -1
- package/dist/shortcuts/okr/index.js +2 -0
- package/dist/shortcuts/okr/operations/write.js +1 -1
- package/dist/shortcuts/plan/continuation.js +2 -1
- package/dist/shortcuts/plan/index.js +2 -0
- package/dist/shortcuts/plan/operations/write.js +1 -1
- package/dist/shortcuts/project/continuation.js +2 -1
- package/dist/shortcuts/project/index.js +2 -0
- package/dist/shortcuts/reception/constants.js +53 -0
- package/dist/shortcuts/reception/errors.js +54 -0
- package/dist/shortcuts/reception/host.js +125 -0
- package/dist/shortcuts/reception/index.js +198 -0
- package/dist/shortcuts/reception/manifest.js +177 -0
- package/dist/shortcuts/reception/operations/ask.js +71 -0
- package/dist/shortcuts/reception/operations/browsertool.js +58 -0
- package/dist/shortcuts/reception/operations/compose.js +129 -0
- package/dist/shortcuts/reception/operations/customparams.js +74 -0
- package/dist/shortcuts/reception/operations/doc.js +238 -0
- package/dist/shortcuts/reception/operations/docrender.js +36 -0
- package/dist/shortcuts/reception/operations/panels.js +179 -0
- package/dist/shortcuts/reception/operations/registry.js +26 -0
- package/dist/shortcuts/reception/operations/render-assets.js +184 -0
- package/dist/shortcuts/reception/operations/render.js +123 -0
- package/dist/shortcuts/reception/operations/runs.js +38 -0
- package/dist/shortcuts/reception/operations/schedule.js +127 -0
- package/dist/shortcuts/reception/operations/shared.js +196 -0
- package/dist/shortcuts/reception/operations/snapshot.js +68 -0
- package/dist/shortcuts/reception/operations/types.js +1 -0
- package/dist/shortcuts/reception/operations.js +33 -0
- package/dist/shortcuts/reception/upload.js +218 -0
- package/dist/shortcuts/recruit-stage/context.js +145 -33
- package/dist/shortcuts/recruit-stage/continuation.js +2 -1
- package/dist/shortcuts/recruit-stage/operations/jd-create.js +106 -17
- package/dist/shortcuts/recruit-stage/operations/resume-import.js +166 -10
- package/dist/shortcuts/workflow/continuation.js +2 -1
- package/dist/shortcuts/workflow/index.js +2 -0
- package/dist/shortcuts/workflow/operations/form.js +32 -8
- package/dist/shortcuts/workflow/operations/write.js +1 -1
- package/docs/_catalog.md +3 -0
- package/docs/agent-invoice.md +1 -1
- package/docs/archive-oa.md +1 -1
- package/docs/caizhilian.md +1 -1
- package/docs/calendar.md +1 -1
- package/docs/contract.md +1 -1
- package/docs/e10-auth.md +16 -0
- package/docs/ehr.md +2 -2
- package/docs/esb.md +1 -1
- package/docs/im.md +1 -1
- package/docs/invoice.md +8 -12
- package/docs/okr.md +1 -1
- package/docs/reception.md +119 -0
- package/package.json +1 -1
- package/scripts/clean.mjs +43 -2
- package/skill-template/business-info.json +18 -2
- package/skill-template/domains/caizhilian.md +1 -1
- package/skill-template/domains/reception-flow.md +18 -0
- package/skill-template/domains/yepiaotong.md +4 -7
- package/skills/weaver-e10-archive-oa/references/source-manifest.json +14 -14
- package/skills/weaver-e10-blog/references/blog-publish.md +1 -1
- package/skills/weaver-e10-blog/references/source-manifest.json +7 -7
- package/skills/weaver-e10-caizhilian/references/source-manifest.json +21 -21
- package/skills/weaver-e10-caizhilian/references/write-protocol.md +2 -2
- package/skills/weaver-e10-calendar/references/calendar-write.md +1 -1
- package/skills/weaver-e10-cowork/SKILL.md +1 -1
- package/skills/weaver-e10-ebuilder-form/SKILL.md +1 -1
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -1
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -1
- package/skills/weaver-e10-esb/SKILL.md +2 -2
- package/skills/weaver-e10-esb/references/source-manifest.json +22 -22
- package/skills/weaver-e10-esb/references/trigger.md +2 -2
- package/skills/weaver-e10-jinchengda/references/contract-clause-library-crud.md +1 -1
- package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +1 -1
- package/skills/weaver-e10-jinchengda/references/contract-request-write.md +1 -1
- package/skills/weaver-e10-jinchengda/references/source-manifest.json +29 -29
- package/skills/weaver-e10-jucailin/SKILL.md +1 -1
- package/skills/weaver-e10-jucailin/references/attend-write.md +2 -2
- package/skills/weaver-e10-jucailin/references/recruit-create.md +12 -3
- package/skills/weaver-e10-jucailin/references/recruit-write.md +1 -1
- package/skills/weaver-e10-meeting/SKILL.md +8 -6
- package/skills/weaver-e10-meeting/references/meeting-change.md +1 -1
- package/skills/weaver-e10-meeting/references/meeting-create.md +1 -1
- package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +1 -1
- package/skills/weaver-e10-meeting/references/meeting-verification.md +1 -1
- package/skills/weaver-e10-okr/references/okr-align.md +1 -1
- package/skills/weaver-e10-okr/references/okr-keyresult.md +1 -1
- package/skills/weaver-e10-okr/references/okr-write.md +1 -1
- package/skills/weaver-e10-okr/references/source-manifest.json +8 -8
- package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
- package/skills/weaver-e10-qiyecheng/references/loan-management.md +1 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +18 -18
- package/skills/weaver-e10-reception-flow/SKILL.md +173 -0
- package/skills/weaver-e10-reception-flow/assets//350/241/214/347/250/213/345/215/225/346/250/241/346/235/277-/346/263/233/345/276/256.docx +0 -0
- package/skills/weaver-e10-reception-flow/data/fields-snapshot.json +213 -0
- package/skills/weaver-e10-reception-flow/references/browsertool.md +68 -0
- package/skills/weaver-e10-reception-flow/references/env-pitfalls.md +51 -0
- package/skills/weaver-e10-reception-flow/references/fields-snapshot.md +227 -0
- package/skills/weaver-e10-reception-flow/references/guide.md +179 -0
- package/skills/weaver-e10-reception-flow/scripts/locate.sh +14 -0
- package/skills/weaver-e10-reception-flow/scripts/render.py +724 -0
- package/skills/weaver-e10-recruit-stage/SKILL.md +9 -5
- package/skills/weaver-e10-recruit-stage/lib/recruit-xiaoe-file.mjs +1 -1
- package/skills/weaver-e10-recruit-stage/lib/xiaoe-session.mjs +66 -0
- package/skills/weaver-e10-recruit-stage/references/position-templates.md +202 -0
- package/skills/weaver-e10-recruit-stage/references/recruit-jd-create.md +22 -0
- package/skills/weaver-e10-recruit-stage/references/recruit-resume-import.md +14 -1
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +6 -0
- package/skills/weaver-e10-shijingran/SKILL.md +5 -5
- package/skills/weaver-e10-shijingran/references/source-manifest.json +37 -36
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +2 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
- package/skills/weaver-e10-workflow/references/source-manifest.json +32 -32
- package/skills/weaver-e10-yepiaotong/SKILL.md +19 -26
- package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +13 -10
- package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +2 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-workflow.md +194 -155
- package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +28 -32
- package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +5 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +8 -10
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +211 -616
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +149 -117
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +17 -20
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +20 -39
- package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +55 -22
- package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +7 -17
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +88 -92
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +144 -354
- package/skills/weaver-e10-yimiaoban/SKILL.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +7 -7
- package/dist/chunks/chunk-TTPJJJC4.js +0 -465
- package/dist/chunks/registry-BKBZPBAJ.js +0 -23
- package/dist/shortcuts/invoice/markdown.js +0 -172
- package/dist/shortcuts/invoice/operations/issuing-qys.js +0 -424
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +0 -131
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-form-fill-rules.md +0 -179
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
render.py — 一次把接待信息渲染成两份产物(对外 docx + 内部 md)
|
|
5
|
+
|
|
6
|
+
为什么要有它:原流程每次都让模型现场写 python-docx 代码,既慢又容易踩
|
|
7
|
+
「模板 444 只读 -> shutil.copy 复制权限位 -> d.save() 抛 PermissionError」这个坑。
|
|
8
|
+
本脚本把踩过的坑固化成代码:
|
|
9
|
+
|
|
10
|
+
1) 复制模板一律用 copyfile(不复制权限位)+ 落盘后 chmod 644
|
|
11
|
+
2) 表格行数按实际行程自动增删(克隆末行以保留版式)
|
|
12
|
+
3) 段落按文本前缀定位,模板微调不会错位
|
|
13
|
+
4) 对外件自动剔除内部口径(分工/待办/金额/审批)
|
|
14
|
+
|
|
15
|
+
用法:
|
|
16
|
+
python3 scripts/render.py --data recv.json --out-dir /workspace/files
|
|
17
|
+
python3 scripts/render.py --data recv.json --out-dir ./recv --only docx|md
|
|
18
|
+
|
|
19
|
+
输入 JSON(字段均可缺省,缺省即不写):
|
|
20
|
+
{
|
|
21
|
+
"customer": "某某集团有限公司",
|
|
22
|
+
"headcount": 5,
|
|
23
|
+
"visitDate": "2026-10-22",
|
|
24
|
+
"visitType": "参观上海总部",
|
|
25
|
+
"contact": "张三(泛微网络 XX中心 XX组)",
|
|
26
|
+
"companions": [{"name": "张三", "mobile": "13800138000"}],
|
|
27
|
+
"demand": "商务洽谈 + 技术交流",
|
|
28
|
+
"pickup": "", "meal": "食堂用餐", "mealHeadcount": 6,
|
|
29
|
+
"hotel": "无需住宿", "gift": "不需要伴手礼", "agendaStart": "09:30",
|
|
30
|
+
"venue": "泛微网络 上海总部", "meetingRoom": "总部129会议室",
|
|
31
|
+
"schedule": [["上午","抵达泛微上海总部,签到与欢迎","泛微上海总部"]],
|
|
32
|
+
"todo": ["客户性质", "项目金额(万元)"]
|
|
33
|
+
}
|
|
34
|
+
"""
|
|
35
|
+
import argparse, json, os, re, shutil, sys, datetime, hashlib
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
import docx
|
|
39
|
+
from docx.shared import Pt
|
|
40
|
+
except ImportError:
|
|
41
|
+
docx = None
|
|
42
|
+
|
|
43
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
44
|
+
TPL = os.path.join(HERE, '..', 'assets', '行程单模板-泛微.docx')
|
|
45
|
+
WEEK = '一二三四五六日'
|
|
46
|
+
|
|
47
|
+
# v2.8:对外件内部口径清洗(原版只剔段落级内部字段,表格与地点里的
|
|
48
|
+
# 「(具体单位待确认)」「待确认」会原样漏到客户版,属产物质量缺陷)
|
|
49
|
+
INTERNAL_MARKS = ('待确认', '待补', '待定', '待办', '内部', '未定', 'TBD', 'tbd')
|
|
50
|
+
EXTERNAL_FALLBACK = '以现场安排为准'
|
|
51
|
+
_PAREN = re.compile(r'[((][^(())]*[))]')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def has_mark(v):
|
|
55
|
+
return any(k in str(v or '') for k in INTERNAL_MARKS)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def ext(v, fallback=EXTERNAL_FALLBACK):
|
|
59
|
+
"""对外件取值:先剥掉含内部口径的括注,整串仍带内部口径则用兜底语。"""
|
|
60
|
+
s = str(v or '').strip()
|
|
61
|
+
if not s:
|
|
62
|
+
return ''
|
|
63
|
+
for _ in range(3):
|
|
64
|
+
s2 = _PAREN.sub(lambda m: '' if has_mark(m.group(0)) else m.group(0), s).strip()
|
|
65
|
+
if s2 == s:
|
|
66
|
+
break
|
|
67
|
+
s = s2
|
|
68
|
+
if not s or has_mark(s):
|
|
69
|
+
return fallback
|
|
70
|
+
return s
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
_RE_NUM = re.compile(r'\d+(?:\.\d+)?')
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def num(v, default=0):
|
|
77
|
+
"""人数类字段取数:兼容 8 / "8" / "6~10人" 等写法。
|
|
78
|
+
区间取上界:用餐与场地按人数上限预留更安全;无法解析时返回 default。"""
|
|
79
|
+
if v is None or isinstance(v, bool) or v == '':
|
|
80
|
+
return default
|
|
81
|
+
if isinstance(v, (int, float)):
|
|
82
|
+
return int(v)
|
|
83
|
+
nums = _RE_NUM.findall(str(v))
|
|
84
|
+
if not nums:
|
|
85
|
+
return default
|
|
86
|
+
try:
|
|
87
|
+
return max(int(float(n)) for n in nums)
|
|
88
|
+
except (TypeError, ValueError):
|
|
89
|
+
return default
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def ppl(v):
|
|
93
|
+
"""人数展示:剥掉「左右/上下/余人」等模糊尾巴与「人/位/名」量词,避免「约8人左右 人」。"""
|
|
94
|
+
if v is None or v == '':
|
|
95
|
+
return ''
|
|
96
|
+
s = re.sub(r'(左右|上下|多人|余人|以上|以下|人|位|名)\s*$', '', str(v).strip()).strip()
|
|
97
|
+
return re.sub(r'[\s人位名]+$', '', s).strip()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def scan_docx(path):
|
|
101
|
+
"""自检:对外件里是否还残留内部口径(有残留就是质量缺陷,要报出来)。"""
|
|
102
|
+
try:
|
|
103
|
+
dd = docx.Document(path)
|
|
104
|
+
except Exception:
|
|
105
|
+
return {'externalLeaks': 0, 'leakSamples': []}
|
|
106
|
+
lines = []
|
|
107
|
+
for p in dd.paragraphs:
|
|
108
|
+
if p.text.strip():
|
|
109
|
+
lines.append(p.text)
|
|
110
|
+
para = len(lines)
|
|
111
|
+
for t in dd.tables:
|
|
112
|
+
for row in t.rows:
|
|
113
|
+
for c in row.cells:
|
|
114
|
+
if c.text.strip():
|
|
115
|
+
lines.append(c.text)
|
|
116
|
+
leaks = [ln for ln in lines if has_mark(ln)]
|
|
117
|
+
return {'externalLeaks': len(leaks), 'leakSamples': leaks[:3], 'paragraphs': para}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def sha8(path):
|
|
121
|
+
"""产物指纹前 8 位:给上层 verify 用,让模型不必 cat 全文就能确认文件是刚生成的那份。"""
|
|
122
|
+
try:
|
|
123
|
+
with open(path, 'rb') as f:
|
|
124
|
+
return hashlib.sha256(f.read()).hexdigest()[:8]
|
|
125
|
+
except Exception:
|
|
126
|
+
return ''
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def prepare_target(path):
|
|
130
|
+
"""目标已存在且为只读时先放开权限,否则 copyfile/open('w') 会直接 PermissionError。"""
|
|
131
|
+
if os.path.exists(path):
|
|
132
|
+
try:
|
|
133
|
+
os.chmod(path, 0o644)
|
|
134
|
+
except OSError:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def safe_write(path):
|
|
139
|
+
"""落盘后统一放开权限,避免只读模板把后续步骤卡死。"""
|
|
140
|
+
try:
|
|
141
|
+
os.chmod(path, 0o644)
|
|
142
|
+
except OSError:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def cn_date(s):
|
|
147
|
+
try:
|
|
148
|
+
d = datetime.date.fromisoformat(s)
|
|
149
|
+
return f'{d.year}年{d.month}月{d.day}日(星期{WEEK[d.weekday()]})'
|
|
150
|
+
except Exception:
|
|
151
|
+
return s or ''
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def set_para(p, text):
|
|
155
|
+
if p.runs:
|
|
156
|
+
p.runs[0].text = text
|
|
157
|
+
for r in p.runs[1:]:
|
|
158
|
+
r.text = ''
|
|
159
|
+
else:
|
|
160
|
+
p.add_run(text)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def fill_para(doc, prefix, text):
|
|
164
|
+
"""按前缀定位段落并改写;前缀不中时退化为包含匹配(模板里有「1月2日参观行程安排:」这类前缀不固定的行);仍不中则追加。"""
|
|
165
|
+
for p in doc.paragraphs:
|
|
166
|
+
if p.text.strip().startswith(prefix):
|
|
167
|
+
set_para(p, text)
|
|
168
|
+
return True
|
|
169
|
+
for p in doc.paragraphs:
|
|
170
|
+
if prefix in p.text:
|
|
171
|
+
set_para(p, text)
|
|
172
|
+
return True
|
|
173
|
+
doc.add_paragraph(text)
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def resize_table(table, need_rows):
|
|
178
|
+
"""need_rows = 数据行数(不含表头)。克隆末行以保留边框与字体。"""
|
|
179
|
+
import copy
|
|
180
|
+
have = len(table.rows) - 1
|
|
181
|
+
if need_rows > have:
|
|
182
|
+
for _ in range(need_rows - have):
|
|
183
|
+
tr = copy.deepcopy(table.rows[-1]._tr)
|
|
184
|
+
table.rows[-1]._tr.addnext(tr)
|
|
185
|
+
elif need_rows < have:
|
|
186
|
+
for _ in range(have - need_rows):
|
|
187
|
+
tr = table.rows[-1]._tr
|
|
188
|
+
tr.getparent().remove(tr)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
SCHED_KEYS = {
|
|
192
|
+
'date': ('date', '日期', 'rq'),
|
|
193
|
+
'time': ('time', '时间', '时段', 'sj'),
|
|
194
|
+
'item': ('item', '接待事项', '事项', 'jdsx'),
|
|
195
|
+
'place': ('place', '地点', 'venue', 'dd'),
|
|
196
|
+
'person': ('person', '接待人员', '人员', 'jdry'),
|
|
197
|
+
'activity': ('activity', '活动安排', '安排', 'hdap'),
|
|
198
|
+
'remark': ('remark', '备注', 'bz'),
|
|
199
|
+
}
|
|
200
|
+
SCHED_ORDER = ['date', 'time', 'item', 'place', 'person', 'activity', 'remark']
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _pick(row, key):
|
|
204
|
+
for alias in SCHED_KEYS[key]:
|
|
205
|
+
if alias in row and row.get(alias) not in (None, ''):
|
|
206
|
+
return row.get(alias)
|
|
207
|
+
return ''
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def norm_schedule(d):
|
|
211
|
+
"""把 schedule 归一成 7 字段行(date/time/item/place/person/activity/remark)。
|
|
212
|
+
|
|
213
|
+
v4.7:既兼容旧的三元组 [时段, 安排, 地点],也接受 7 字段对象(中英文键均可),
|
|
214
|
+
产出的结构同时用于对外行程单、内部方案与流程【日程明细】子表。
|
|
215
|
+
"""
|
|
216
|
+
rows = []
|
|
217
|
+
for raw in (d.get('schedule') or []):
|
|
218
|
+
if isinstance(raw, dict):
|
|
219
|
+
rows.append({k: _pick(raw, k) for k in SCHED_ORDER})
|
|
220
|
+
elif isinstance(raw, (list, tuple)):
|
|
221
|
+
vals = list(raw) + [''] * 7
|
|
222
|
+
rows.append({
|
|
223
|
+
'date': vals[0] if len(raw) >= 7 else '',
|
|
224
|
+
'time': vals[0] if len(raw) < 7 else vals[1],
|
|
225
|
+
'item': vals[1] if len(raw) < 7 else vals[2],
|
|
226
|
+
'place': vals[2] if len(raw) < 7 else vals[3],
|
|
227
|
+
'person': '' if len(raw) < 7 else vals[4],
|
|
228
|
+
'activity': '' if len(raw) < 7 else vals[5],
|
|
229
|
+
'remark': '' if len(raw) < 7 else vals[6],
|
|
230
|
+
})
|
|
231
|
+
return rows
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ==================== v4.9:标准议程 + 陪同人员联系方式 ====================
|
|
235
|
+
# 对外行程单只呈现「公司参观 → 方案介绍 → 讨论交流」这类议程环节。
|
|
236
|
+
# 用餐、接送、住宿、返程等后勤动作一律不进时间表(它们写在行程单抬头
|
|
237
|
+
# 与内部方案的「食宿与用车」章节),这是接待口径不是报错。
|
|
238
|
+
AGENDA_EXCLUDE = (
|
|
239
|
+
'用餐', '午餐', '晚餐', '早餐', '午宴', '晚宴', '宴请', '食堂', '餐厅', '饭',
|
|
240
|
+
'接机', '接站', '送机', '送站', '接送', '用车', '车辆', '商务车', '大巴',
|
|
241
|
+
'住宿', '入住', '离店', '酒店', '返程', '接返', '出发去机场',
|
|
242
|
+
)
|
|
243
|
+
# (展示名, 起始偏移分钟, 时长分钟, 活动说明缺省值)
|
|
244
|
+
STANDARD_AGENDA = (
|
|
245
|
+
('公司参观', 0, 45, '参观泛微上海总部展厅与办公区,了解公司发展历程与产品能力'),
|
|
246
|
+
('方案介绍', 45, 75, ''),
|
|
247
|
+
('讨论交流', 120, 30, '双方围绕关注点交流答疑,明确后续推进节奏'),
|
|
248
|
+
)
|
|
249
|
+
DEFAULT_START = '09:30'
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _hm(minutes):
|
|
253
|
+
return f'{minutes // 60:02d}:{minutes % 60:02d}'
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _parse_hm(s):
|
|
257
|
+
m = re.match(r'^\s*(\d{1,2})[::](\d{2})\s*$', str(s or ''))
|
|
258
|
+
if not m:
|
|
259
|
+
return None
|
|
260
|
+
h, mi = int(m.group(1)), int(m.group(2))
|
|
261
|
+
return h * 60 + mi if (0 <= h <= 23 and 0 <= mi <= 59) else None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def is_logistics(text):
|
|
265
|
+
"""判断一行是否属于用餐/接送/住宿类后勤动作(不进议程时间表)。"""
|
|
266
|
+
s = str(text or '')
|
|
267
|
+
return any(k in s for k in AGENDA_EXCLUDE)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _agenda_row(start, end, item, activity='', place='', person='', remark=''):
|
|
271
|
+
return {
|
|
272
|
+
'start': start, 'end': end,
|
|
273
|
+
'time': f'{start}-{end}' if (start and end) else (start or end),
|
|
274
|
+
'item': item, 'activity': activity, 'place': place,
|
|
275
|
+
'person': person, 'remark': remark,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def norm_agenda(d):
|
|
280
|
+
"""行程归一为「议程行」,自动剔除用餐/接送/住宿等后勤动作。
|
|
281
|
+
|
|
282
|
+
取值优先级:
|
|
283
|
+
1) data.agenda 显式议程(逐行 time/start+end/item/activity/place/person)
|
|
284
|
+
2) data.schedule 中剔除后勤动作后的行(保留客户真实时间)
|
|
285
|
+
3) 标准议程:公司参观 → 方案介绍 → 讨论交流(起点 data.agendaStart,缺省 09:30)
|
|
286
|
+
"""
|
|
287
|
+
venue = str(d.get('venue') or '泛微上海总部').strip()
|
|
288
|
+
room = str(d.get('meetingRoom') or '').strip() or venue
|
|
289
|
+
|
|
290
|
+
explicit = [r for r in (d.get('agenda') or []) if isinstance(r, dict)]
|
|
291
|
+
if explicit:
|
|
292
|
+
rows = []
|
|
293
|
+
for r in explicit:
|
|
294
|
+
start = str(r.get('start') or r.get('time') or '').strip()
|
|
295
|
+
end = str(r.get('end') or '').strip()
|
|
296
|
+
rows.append(_agenda_row(
|
|
297
|
+
start, end,
|
|
298
|
+
str(r.get('item') or r.get('name') or '').strip(),
|
|
299
|
+
str(r.get('activity') or '').strip(),
|
|
300
|
+
str(r.get('place') or r.get('venue') or '').strip(),
|
|
301
|
+
str(r.get('person') or '').strip(),
|
|
302
|
+
str(r.get('remark') or '').strip(),
|
|
303
|
+
))
|
|
304
|
+
return rows
|
|
305
|
+
|
|
306
|
+
base = _parse_hm(d.get('agendaStart')) or _parse_hm(DEFAULT_START)
|
|
307
|
+
demand = str(d.get('demand') or '').strip()
|
|
308
|
+
rows = []
|
|
309
|
+
for name, off, dur, act in STANDARD_AGENDA:
|
|
310
|
+
start, end = _hm(base + off), _hm(base + off + dur)
|
|
311
|
+
if name == '方案介绍':
|
|
312
|
+
act = act or (demand or '泛微数字化运营平台整体方案介绍')
|
|
313
|
+
row = _agenda_row(start, end, name, act, venue if name == '公司参观' else room)
|
|
314
|
+
row['dur'] = dur
|
|
315
|
+
rows.append(row)
|
|
316
|
+
|
|
317
|
+
# 真实行程里已有对应环节时,用客户的实际时间/地点/人员覆盖标准环节,
|
|
318
|
+
# 保证输出恒为「公司参观—方案介绍—讨论交流」三段,但时间贴合实际。
|
|
319
|
+
alias = (
|
|
320
|
+
('公司参观', ('参观', '展厅', '走访')),
|
|
321
|
+
('方案介绍', ('介绍', '演示', '讲解', '汇报', 'PPT', 'ppt', '方案')),
|
|
322
|
+
('讨论交流', ('交流', '讨论', '答疑', '座谈', '洽谈', '沟通')),
|
|
323
|
+
)
|
|
324
|
+
for r in norm_schedule(d):
|
|
325
|
+
blob = ' '.join([str(r.get('item') or ''), str(r.get('activity') or '')])
|
|
326
|
+
if is_logistics(blob):
|
|
327
|
+
continue
|
|
328
|
+
t = str(r.get('time') or '').strip()
|
|
329
|
+
parts = [p for p in re.split(r'\s*[-—~至到]+\s*', t) if p.strip()]
|
|
330
|
+
st = parts[0].strip() if parts else ''
|
|
331
|
+
en = parts[1].strip() if len(parts) >= 2 else ''
|
|
332
|
+
for idx, (nm, keys) in enumerate(alias):
|
|
333
|
+
if any(k in blob for k in keys):
|
|
334
|
+
row = rows[idx]
|
|
335
|
+
if st:
|
|
336
|
+
if not en:
|
|
337
|
+
_s = _parse_hm(st)
|
|
338
|
+
en = _hm(_s + int(row.get('dur') or 45)) if _s is not None else ''
|
|
339
|
+
row['start'], row['end'] = st, en
|
|
340
|
+
row['time'] = f'{st}-{en}' if en else st
|
|
341
|
+
if r.get('place'):
|
|
342
|
+
row['place'] = r.get('place')
|
|
343
|
+
if r.get('person'):
|
|
344
|
+
row['person'] = r.get('person')
|
|
345
|
+
if r.get('activity') and not row['activity']:
|
|
346
|
+
row['activity'] = r.get('activity')
|
|
347
|
+
break
|
|
348
|
+
|
|
349
|
+
prev_end = None
|
|
350
|
+
for row in rows:
|
|
351
|
+
st = _parse_hm(row.get('start'))
|
|
352
|
+
if prev_end is not None and st is not None and st < prev_end:
|
|
353
|
+
st = prev_end
|
|
354
|
+
row['start'] = _hm(st)
|
|
355
|
+
row['end'] = _hm(st + int(row.get('dur') or 45))
|
|
356
|
+
row['time'] = f"{row['start']}-{row['end']}"
|
|
357
|
+
en = _parse_hm(row.get('end'))
|
|
358
|
+
if en is None and st is not None:
|
|
359
|
+
en = st + int(row.get('dur') or 45)
|
|
360
|
+
row['end'] = _hm(en)
|
|
361
|
+
row['time'] = f"{row['start']}-{row['end']}"
|
|
362
|
+
if en is not None:
|
|
363
|
+
prev_end = en if prev_end is None else max(prev_end, en)
|
|
364
|
+
for row in rows:
|
|
365
|
+
row.pop('dur', None)
|
|
366
|
+
return rows
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
MOBILE_RE = re.compile(r'^1[3-9]\d{9}$')
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _mobile_of(x):
|
|
373
|
+
"""v4.10:只取手机号(11 位)。座机 telephone、脱敏串一律不取、不回退。"""
|
|
374
|
+
if not isinstance(x, dict):
|
|
375
|
+
return ''
|
|
376
|
+
for k in ('mobile', 'mobile_plain', 'cellphone', 'phone_mobile', '手机号'):
|
|
377
|
+
v = str(x.get(k) or '').strip()
|
|
378
|
+
if not v or 'desensiti' in v.lower():
|
|
379
|
+
continue
|
|
380
|
+
v = re.sub(r'[\s-]', '', v)
|
|
381
|
+
if MOBILE_RE.match(v):
|
|
382
|
+
return v
|
|
383
|
+
return ''
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def norm_companions(d):
|
|
387
|
+
"""陪同人员归一:[{name, phone}]。
|
|
388
|
+
|
|
389
|
+
v4.10 口径:phone 只认手机号(11 位)。座机(telephone)不再作为兜底,
|
|
390
|
+
取不到手机号就只写姓名,对外件不出现内部口径。
|
|
391
|
+
"""
|
|
392
|
+
out = []
|
|
393
|
+
for x in (d.get('companions') or []):
|
|
394
|
+
if isinstance(x, dict):
|
|
395
|
+
nm = str(x.get('name') or x.get('text') or '').strip()
|
|
396
|
+
tel = _mobile_of(x)
|
|
397
|
+
else:
|
|
398
|
+
s = str(x or '').strip()
|
|
399
|
+
nm, tel = s, ''
|
|
400
|
+
m = re.search(r'[((]([^(())]*)[))]', s)
|
|
401
|
+
if m:
|
|
402
|
+
inner = m.group(1).strip()
|
|
403
|
+
# 只有「11 位手机号」的括注才当电话;"张三(SH)" 这类机构后缀保留原名
|
|
404
|
+
if MOBILE_RE.match(re.sub(r'[\s-]', '', inner)):
|
|
405
|
+
tel = re.sub(r'[\s-]', '', inner)
|
|
406
|
+
nm = (s[:m.start()] + s[m.end():]).strip()
|
|
407
|
+
if nm:
|
|
408
|
+
out.append({'name': nm, 'phone': tel})
|
|
409
|
+
return out
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def contact_line(d):
|
|
413
|
+
"""接待人员展示串:姓名(电话);无电话只写姓名,不写「待补」。"""
|
|
414
|
+
parts = []
|
|
415
|
+
for c in norm_companions(d):
|
|
416
|
+
parts.append(f"{c['name']}({c['phone']})" if c['phone'] else c['name'])
|
|
417
|
+
if parts:
|
|
418
|
+
return '、'.join(parts)
|
|
419
|
+
return str(d.get('contact') or '').strip()
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def sched_cells(row):
|
|
423
|
+
"""议程行 -> 对外行程单/内部方案的三列表格(时间 | 安排 | 地点)。"""
|
|
424
|
+
t = row.get('time') or ''
|
|
425
|
+
item = row.get('item') or ''
|
|
426
|
+
act = row.get('activity') or ''
|
|
427
|
+
main = item if not act else (f'{item}:{act}' if item else act)
|
|
428
|
+
return [t, main, row.get('place') or '']
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def build_docx(d, out_path):
|
|
432
|
+
os.makedirs(os.path.dirname(out_path) or '.', exist_ok=True)
|
|
433
|
+
prepare_target(out_path)
|
|
434
|
+
# 关键:copyfile 不复制 444 权限位(用 shutil.copy 会把只读位一起带过去)
|
|
435
|
+
shutil.copyfile(TPL, out_path)
|
|
436
|
+
safe_write(out_path)
|
|
437
|
+
|
|
438
|
+
doc = docx.Document(out_path)
|
|
439
|
+
customer = d.get('customer', '')
|
|
440
|
+
visit_date = d.get('visitDate', '')
|
|
441
|
+
venue = d.get('venue') or '泛微网络 上海总部'
|
|
442
|
+
|
|
443
|
+
cleaned = []
|
|
444
|
+
|
|
445
|
+
def ext_track(field, raw):
|
|
446
|
+
val = ext(raw)
|
|
447
|
+
if str(raw or '').strip() != val:
|
|
448
|
+
cleaned.append(f'{field}: {raw} -> {val or "(不写)"}')
|
|
449
|
+
return val
|
|
450
|
+
|
|
451
|
+
venue = ext_track('参观地点', venue)
|
|
452
|
+
fill_para(doc, '参观时间', f'参观时间:{cn_date(visit_date)}')
|
|
453
|
+
fill_para(doc, '参观地点', f'参观地点:{venue}')
|
|
454
|
+
fill_para(doc, '莅临领导', f'莅临领导:贵司各位来宾(共 {ppl(d.get("headcount"))} 人)' if d.get('headcount') else '莅临领导:贵司各位来宾')
|
|
455
|
+
contact_ext = ext_track('接待人员', contact_line(d) or '泛微接待团队')
|
|
456
|
+
fill_para(doc, '接待人员', f'接待人员:{contact_ext}')
|
|
457
|
+
if visit_date:
|
|
458
|
+
fill_para(doc, '参观行程安排', f'{cn_date(visit_date).split("(")[0]}参观行程安排:')
|
|
459
|
+
|
|
460
|
+
schedule = [sched_cells(r) for r in norm_agenda(d)]
|
|
461
|
+
if doc.tables and schedule:
|
|
462
|
+
t = doc.tables[0]
|
|
463
|
+
resize_table(t, len(schedule))
|
|
464
|
+
for i, row in enumerate(schedule, start=1):
|
|
465
|
+
cells = t.rows[i].cells
|
|
466
|
+
for ci, (cell, val) in enumerate(zip(cells, list(row) + ['', '', ''])):
|
|
467
|
+
ps = cell.paragraphs
|
|
468
|
+
txt = '' if val is None else str(val)
|
|
469
|
+
# 地点列(第 3 列)与任何含内部口径的单元格统一走对外口径
|
|
470
|
+
if ci == 2:
|
|
471
|
+
txt = ext_track('行程地点', txt)
|
|
472
|
+
elif has_mark(txt):
|
|
473
|
+
txt = ext_track('行程安排', txt)
|
|
474
|
+
set_para(ps[0], txt)
|
|
475
|
+
for extra in ps[1:]:
|
|
476
|
+
set_para(extra, '')
|
|
477
|
+
|
|
478
|
+
doc.add_paragraph('具体时间安排以现场为准;如有调整,我们将第一时间与贵司沟通。')
|
|
479
|
+
doc.save(out_path)
|
|
480
|
+
safe_write(out_path)
|
|
481
|
+
qa = scan_docx(out_path)
|
|
482
|
+
qa['cleaned'] = cleaned
|
|
483
|
+
return out_path, qa
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def norm_cases(d):
|
|
487
|
+
"""案例参考:兼容字符串 / 对象 / {items|list} 三种写法,字段名给多个别名。
|
|
488
|
+
只取能落到表格的内容;空则整段不出(不写"暂无案例")。"""
|
|
489
|
+
raw = d.get('cases') or []
|
|
490
|
+
if isinstance(raw, dict):
|
|
491
|
+
raw = raw.get('items') or raw.get('list') or raw.get('cases') or []
|
|
492
|
+
if not isinstance(raw, list):
|
|
493
|
+
return []
|
|
494
|
+
out = []
|
|
495
|
+
for c in raw:
|
|
496
|
+
if isinstance(c, str):
|
|
497
|
+
s = c.strip()
|
|
498
|
+
if s:
|
|
499
|
+
out.append({'title': s, 'relevance': '', 'date': ''})
|
|
500
|
+
elif isinstance(c, dict):
|
|
501
|
+
title = str(c.get('title') or c.get('material') or c.get('name') or '').strip()
|
|
502
|
+
rel = str(c.get('relevance') or c.get('point') or c.get('why') or '').strip()
|
|
503
|
+
date = str(c.get('date') or c.get('time') or c.get('when') or '').strip()
|
|
504
|
+
if title or rel:
|
|
505
|
+
out.append({'title': title, 'relevance': rel, 'date': date})
|
|
506
|
+
return out
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def norm_experts(d):
|
|
510
|
+
"""专家参考:兼容字符串 / 对象 / {items|list},字段名给多个别名。
|
|
511
|
+
只取能落到表格的内容;空则整段不出。"""
|
|
512
|
+
raw = d.get('experts') or []
|
|
513
|
+
if isinstance(raw, dict):
|
|
514
|
+
raw = raw.get('items') or raw.get('list') or raw.get('experts') or []
|
|
515
|
+
if not isinstance(raw, list):
|
|
516
|
+
return []
|
|
517
|
+
out = []
|
|
518
|
+
for e in raw:
|
|
519
|
+
if isinstance(e, str):
|
|
520
|
+
s = e.strip()
|
|
521
|
+
if s:
|
|
522
|
+
out.append({'name': s, 'dept': '', 'relevance': ''})
|
|
523
|
+
elif isinstance(e, dict):
|
|
524
|
+
name = str(e.get('name') or e.get('expert') or e.get('person') or '').strip()
|
|
525
|
+
dept = str(e.get('dept') or e.get('department') or e.get('role') or e.get('title') or '').strip()
|
|
526
|
+
rel = str(e.get('relevance') or e.get('point') or e.get('match') or e.get('why') or '').strip()
|
|
527
|
+
if name or dept or rel:
|
|
528
|
+
out.append({'name': name, 'dept': dept, 'relevance': rel})
|
|
529
|
+
return out
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _cell(s):
|
|
533
|
+
"""表格单元格:竖线会截断表格,换行会断行,统一压平。"""
|
|
534
|
+
return str(s or '').replace('|', '/').replace('\n', ' ').strip() or '—'
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
_CJK_NUM = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def build_md(d, out_path):
|
|
541
|
+
os.makedirs(os.path.dirname(out_path) or '.', exist_ok=True)
|
|
542
|
+
prepare_target(out_path)
|
|
543
|
+
L = []
|
|
544
|
+
A = L.append
|
|
545
|
+
_n = [0]
|
|
546
|
+
|
|
547
|
+
def sec(t):
|
|
548
|
+
"""章节号自增:中间插章节时无需手改后面所有编号。"""
|
|
549
|
+
_n[0] += 1
|
|
550
|
+
return f'## {_CJK_NUM[_n[0] - 1]}、{t}\n'
|
|
551
|
+
|
|
552
|
+
A(f'# 客户接待交流参考文档(内部)\n')
|
|
553
|
+
A(f'> 编制日期:{datetime.date.today().isoformat()} | 流程:泛微客户参观接待流程(草稿,待发起) | 内部文件,未经许可不得外发\n')
|
|
554
|
+
|
|
555
|
+
A(sec('客户与来访信息'))
|
|
556
|
+
A('| 项目 | 内容 |')
|
|
557
|
+
A('| --- | --- |')
|
|
558
|
+
A(f'| 来访客户 | {d.get("customer", "待补")} |')
|
|
559
|
+
A(f'| 来访人数 | {ppl(d.get("headcount", "待补"))} 人 |' if d.get('headcount') else '| 来访人数 | 待补 |')
|
|
560
|
+
A(f'| 客户带队人 / 来访名单 | {d.get("visitors") or "待补"} |')
|
|
561
|
+
A(f'| 客户性质 | {d.get("nature") or "待补(流程必填项,请在界面选择)"} |')
|
|
562
|
+
A(f'| 参观日期 | {cn_date(d.get("visitDate")) or "待补"} |')
|
|
563
|
+
A(f'| 参观类型 | {d.get("visitType") or "待补"} |')
|
|
564
|
+
A(f'| 客户需求 | {d.get("demand") or "待补"} |')
|
|
565
|
+
A(f'| 主联络人 | {d.get("contact") or "待补"} |')
|
|
566
|
+
A(f'| 项目金额 | {d.get("amount") or "待补(不在本方案中估算)"} |')
|
|
567
|
+
A('')
|
|
568
|
+
|
|
569
|
+
A(sec('接待目标'))
|
|
570
|
+
A('1. 向来访客户完整展示泛微上海总部办公环境、产品与数智化能力,建立直观认知。')
|
|
571
|
+
A('2. 围绕商务合作模式与技术实现路径开展双向沟通,明确客户关注点与后续推进节奏。')
|
|
572
|
+
A('3. 通过规范、有温度的接待流程,强化客户对泛微交付能力与服务体系的信任。\n')
|
|
573
|
+
|
|
574
|
+
# 案例参考:仅当有搜索结果时成章(软依赖,取不到就整段省略)
|
|
575
|
+
cases = norm_cases(d)
|
|
576
|
+
if cases:
|
|
577
|
+
A(sec('相关案例参考'))
|
|
578
|
+
note = str(d.get('casesNote') or '').strip()
|
|
579
|
+
if note:
|
|
580
|
+
A(f'> {note}\n')
|
|
581
|
+
A('| 材料 | 与本次拜访的关联 | 时间 |')
|
|
582
|
+
A('| --- | --- | --- |')
|
|
583
|
+
for c in cases:
|
|
584
|
+
A(f'| {_cell(c["title"])} | {_cell(c["relevance"])} | {_cell(c["date"])} |')
|
|
585
|
+
A('')
|
|
586
|
+
|
|
587
|
+
# 专家参考:同样软依赖,没有就整段省略
|
|
588
|
+
experts = norm_experts(d)
|
|
589
|
+
if experts:
|
|
590
|
+
A(sec('相关专家参考'))
|
|
591
|
+
enote = str(d.get('expertsNote') or '').strip()
|
|
592
|
+
if enote:
|
|
593
|
+
A(f'> {enote}\n')
|
|
594
|
+
A('| 专家 | 部门 / 岗位 | 与本次需求的匹配点 |')
|
|
595
|
+
A('| --- | --- | --- |')
|
|
596
|
+
for e in experts:
|
|
597
|
+
A(f'| {_cell(e["name"])} | {_cell(e["dept"])} | {_cell(e["relevance"])} |')
|
|
598
|
+
A('')
|
|
599
|
+
|
|
600
|
+
A(sec(f'日程安排({d.get("visitDate", "")})'))
|
|
601
|
+
A('| 时段 | 安排 | 地点 |')
|
|
602
|
+
A('| --- | --- | --- |')
|
|
603
|
+
for row in [sched_cells(r) for r in norm_agenda(d)]:
|
|
604
|
+
r = list(row) + ['', '', '']
|
|
605
|
+
A(f'| {r[0]} | {r[1]} | {r[2]} |')
|
|
606
|
+
A('\n> 各环节具体起止时间待与客户最终确认;接送与用车安排未落实时需在行程确认后同步客户。\n')
|
|
607
|
+
|
|
608
|
+
A(sec('人员分工'))
|
|
609
|
+
A('| 角色 | 人员 | 职责 |')
|
|
610
|
+
A('| --- | --- | --- |')
|
|
611
|
+
A(f'| 主联络人 / 全程陪同 | {contact_line(d) or "待定"} | 接待总协调,全程陪同参观,客户沟通对接 |')
|
|
612
|
+
A('| 商务线条 | 待定 | 商务合作模式沟通、报价与合同口径 |')
|
|
613
|
+
A('| 技术线条 | 待定 | 技术方案讲解、客户技术疑问应答 |')
|
|
614
|
+
A('| 会务支持 | 待定 | 场地、餐饮、物料、字幕与签到准备 |\n')
|
|
615
|
+
A('> 是否需公司高层参与,尚未表态的,建议在客户层级明确后再定。\n')
|
|
616
|
+
|
|
617
|
+
A(sec('食宿与用车'))
|
|
618
|
+
hc = num(d.get('headcount'))
|
|
619
|
+
comp = len(d.get('companions') or [])
|
|
620
|
+
meal_hc = d.get('mealHeadcount') or ((hc + comp) if (hc or comp) else '')
|
|
621
|
+
cust_hc = ppl(d.get('headcount'))
|
|
622
|
+
A(f'- **用餐**:形式为{d.get("meal") or "待定"};用餐人数 {ppl(meal_hc) or "待定"}(客户 {cust_hc + " 人" if cust_hc else "人数待补"} + 陪同 {comp} 人,可调整);用餐档次需在流程界面按实际情况选择。')
|
|
623
|
+
A(f'- **住宿**:{d.get("hotel") or "待定"}。')
|
|
624
|
+
A(f'- **接送 / 用车**:{d.get("pickup") or "暂未确定,需补充接机时间、地点、车型与数量、送机安排"}。')
|
|
625
|
+
A(f'- **伴手礼**:{d.get("gift") or "待定"}。\n')
|
|
626
|
+
|
|
627
|
+
A(sec('会议与会务'))
|
|
628
|
+
A(f'- 场地:{d.get("meetingRoom") or "总部129/130/131会议室(按人数择一)"}(按实际人数预留,建议提前 1 天确认可用)。')
|
|
629
|
+
A(f'- 欢迎字幕:欢迎{d.get("customer", "")}莅临指导。')
|
|
630
|
+
A('- 会务物料:公司介绍资料、方案材料、演示环境、席位牌、签到表(待准备)。')
|
|
631
|
+
A('- 演示准备:提前调试演示环境与投屏,准备商务洽谈与技术交流对应的介绍材料。\n')
|
|
632
|
+
|
|
633
|
+
A(sec('应急预案'))
|
|
634
|
+
A('- 客户行程变更:由主联络人第一时间同步会务支持调整场地、餐饮与人员安排。')
|
|
635
|
+
A('- 关键人员临时缺席:由同线条备份人员顶替,确保商务与技术口径均有对应人负责。')
|
|
636
|
+
A('- 设备 / 演示故障:准备离线材料与备份设备,必要时改用文档讲解。')
|
|
637
|
+
A('- 用餐与场地冲突:提前确认餐厅与会议室备用方案。\n')
|
|
638
|
+
|
|
639
|
+
A(sec('待确认事项'))
|
|
640
|
+
todo = d.get('todo') or []
|
|
641
|
+
if todo:
|
|
642
|
+
for i, t in enumerate(todo, start=1):
|
|
643
|
+
A(f'{i}. {t}')
|
|
644
|
+
else:
|
|
645
|
+
A('无')
|
|
646
|
+
A('')
|
|
647
|
+
|
|
648
|
+
with open(out_path, 'w', encoding='utf-8') as f:
|
|
649
|
+
f.write('\n'.join(L))
|
|
650
|
+
safe_write(out_path)
|
|
651
|
+
return out_path
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def main():
|
|
655
|
+
ap = argparse.ArgumentParser()
|
|
656
|
+
ap.add_argument('--data', required=True, help='接待信息 JSON 文件')
|
|
657
|
+
ap.add_argument('--out-dir', default='/workspace/files')
|
|
658
|
+
ap.add_argument('--only', choices=['docx', 'md'], help='只生成其中一份')
|
|
659
|
+
ap.add_argument('--docx-name', default='客户接待行程单.docx')
|
|
660
|
+
ap.add_argument('--md-name', default='接待交流参考文档.md')
|
|
661
|
+
a = ap.parse_args()
|
|
662
|
+
|
|
663
|
+
with open(a.data, encoding='utf-8') as f:
|
|
664
|
+
d = json.load(f)
|
|
665
|
+
|
|
666
|
+
res = {'ok': True, 'files': [], 'qa': {}}
|
|
667
|
+
# v4.4:verify 统计随渲染结果一起回传,供 e10.mjs 组装「可核验证据」,
|
|
668
|
+
# 让上层不必 cat / 重读产物就能确认「文件渲染对了」(省一整批往返)
|
|
669
|
+
verify = {}
|
|
670
|
+
if a.only != 'md':
|
|
671
|
+
if docx is None:
|
|
672
|
+
res.update(ok=False, error='缺少 python-docx,请先安装:pip install python-docx')
|
|
673
|
+
print(json.dumps(res, ensure_ascii=False))
|
|
674
|
+
return 1
|
|
675
|
+
if not os.path.exists(TPL):
|
|
676
|
+
res.update(ok=False, error='内置模板缺失: assets/行程单模板-泛微.docx')
|
|
677
|
+
print(json.dumps(res, ensure_ascii=False))
|
|
678
|
+
return 1
|
|
679
|
+
p, docx_qa = build_docx(d, os.path.join(a.out_dir, a.docx_name))
|
|
680
|
+
res['files'].append({'kind': 'docx', 'path': p, 'size': os.path.getsize(p), 'audience': 'customer'})
|
|
681
|
+
res['qa']['external'] = docx_qa
|
|
682
|
+
verify['docxParagraphs'] = docx_qa.get('paragraphs', 0)
|
|
683
|
+
verify['docxSha256'] = sha8(p)
|
|
684
|
+
verify['externalLeaks'] = docx_qa.get('externalLeaks', 0)
|
|
685
|
+
if docx_qa.get('cleaned'):
|
|
686
|
+
verify['cleaned'] = docx_qa['cleaned']
|
|
687
|
+
warn = list(res['qa'].get('warnings') or [])
|
|
688
|
+
if docx_qa.get('externalLeaks'):
|
|
689
|
+
warn.append(f'对外行程单仍含内部口径 {docx_qa["externalLeaks"]} 处,请检查')
|
|
690
|
+
if warn:
|
|
691
|
+
res['qa']['warnings'] = warn
|
|
692
|
+
if a.only != 'docx':
|
|
693
|
+
p = build_md(d, os.path.join(a.out_dir, a.md_name))
|
|
694
|
+
res['files'].append({'kind': 'md', 'path': p, 'size': os.path.getsize(p), 'audience': 'internal'})
|
|
695
|
+
try:
|
|
696
|
+
with open(p, encoding='utf-8') as f:
|
|
697
|
+
secs = [ln.strip() for ln in f.read().splitlines() if ln.startswith('## ')]
|
|
698
|
+
except Exception:
|
|
699
|
+
secs = []
|
|
700
|
+
verify['mdSections'] = len(secs)
|
|
701
|
+
verify['mdHead'] = secs[0] if secs else ''
|
|
702
|
+
verify['mdTail'] = secs[-1] if secs else ''
|
|
703
|
+
verify['mdSha256'] = sha8(p)
|
|
704
|
+
|
|
705
|
+
# v4.7:把日程行结构化输出,供流程【日程明细】子表直接使用
|
|
706
|
+
agenda_rows = norm_agenda(d)
|
|
707
|
+
res['agendaRows'] = agenda_rows
|
|
708
|
+
res['scheduleRows'] = [{
|
|
709
|
+
'date': d.get('visitDate') or '',
|
|
710
|
+
'time': r.get('start') or '',
|
|
711
|
+
'item': r.get('item') or '',
|
|
712
|
+
'place': r.get('place') or '',
|
|
713
|
+
'person': r.get('person') or '',
|
|
714
|
+
'activity': r.get('activity') or '',
|
|
715
|
+
'remark': r.get('remark') or '',
|
|
716
|
+
} for r in agenda_rows]
|
|
717
|
+
verify['fileCount'] = len(res['files'])
|
|
718
|
+
res['verify'] = verify
|
|
719
|
+
print(json.dumps(res, ensure_ascii=False))
|
|
720
|
+
return 0
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
if __name__ == '__main__':
|
|
724
|
+
sys.exit(main())
|