WAPI 固化企微 API 能力目录与读取口径

这个提交包含在:
2026-05-18 22:36:56 +08:00
父节点 6273201a13
当前提交 d81b5425a7
共修改 13 个文件,包含 276 行新增和 153 行删除
+4 -3
查看文件
@@ -15,8 +15,9 @@ Read only what is relevant:
- After R5, first check the main R5/R6 contract status before planning trial-run or follow-up work; R5 must remain the completed gate and R6 must have its own Issue CSV before expanding scope.
- For UI/page work, PC/mobile/WeCom experience, approval UX, or domestic OA benchmarking, read `docs/产品设计规则.md` and `references/visual-qa.md`. DG8 makes Feishu workbench/approval the single main UI/layout reference, with Hyinda branding and project rules taking priority. DG9 adds the A/B/C page layering and shared workbench/mobile samples; choose the DG9 sample before designing or coding a page. Other products are only function/process/control references. Mobile page work must treat `docs/产品设计规则.md` as the source of truth for the unified mobile shell, icon grid, card lists, deep Push pages, sticky actions, safe-area, and cross-viewport matrix. 移动页必须先对齐该设计事实源,再进入实现或验证。
- For approval workflow, roles, self-approval, notification, deeplink, reminder, or escalation work, read `docs/需求-统一审批中心规约.md`.
- For HR lifecycle, employee onboarding/offboarding, Enterprise WeChat directory sync, or account disablement work, read `docs/需求-企业微信基础设施规约.md`, `docs/管理员手册.md`, and the latest HCLT/HRWD Issue CSV evidence first.
- For Enterprise WeChat-first HR/attendance runtime migration, read `docs/企微优先开发路线图.md` before planning or coding. WDRG makes the roadmap authoritative for stage order; WCPC has rerun the capability POC with an existing active test account and reached `GO_TO_HR_MASTER_SYNC`; WHMS/WHMSLIVE has implemented, deployed, and publicly verified `wecom_hr_master_sync_v1`, with the production flag restored to `force_off` and production real-employee batch apply not approved. Do not start `wecom_attendance_approval_mirror_v1`, `oa_hr_emergency_mode_v1`, `wecom_recovery_writeback_v1`, or `wecom_ruleops_v1` without the next stage's own Issue CSV.
- For HR lifecycle, employee onboarding/offboarding, Enterprise WeChat directory sync, account disablement, or Enterprise WeChat API work, read `docs/需求-企业微信基础设施规约.md`, `docs/企微API能力目录.md`, `docs/管理员手册.md`, and the latest HCLT/HRWD/WAPI Issue CSV evidence first.
- For any Enterprise WeChat capability, Secret, callback, approval, check-in, HR assistant, or contact-directory task, read `docs/企微API能力目录.md` before planning or coding. WAPI fixes the current Secret matrix: contacts use `WECOM_CONTACT_SECRET`; workbench/OAuth/messages/HR assistant/roster/check-in/approval use `WECOM_AGENT_SECRET`; `WECOM_CHECKIN_SECRET` is only an explicit check-in override; `WECOM_HR_ASSISTANT_SECRET` is historical compatibility and not a default. Do not write runtime code that tries multiple Secrets to guess the correct one.
- For Enterprise WeChat-first HR/attendance runtime migration, read `docs/企微优先开发路线图.md` before planning or coding. WDRG makes the roadmap authoritative for stage order; WAPI adds the runtime-first stage before deep mirror work; WCPC has rerun the capability POC with an existing active test account and reached `GO_TO_HR_MASTER_SYNC`; WHMS/WHMSLIVE has implemented, deployed, and publicly verified `wecom_hr_master_sync_v1`, with the production flag restored to `force_off` and production real-employee batch apply not approved. Do not start `wecom_attendance_approval_mirror_v1`, `oa_hr_emergency_mode_v1`, `wecom_recovery_writeback_v1`, or `wecom_ruleops_v1` without the next stage's own Issue CSV.
- For reimbursement, invoice, OCR, duplicate invoice, or attachment work, read `docs/报销发票附件规则.md` and `references/reimbursement.md`.
- For local/WeCom/public trial-run, OCR runtime, Caddy, or DB drift work, read `docs/试运行与自动化验证手册.md` and `references/trialrun.md`.
- For how this skill should evolve, read `docs/Agent开发交付实践.md`.
@@ -105,7 +106,7 @@ Final responses should include:
- R6 scope defaults to reimbursement, leave, procurement, approval, Enterprise WeChat entry/notification, finance payment/export.
- Do not expand to budget, ERP, AI, full travel, or complex BPM during R6; record those as follow-up Issues.
- Enterprise WeChat personal app message is the formal approval notification channel after R5; mock and group bots are not acceptance evidence for R6 business notification.
- After WHMSLIVE, ERP is not a future OA integration target. HR/attendance employee daily approvals (leave, missed punch, outing, overtime) should reuse mature Enterprise WeChat capabilities first, while OA provides domain-based mirror, manual takeover, recovery writeback, finance closeout, and audit. Reimbursement, payment batches, AP, accounting handoff, procurement, expenses, contracts, projects, sensitive attachments, invoices, bank receipts, and accounting handoff materials remain OA-primary unless a later Issue changes that domain explicitly. OA-primary approvals may send Enterprise WeChat personal-app notifications, but messages must contain only dehydrated title/type/no/status/deeplink; do not upload procurement forms, contract bodies, quotes, supplier files, invoices, bank receipts, or accounting materials to WeCom native approvals or WeCom attachments. Before runtime migration, read `docs/企微优先开发路线图.md`, run or check `WECOM-CAPABILITY-POC-V1`/WCPC, and classify each capability as `CAN_REUSE_NOW`, `NEEDS_CONFIG`, `NEEDS_API_POC`, `KEEP_OA_PRIMARY`, or `NOT_SUPPORTED_BY_API`; missing Secret, callback, template, trusted IP, visible scope, or API permission must be recorded as `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`, not PASS. Current WCPC status: existing active test account `SMOKE002/测试员工` (reported only as `oa_smoke***`) reran successfully with report `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`, `status=PASS`, `conclusion=GO_TO_HR_MASTER_SYNC`, and `blockingCapabilities=[]`. Current WHMSLIVE status: `wecom_hr_master_sync_v1` is implemented and deployed with feature flag default/final `force_off`, HR/contact read service, dry-run/apply, sync run/item records, People management card, and QA-only runner `scripts/qa/check-wecom-hr-master-sync.mjs`; public verification applied migration `20260518184319_wecom_hr_master_sync_v1`, temporarily forced the flag on, ran dry-run and explicit apply for `SMOKE002` (`oa_smoke***`), verified ordinary employee API denial with 403, then restored `force_off`. The SMOKE002 apply did not write OA master data because WeCom lacks phone/email/department and was correctly skipped as `missing_required_field`; production real-employee batch apply is not approved. WHMS rules: missing OA-required fields are skipped as `missing_required_field`, inactive WeCom status becomes `needs_offboarding_confirmation`, and OA accounts are not auto-disabled. Approval template ID, approval `sp_no`, callback config, attendance/approval mirror, emergency mode, recovery writeback, RuleOps, and HR write APIs remain later-stage gaps. Do not skip stage order: `WECOM-CAPABILITY-POC-V1`/WCPC -> `wecom_hr_master_sync_v1`/WHMSLIVE -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1`. UI and API work must distinguish `OA 主办`, `企微镜像`, and `OA 应急接管`. OA-driven Enterprise WeChat directory create/update/disable is live verified; for later HR/WeCom directory issues, first check configuration, permission, department, and data-specific causes before treating the capability as unfinished. After WCFB, Enterprise WeChat check-in sync uses an authorized custom app Secret: `WECOM_CHECKIN_SECRET` is an explicit override for a dedicated check-in app, and when it is empty OA can reuse the authorized workbench app `WECOM_AGENT_SECRET`.
- After WAPI, ERP is not a future OA integration target. HR/attendance employee daily approvals (leave, missed punch, outing, overtime) should reuse mature Enterprise WeChat capabilities first, while OA provides domain-based mirror, manual takeover, recovery writeback, finance closeout, and audit. Reimbursement, payment batches, AP, accounting handoff, procurement, expenses, contracts, projects, sensitive attachments, invoices, bank receipts, and accounting handoff materials remain OA-primary unless a later Issue changes that domain explicitly. OA-primary approvals may send Enterprise WeChat personal-app notifications, but messages must contain only dehydrated title/type/no/status/deeplink; do not upload procurement forms, contract bodies, quotes, supplier files, invoices, bank receipts, or accounting materials to WeCom native approvals or WeCom attachments. Before runtime migration, read `docs/企微API能力目录.md` and `docs/企微优先开发路线图.md`, run or check `WECOM-CAPABILITY-POC-V1`/WCPC, and classify each capability as `CAN_REUSE_NOW`, `NEEDS_CONFIG`, `NEEDS_API_POC`, `KEEP_OA_PRIMARY`, or `NOT_SUPPORTED_BY_API`; missing Secret, callback, template, trusted IP, visible scope, or API permission must be recorded as `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`, not PASS. Current WAPI rule: contacts read with `WECOM_CONTACT_SECRET` and `POST /cgi-bin/user/list_id`; HR assistant/roster uses `WECOM_AGENT_SECRET` and `POST /cgi-bin/hr/get_staff_info`; check-in defaults to `WECOM_AGENT_SECRET` with optional explicit `WECOM_CHECKIN_SECRET`; no runtime Secret guessing. Current WCPC status: existing active test account `SMOKE002/测试员工` (reported only as `oa_smoke***`) reran successfully with report `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`, `status=PASS`, `conclusion=GO_TO_HR_MASTER_SYNC`, and `blockingCapabilities=[]`. Current WHMSLIVE status: `wecom_hr_master_sync_v1` is implemented and deployed with feature flag default/final `force_off`, HR/contact read service, dry-run/apply, sync run/item records, People management card, and QA-only runner `scripts/qa/check-wecom-hr-master-sync.mjs`; public verification applied migration `20260518184319_wecom_hr_master_sync_v1`, temporarily forced the flag on, ran dry-run and explicit apply for `SMOKE002` (`oa_smoke***`), verified ordinary employee API denial with 403, then restored `force_off`. The SMOKE002 apply did not write OA master data because WeCom lacks phone/email/department and was correctly skipped as `missing_required_field`; production real-employee batch apply is not approved. WHMS rules: missing OA-required fields are skipped as `missing_required_field`, inactive WeCom status becomes `needs_offboarding_confirmation`, and OA accounts are not auto-disabled. Approval template ID, approval `sp_no`, callback config, attendance/approval mirror, emergency mode, recovery writeback, RuleOps, and HR write APIs remain later-stage gaps. Do not skip stage order: `WECOM-CAPABILITY-POC-V1`/WCPC -> `wecom_hr_master_sync_v1`/WHMSLIVE -> `runtime_first_wecom_enablement`/WAPI+WOUA rerun -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1`. UI and API work must distinguish `OA 主办`, `企微镜像`, and `OA 应急接管`. OA-driven Enterprise WeChat directory create/update/disable is live verified; for later HR/WeCom directory issues, first check configuration, permission, department, and data-specific causes before treating the capability as unfinished.
- Approval-chain correctness can be proven by PC or automation, while Enterprise WeChat validates entry, binding,免登录, notification, deeplink, and mobile layout.
- If real-device mobile validation is paused after automation passes, record the gate as `PAUSED`; do not close RS6/UI6/R6 mobile blockers until representative real-device confirmation is recorded.
- Role-specific pages should use one shared OA/mobile framework and tailor summaries, entries, actions, and sensitive fields by role. Do not create unrelated UI systems for employee, approver, finance, admin, or CEO views.
+3 -3
查看文件
@@ -47,7 +47,7 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
- 2026-05-18 WCP1 已完成企业微信能力 POC:新增 QA-only 只读 runner `scripts/qa/check-wecom-capability-poc.mjs`,本地不开启 live read 时可解释为 `PAUSE_FOR_API_POC`,本地开启 live read 但无真实 env 时可解释为 `PAUSE_FOR_CONFIG`;部署服务器容器内 live 只读探测报告为远端 `/tmp/wecom-capability-poc-wcp1/summary.json`,历史结论为 `PAUSE_FOR_CONFIG`。该报告继续作为初始配置缺口证据保留,当前 HR 主数据镜像准入以 WCPC 复跑结论为准。
- 2026-05-18 WCPC 已完成 WCP1 配置收口复跑:先完成未补配置前远端基线复跑,报告目录 `/tmp/wecom-capability-poc-wcpc-baseline`,结论为 `PAUSE_FOR_CONFIG`;随后按用户要求自动查找并使用既有在职低风险测试号 `SMOKE002/测试员工`(报告只记录 `oa_smoke***`)复跑,只读报告 `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json` 返回 `PASS / GO_TO_HR_MASTER_SYNC`,`nextStageAllowed=true`,`blockingCapabilities=[]`,状态计数为 `CAN_REUSE_NOW=15`、`NEEDS_CONFIG=3`、`NEEDS_API_POC=1`、`KEEP_OA_PRIMARY=1`。本轮不部署、不改业务代码、不持久写服务器 `.env`;下一步允许规划 `wecom_hr_master_sync_v1`,审批模板/实例、回调配置和 HR 写接口仍留后续合同。
- 2026-05-18 WHMS 已完成企微 HR 主数据镜像本地实现、部署和受控公网验证:新增 `wecom_hr_master_sync_v1` 默认 `force_off`,实现企业微信通讯录/人事助手读取、dry-run/apply、同步批次/逐员工记录、管理员 API、People 管理卡片和 QA-only runner。WHMSLIVE 部署到 `https://oa.hyinda.com` 并应用 migration `20260518184319_wecom_hr_master_sync_v1`;公网临时 `force_on` 后对 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply,企微读取链路通过,因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,未写 OA 主数据;普通员工访问同步 API 返回 403;最终 `wecom_hr_master_sync_v1=force_off`。未执行生产真实员工批量 apply,不代表考勤/审批镜像、应急接管、恢复回写或 RuleOps 已完成。
- 2026-05-18 WOUA 已完成 OA active 员工 vs 企业微信通讯录 + 人事助手的只读对账 runner、本地门禁、受控部署和公网执行。公网 OA 侧只读成功,脱敏报告记录 `oaEmployees=13`、`oaActiveEmployees=12`、`oaActiveWithBinding=5`;企微侧全量读取被外部配置/权限阻断,结论为 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`,错误码 `wecom_errcode_60011`。本轮未修改 OA 或企微用户,未执行真实员工批量 apply,不能据此判定“OA 和企微用户已一致”;进入考勤/审批镜像前,应先处理企微读权限或另建 WOUA-FIX/WHMS 批量同步窗口。
- 2026-05-18 WOUA 已完成 OA active 员工 vs 企业微信通讯录 + 人事助手的只读对账 runner、本地门禁、受控部署和公网执行。初始公网报告因旧读取路径返回 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`;WAPI 固化 Secret 矩阵和 `user/list_id` + 人事助手读取后复跑成功,远端脱敏报告 `/app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json` 结论为 `PASS_WITH_FINDINGS`。当前只读计数:`oaEmployees=13`、`oaActiveEmployees=12`、`oaActiveWithBinding=5`、`wecomProfiles=5`、`matchedActive=5`、`oaActiveMissingInWecom=7`、`inactiveWithBinding=1`、`missingRequiredFields=5`。本轮未修改 OA 或企微用户,未执行真实员工批量 apply,不能据此判定“OA 和企微用户已一致”;下一步应按差异决定 WOUA-FIX 或 WHMS 批量同步窗口。
## 关键证据索引
@@ -94,7 +94,7 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
| AUTO-REMAINING-CLOSEOUT | 已收口:剩余自动化运营门禁通过,结论 `GO_TO_OPERATE`;公网 `payment_batch_v1=force_on`、`erp_sync=force_off`,付款批次常开闭环、AP 导出、审计、公网移动和企业微信渲染矩阵均通过;企微打卡同步外部配置仍记 `SKIP` | `issues/2026-05-17_19-30-44-auto-remaining-closeout.csv`、`artifacts/qa-flow/launch-auto-ops-20260517-193317/summary.json`、`artifacts/qa-flow/launch-auto-ops-20260517-193317/live-result.json` |
| HR-WECOM-DIRECTORY-V1 | 已收口:人事生命周期与企业微信通讯录同步闭环已部署公网;ERP 改为未来不接入 OA;员工创建/入职/调岗/资料变更/离职可触发企微通讯录创建、更新、禁用并记录同步状态;缺外部配置时公网重试记录 `SKIP wecom_contact_sync_disabled`,不阻断 OA 人事流程;部署 seed 已修正为不覆盖正式 flag | `issues/2026-05-17_20-00-40-hr-wecom-directory-v1.csv`、`server/tests/unit/routes/employees.wecomDirectory.test.js`、`artifacts/qa-flow/live-smoke-readiness-20260517-205813/summary.json`、`docs/需求-企业微信基础设施规约.md` |
| HRWD-CONTACT-LIVE-TEST | 已收口:公网企微通讯录真实写入闭环通过;测试员工 `HRWDTEST-20260517214548` 完成 create/update/disable,企微 `errcode=0`,OA 最终 archived/inactive,离职后登录负例、普通员工权限负例和审计动作均通过;最终保留离职/禁用,不删除成员 | `issues/2026-05-17_21-41-43-hrwd-contact-live-test.csv`、`artifacts/qa-flow/hrwd-contact-live-test-20260517T134548Z/summary.json`、`artifacts/qa-flow/live-smoke-readiness-20260517-214632/summary.json` |
| WOUA | 已收口:OA/企微用户一致性只读审计 runner 已部署执行;公网报告为 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`,企微读取阻断 `wecom_errcode_60011`,OA 侧脱敏计数为 13/12/5;未修改 OA/企微用户,未执行真实员工批量 apply | `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv`、`artifacts/qa-flow/wecom-oa-user-consistency-20260518-2055/summary.json` |
| WOUA/WAPI | 已收口:OA/企微用户一致性只读审计 runner 已按 WAPI 读取口径复跑;公网报告 `PASS_WITH_FINDINGS`,OA 侧脱敏计数 13/12/5,企微可见 active 5,匹配 5,OA active 缺企微 7,已归档仍有绑定 1,缺必填字段 5;未修改 OA/企微用户,未执行真实员工批量 apply | `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv`、`issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv`、`/app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json` |
## 当前门禁
@@ -138,7 +138,7 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
| WCP1 企业微信能力 POC | 完成只读 POC,历史结论 `PAUSE_FOR_CONFIG` | Issue `issues/2026-05-18_13-30-07-wecom-capability-poc-v1.csv`;新增 `scripts/qa/check-wecom-capability-poc.mjs`。本地脚本门禁通过,远端容器内 live 只读报告为 `/tmp/wecom-capability-poc-wcp1/summary.json`:`CAN_REUSE_NOW=9`、`NEEDS_CONFIG=8`、`NEEDS_API_POC=1`、`KEEP_OA_PRIMARY=1`。阻断为 `contact.member_read=wecom_errcode_48009`、`hr.staff_read=wecom_errcode_60021`、打卡同步未开启、缺审批模板/实例、假期权限 `wecom_errcode_301062` 和回调配置;该历史阻断已由 WCPC 复跑收口。 |
| WCPC 企业微信能力 POC 配置收口 | 通过,结论 `GO_TO_HR_MASTER_SYNC` | Issue `issues/2026-05-18_15-01-23-wecom-capability-poc-config-closeout-v1.csv`;未补配置前远端基线复跑报告 `/tmp/wecom-capability-poc-wcpc-baseline` 为 `PASS / PAUSE_FOR_CONFIG`;随后使用既有在职测试号 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)完成只读复跑,报告 `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`,`nextStageAllowed=true`,`blockingCapabilities=[]`,`CAN_REUSE_NOW=15`、`NEEDS_CONFIG=3`、`NEEDS_API_POC=1`、`KEEP_OA_PRIMARY=1`。该准入已由 WHMS 承接;审批模板/实例、回调配置和 HR 写接口仍留后续合同。 |
| WHMS 企微 HR 主数据镜像 | 已部署并完成受控公网验证 | Issue `issues/2026-05-18_18-43-19-wecom-hr-master-sync-v1.csv`、`issues/2026-05-18_19-28-25-whms-deploy-live-verify-v1.csv`;已部署 `wecom_hr_master_sync_v1`、同步批次/逐员工记录、企微通讯录/人事助手读取服务、管理员 API、People 管理卡片和 `scripts/qa/check-wecom-hr-master-sync.mjs`。公网 dry-run/apply 对 `SMOKE002` 返回 `missing_required_field` skip,未写 OA 主数据;权限负例 403;最终 `force_off`,未执行生产真实员工批量 apply。 |
| WOUA OA/企微用户一致性只读对账 | 外部配置阻断,结论 `BLOCKED_BY_EXTERNAL_CONFIG` | Issue `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv`;新增 QA-only runner `server/src/scripts/audit-wecom-oa-user-consistency.js` 和脱敏匹配单测。公网只读报告 `artifacts/qa-flow/wecom-oa-user-consistency-20260518-2055/summary.json`:OA 侧只读成功,`oaEmployees=13`、`oaActiveEmployees=12`、`oaActiveWithBinding=5`;企微侧全量读取返回 `wecom_errcode_60011`,按 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG` 收口。未打开 `wecom_hr_master_sync_v1`,未调用 WHMS dry-run/apply API,未修改 OA/企微用户。 |
| WOUA/WAPI OA/企微用户一致性只读对账 | 已复跑,结论 `PASS_WITH_FINDINGS` | Issue `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv` 和 `issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv`;WAPI 修正读取口径为通讯录 `user/list_id` + 可选成员详情 + 人事助手花名册。公网只读报告 `/app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json`:`oaEmployees=13`、`oaActiveEmployees=12`、`oaActiveWithBinding=5`、`wecomProfiles=5`、`matchedActive=5`、`oaActiveMissingInWecom=7`、`inactiveWithBinding=1`、`missingRequiredFields=5`。未打开 `wecom_hr_master_sync_v1`,未调用 WHMS dry-run/apply API,未修改 OA/企微用户。 |
## R7EP / OPS8 / QAF8 / UX8 / P2T 预检推进
+7 -4
查看文件
@@ -15,12 +15,13 @@
- 企业微信作为手机工作台入口、免登/绑定、通讯录身份、消息通知、打卡、人事助手和 HR/考勤员工日常审批主操作端;OA 主办审批通过企业微信个人应用消息发送脱敏提醒并 deeplink 回 OA 处理。
- 采购单附件、合同正文、报价、供应商资料、发票、银行回单和代账资料不上传到企业微信原生审批或企业微信附件。
- 简写口径:采购不进企微原生审批,合同不进企微原生审批,附件不进企微附件;企微只做提醒、入口和免登录回 OA。
- WAPI 后固定“运行优先”:近期先用入口、免登/绑定、脱敏消息、deeplink、通讯录 userid 清单、人事助手花名册读取和打卡读取;深度镜像和功能对齐暂缓。
### P0-2 企业微信打卡与 OA 考勤口径
- 企业微信打卡用于原始采集,保留其定位、人脸、Wi-Fi、规则与设备能力。
- OA 后续同步企业微信打卡规则、排班、记录、日报、月报和 HR/考勤审批镜像,形成正式考勤口径。
- 请假、补卡、外出、加班等考勤修正正常在企业微信完成;OA 只在应急模式下接管,并在企微恢复后 dry-run 和自动回写。
- `wecom_hr_master_sync_v1` 已本地实现并部署公网完成受控验证;最终保持 `force_off`,后续考勤/审批镜像、应急接管、恢复回写或 RuleOps 仍必须另建 `wecom_attendance_approval_mirror_v1`(承接旧口径 `wecom_native_approval_mirror_v1`)、`oa_hr_emergency_mode_v1`、`wecom_recovery_writeback_v1` 或 `wecom_ruleops_v1` 合同。
- `wecom_hr_master_sync_v1` 已本地实现并部署公网完成受控验证;最终保持 `force_off`。WAPI 已复跑用户一致性只读对账,结论为 `PASS_WITH_FINDINGS`,说明 OA/企微用户仍不一致;后续考勤/审批镜像、应急接管、恢复回写或 RuleOps 仍必须另建 `wecom_attendance_approval_mirror_v1`(承接旧口径 `wecom_native_approval_mirror_v1`)、`oa_hr_emergency_mode_v1`、`wecom_recovery_writeback_v1` 或 `wecom_ruleops_v1` 合同。
### P0-3 按域镜像定义
- 企微主办域镜像到 OA:通讯录/人事助手、打卡、请假、补卡、外出、加班、假期余额和 HR/考勤规则。
@@ -31,14 +32,15 @@
- WFDG 后,后续企微/OA 改造必须先按业务域判定主办方、主事实源、操作端、镜像方向、附件归属和审计责任,再拆实现合同。
- PC 和移动页面需要区分三种状态:`OA 主办`、`企微镜像`、`OA 应急接管`;不得把企微镜像页面误做成员工日常新建入口。
- 企业微信同步、镜像和回写必须具备幂等、重试、脱敏日志、审计记录和 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG` 状态;缺 Secret、权限、模板或可信 IP 时不得伪造成成功。
- 下一轮先做 `WECOM-CAPABILITY-POC-V1`,输出 `CAN_REUSE_NOW`、`NEEDS_CONFIG`、`NEEDS_API_POC`、`KEEP_OA_PRIMARY`、`NOT_SUPPORTED_BY_API` 能力矩阵,再进入正式开发。
- 企微能力不清楚时先读 [企微 API 能力目录](./企微API能力目录.md),输出 `CAN_REUSE_NOW`、`NEEDS_CONFIG`、`NEEDS_API_POC`、`KEEP_OA_PRIMARY`、`NOT_SUPPORTED_BY_API` 能力矩阵,再进入正式开发。
### P0-5 WDRG 开发路线图门禁
- WDRG 后采用“路线图门禁 + 单功能 Issue CSV”:路线图只控制阶段顺序、准入/退出门禁、并行规则和禁止跳阶段,具体功能仍单独方案、单独 Issue CSV、单独验证。
- 当前企微优先 HR/考勤路线顺序固定为:`WECOM-CAPABILITY-POC-V1` -> `wecom_hr_master_sync_v1` -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1`。
- 当前企微优先 HR/考勤路线顺序固定为:`WECOM-CAPABILITY-POC-V1` -> `wecom_hr_master_sync_v1` -> `runtime_first_wecom_enablement` -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1`。
- `WECOM-CAPABILITY-POC-V1` 未完成前,不得直接开发 HR 主数据镜像、考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps 正式写入。
- 线上缺陷、正式使用阻断、真实反馈扫尾和 QA-only 脚本维护可以另开合同并行;采购、合同、报销、付款、AP、代账和高敏附件继续 OA 主办,不混入企微 HR/考勤路线。
- 路线图事实源为 [企微优先开发路线图](./企微优先开发路线图.md);该路线图不代表任何运行时迁移能力已完成。
- API 和 Secret 事实源为 [企微 API 能力目录](./企微API能力目录.md):通讯录用 `WECOM_CONTACT_SECRET`;人事助手、打卡、消息和 OAuth 默认用 `WECOM_AGENT_SECRET`;不得在运行时代码里轮询多个 Secret 试错。
### P0-6 WCP1 企业微信能力 POC 结论
- WCP1 已完成 QA-only 只读 POC,历史结论为 `PAUSE_FOR_CONFIG`;该阻断已由 WCPC 复跑收口。
@@ -59,7 +61,8 @@
- 缺姓名、手机号、邮箱或部门时记录 `missing_required_field` 并跳过,不生成占位数据;企微不在职状态记录 `needs_offboarding_confirmation`,不自动禁用 OA 账号。
- WHMSLIVE 公网验证中,`SMOKE002/测试员工`(脱敏 `oa_smoke***`)因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,证明缺字段不会写入占位数据;普通员工访问同步 API 已验证为 403。
- 已有 OA 员工只更新安全主数据字段,不覆盖银行账户、身份证、工资、合同附件或历史审批。
- WHMS 不代表考勤/审批镜像、审批回调、OA 应急接管、恢复回写或 RuleOps 已完成;下一阶段仍需另建 `wecom_attendance_approval_mirror_v1`。
- WAPI 后 WHMS/WOUA 读取策略修正为通讯录 `user/list_id` + 可选成员详情 + 人事助手 `get_staff_info`,通讯录用 `WECOM_CONTACT_SECRET`,人事助手用 `WECOM_AGENT_SECRET`,不再靠 fallback 试错。
- WHMS 不代表考勤/审批镜像、审批回调、OA 应急接管、恢复回写或 RuleOps 已完成;下一步先处理 WOUA 差异或确定 WHMS 批量同步窗口,之后再判断是否另建 `wecom_attendance_approval_mirror_v1`。
---
+73
查看文件
@@ -0,0 +1,73 @@
# 企微 API 能力目录
**文档状态**:当前企微 API 调研与本项目使用事实源。
**分类**:集成事实源 / API 能力目录。
**适用时间**:WAPI 之后。
**关联 Issue**:`WAPI`。
**最近核对**:2026-05-18。
**不得误读**:本文只记录本项目会用到的企业微信官方 API 摘要、链接和项目决策,不复制官方文档全文,不保存 Secret、access token、完整 userid、手机号、邮箱或企微原始 payload。官方文档和企业微信后台权限配置始终是最终依据。
## 1. 固定规则
- 任何企微能力不清楚时,先查企业微信官方开发者文档,再进入 Issue CSV;不得在运行时代码里靠轮询多个 Secret 试错。
- 每个企微 Issue 必须明确接口路径、请求方法、Secret 来源、应用可见范围、可信 IP、回调/模板要求、读写边界、脱敏输出和 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG` 语义。
- 产品 UI 支持不等于开放 API 支持。入职、转正、离职、工资条确认、考勤确认等人事助手产品能力,只有在官方 API 明确支持并通过 POC 后才能进入自动化实现。
- 生产写接口、真实员工批量同步、企微规则写入、应急模式开启和恢复回写都必须另建合同并获得明确批准。
## 2. Secret 矩阵
| Secret | 当前标准用途 | 禁止或限制 |
| --- | --- | --- |
| `WECOM_CONTACT_SECRET` | 通讯录专用:成员 ID 清单、成员详情、部门、创建/更新/禁用成员。 | 不用于工作台消息、人事助手、打卡或审批;运行时代码不得 fallback 到 `WECOM_AGENT_SECRET`。 |
| `WECOM_AGENT_SECRET` | 工作台自建应用和多数开放 API:OAuth/免登、应用消息、人事助手/花名册、打卡、审批读取。 | 不作为通讯录读取的默认 fallback;缺权限时记录阻断,不换 Secret 试错。 |
| `WECOM_CHECKIN_SECRET` | 仅作为专用打卡应用的显式 override;为空时使用 `WECOM_AGENT_SECRET`。 | 不要求为了打卡重复维护 Secret;只有后台单独建打卡应用时才配置。 |
| `WECOM_HR_ASSISTANT_SECRET` | 当前不作为标准配置,历史兼容项。 | 默认不参与人事助手读取;后续若确需启用,必须先更新本文和 Issue CSV。 |
## 3. 能力目录
| 能力 | 官方文档 | 关键接口 | 方法 | Secret | 当前项目状态 | 本项目使用决策 |
| --- | --- | --- | --- | --- | --- | --- |
| 基础 token | [获取 access_token](https://developer.work.weixin.qq.com/document/path/91039) | `/cgi-bin/gettoken` | `GET` | 按能力域选择对应 Secret | `CAN_REUSE_NOW` | 所有服务端 API 的前置;日志只记录 token source,不输出 token。 |
| 回调配置 | [回调配置](https://developer.work.weixin.qq.com/document/path/90930) | 企业微信后台配置 URL、Token、EncodingAESKey | `GET/POST` 验证事件 | 按回调所属应用 | `NEEDS_CONFIG` | 考勤/审批镜像前必须先证明真实回调可达、可验签、可解密、可幂等入库。 |
| 企业微信接口 IP | [获取企业微信接口 IP 段](https://developer.work.weixin.qq.com/document/path/92520) | `/cgi-bin/get_api_domain_ip` | `GET` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW` | 只作为运维排障和防火墙/可信 IP 核对参考。 |
| 企业微信回调 IP | [获取企业微信回调 IP 段](https://developer.work.weixin.qq.com/document/path/92521) | `/cgi-bin/getcallbackip` | `GET` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW` | 用于回调入口放行和安全审计,不进入业务数据。 |
| 网页授权链接 | [构造网页授权链接](https://developer.work.weixin.qq.com/document/path/91022) | `connect/oauth2/authorize` | 浏览器跳转 | `WECOM_AGENT_SECRET` 所属应用 | `CAN_REUSE_NOW` | 企业微信工作台入口和 deeplink 回 OA 使用;只改变登录体验,不改变 OA 权限。 |
| 访问用户身份 | [获取访问用户身份](https://developer.work.weixin.qq.com/document/path/91023) | `/cgi-bin/auth/getuserinfo` | `GET` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW` | 获取当前企业微信 userid 后匹配 OA 绑定;不能仅靠手机号/邮箱授权登录。 |
| 应用消息 | [发送应用消息](https://developer.work.weixin.qq.com/document/path/90236) | `/cgi-bin/message/send` | `POST` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW` | OA 主办审批只发脱敏标题、类型、编号、状态和 OA deeplink;不发送高敏附件。 |
| 通讯录概述 | [通讯录管理](https://developer.work.weixin.qq.com/document/path/90193) | 通讯录成员、部门、回调和导入导出 | 混合 | `WECOM_CONTACT_SECRET` | `CAN_REUSE_NOW_PARTIAL` | 通讯录是单独 Secret 域,和工作台应用 Secret 分开管理。 |
| 成员 ID 清单 | [获取成员 ID 列表](https://developer.work.weixin.qq.com/document/path/96067) | `/cgi-bin/user/list_id` | `POST` | `WECOM_CONTACT_SECRET` | `CAN_REUSE_NOW` | WOUA/WHMS 全量候选必须走该接口,请求体只使用 `cursor`/`limit`,返回用户-部门关系后本地按 userid 去重;不再用旧 `/cgi-bin/user/list` 做全量读取。 |
| 读取成员 | [读取成员](https://developer.work.weixin.qq.com/document/path/90196) | `/cgi-bin/user/get` | `GET` | `WECOM_CONTACT_SECRET` | `OPTIONAL_ENRICHMENT` | 用于补齐 userid 对应的通讯录详情;若 `list_id` 与人事助手花名册可读,详情权限不足不阻断 WOUA,只降低可富化字段完整度。 |
| 获取部门成员 | [获取部门成员](https://developer.work.weixin.qq.com/document/path/90200) | `/cgi-bin/user/simplelist` | `GET` | `WECOM_CONTACT_SECRET` | `LEGACY_NOT_DEFAULT` | 仅作历史参考,当前全量读取不依赖它。 |
| 获取部门成员详情 | [获取部门成员详情](https://developer.work.weixin.qq.com/document/path/90201) | `/cgi-bin/user/list` | `GET` | `WECOM_CONTACT_SECRET` | `LEGACY_NOT_DEFAULT` | 当前部署曾返回权限错误,不再作为 WOUA/WHMS 全量入口。 |
| 获取部门列表 | [获取部门列表](https://developer.work.weixin.qq.com/document/path/90208) | `/cgi-bin/department/list` | `GET` | `WECOM_CONTACT_SECRET` | `OPTIONAL_ENRICHMENT` | 只用于部门名富化;失败不得阻断 userid 清单读取和只读对账。 |
| 创建成员 | [创建成员](https://developer.work.weixin.qq.com/document/path/90195) | `/cgi-bin/user/create` | `POST` | `WECOM_CONTACT_SECRET` | `DONE_LIVE_VERIFIED` | 已由 HRWD/HCLT 验证公网 create;仅作为管理员修复/迁移能力,不作为默认入职主流程。 |
| 更新成员 | [更新成员](https://developer.work.weixin.qq.com/document/path/90197) | `/cgi-bin/user/update` | `POST` | `WECOM_CONTACT_SECRET` | `DONE_LIVE_VERIFIED` | 仅在明确合同下做受控更新;不得静默覆盖企微资料。 |
| 删除/禁用成员 | [删除成员](https://developer.work.weixin.qq.com/document/path/90198) | `/cgi-bin/user/delete` 或更新启用状态 | `GET/POST` | `WECOM_CONTACT_SECRET` | `DONE_LIVE_VERIFIED_FOR_DISABLE` | 本项目离职默认禁用成员,不删除成员;历史业务和绑定审计保留。 |
| 人事助手概述 | [人事助手](https://developer.work.weixin.qq.com/document/path/99130) | 字段配置、花名册读取、花名册更新 | 混合 | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW_FOR_READ` | 人事助手产品 UI 功能很多,但当前 API 只按官方开放接口使用。 |
| 员工字段配置 | [获取员工字段配置](https://developer.work.weixin.qq.com/document/path/99131) | `/cgi-bin/hr/get_fields` | `GET` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW` | 作为花名册字段解释和后续字段映射依据。 |
| 花名册读取 | [获取员工花名册信息](https://developer.work.weixin.qq.com/document/path/99132) | `/cgi-bin/hr/get_staff_info` | `POST` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW` | WHMS/WOUA 读取员工在职资料;不使用 `WECOM_HR_ASSISTANT_SECRET`。 |
| 花名册更新 | [更新员工花名册信息](https://developer.work.weixin.qq.com/document/path/99133) | `/cgi-bin/hr/update_staff_info` | `POST` | `WECOM_AGENT_SECRET` | `NEEDS_API_POC_WRITE` | 写接口,后续需单独低风险 POC 和人工批准;本轮不调用。 |
| 打卡概述 | [打卡](https://developer.work.weixin.qq.com/document/path/90261) | 记录、规则、日报、月报、排班 | 混合 | `WECOM_AGENT_SECRET` 或显式 `WECOM_CHECKIN_SECRET` | `CAN_REUSE_NOW_FOR_READ_PARTIAL` | 企业微信继续做原始采集和风控,OA 只读同步摘要和解释。 |
| 打卡记录 | [获取打卡记录数据](https://developer.work.weixin.qq.com/document/path/90262) | `/cgi-bin/checkin/getcheckindata` | `POST` | `WECOM_AGENT_SECRET` 或显式 `WECOM_CHECKIN_SECRET` | `CAN_REUSE_NOW_FOR_READ` | 已用于考勤同步 V1;不保存完整位置、人脸、设备明文或原始 payload。 |
| 员工打卡规则 | [获取员工打卡规则](https://developer.work.weixin.qq.com/document/path/90263) | `/cgi-bin/checkin/getcheckinoption` | `POST` | `WECOM_AGENT_SECRET` 或显式 `WECOM_CHECKIN_SECRET` | `CAN_REUSE_NOW_FOR_READ` | 可作为后续考勤解释和规则快照依据。 |
| 打卡日报/月报/排班 | [日报](https://developer.work.weixin.qq.com/document/path/93374)、[月报](https://developer.work.weixin.qq.com/document/path/93387)、[排班](https://developer.work.weixin.qq.com/document/path/93380) | 官方打卡扩展接口 | `POST` 为主 | `WECOM_AGENT_SECRET` 或显式 `WECOM_CHECKIN_SECRET` | `NEEDS_API_POC` | 不进入当前运行优先主线;后续考勤镜像合同时再做只读 POC。 |
| 审批概述 | [审批](https://developer.work.weixin.qq.com/document/path/91854) | 模板、实例、回调、假期 | 混合 | `WECOM_AGENT_SECRET` | `PARTIAL_NEEDS_CONFIG` | HR/考勤员工日常审批可优先在企微处理,但 OA 镜像尚未实现。 |
| 审批模板详情 | [获取审批模板详情](https://developer.work.weixin.qq.com/document/path/91982) | `/cgi-bin/oa/gettemplatedetail` | `POST` | `WECOM_AGENT_SECRET` | `NEEDS_CONFIG` | 需要审批模板 ID;缺模板不得进入镜像实现。 |
| 审批申请详情 | [获取审批申请详情](https://developer.work.weixin.qq.com/document/path/91983) | `/cgi-bin/oa/getapprovaldetail` | `POST` | `WECOM_AGENT_SECRET` | `NEEDS_CONFIG` | 需要测试审批单号 `sp_no`;不上传采购、合同、报销、发票或银行回单做测试附件。 |
| 审批状态回调 | [审批申请状态变化回调通知](https://developer.work.weixin.qq.com/document/path/91815) | 事件回调 | `POST` 事件 | `WECOM_AGENT_SECRET` 所属应用 | `NEEDS_CONFIG` | 需要真实回调 URL、Token、EncodingAESKey、验签、解密、幂等和重试设计。 |
| 批量获取审批单号 | [批量获取审批单号](https://developer.work.weixin.qq.com/document/path/91816) | 官方审批单查询接口 | `POST` | `WECOM_AGENT_SECRET` | `NEEDS_API_POC` | 可作为补偿拉取入口,后续镜像合同中评估。 |
| 提交审批申请 | [提交审批申请](https://developer.work.weixin.qq.com/document/path/91853) | 官方提交审批接口 | `POST` | `WECOM_AGENT_SECRET` | `NOT_IN_CURRENT_SCOPE_WRITE` | 本项目当前不把 OA 主办报销、采购、合同、项目迁到企微原生审批。 |
| 假期余额 | [获取成员假期余额](https://developer.work.weixin.qq.com/document/path/93376) | `/cgi-bin/oa/vacation/getuservacationquota` | `POST` | `WECOM_AGENT_SECRET` | `CAN_REUSE_NOW_OR_NEEDS_PERMISSION` | WCPC 已证明可读但历史有权限错误;后续作为考勤镜像的只读项。 |
| 审批模板创建/更新 | [创建审批模板](https://developer.work.weixin.qq.com/document/path/97437)、[更新审批模板](https://developer.work.weixin.qq.com/document/path/97438) | 官方模板写接口 | `POST` | `WECOM_AGENT_SECRET` | `RULEOPS_LATER_WRITE` | 只允许 RuleOps 后期在人工确认、前后快照和回滚建议齐全时写入。 |
## 4. 运行优先结论
- 近期优先利用:企业微信工作台入口、OAuth/免登、绑定、个人应用消息、OA deeplink、通讯录 userid 清单、可选成员详情、人事助手花名册读取、打卡记录和打卡规则读取。
- 暂缓深度镜像:`wecom_attendance_approval_mirror_v1`、`oa_hr_emergency_mode_v1`、`wecom_recovery_writeback_v1`、`wecom_ruleops_v1` 均等 WOUA 复跑、审批模板 ID、测试 `sp_no` 和真实回调配置明确后再拆。
- OA 主办边界不变:报销、采购、费用、合同、项目、付款批次、AP、代账交接、审计和高敏附件继续在 OA 内完成,只向企业微信发送脱敏提醒和 OA 链接。
## 5. 后续使用要求
- 新增企微能力前,先在本文补一行能力记录,再写 Issue CSV。
- POC 输出统一使用 `CAN_REUSE_NOW`、`NEEDS_CONFIG`、`NEEDS_API_POC`、`KEEP_OA_PRIMARY`、`NOT_SUPPORTED_BY_API`、`SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
- 公网验证报告只允许输出脱敏 userid 前缀、计数、错误码、token source 和结论,不得输出 Secret、token、完整 userid、手机号、邮箱或原始 payload。
+17 -13
查看文件
@@ -2,10 +2,10 @@
**文档状态**:当前开发路线图事实源。
**分类**:开发治理 / 路线图。
**适用时间**:WDRG / WCP1 / WHMSLIVE 之后。
**关联 Issue**:`WDRG,WCP1,WHMS,WHMSLIVE`。
**适用时间**:WDRG / WCP1 / WHMSLIVE / WOUA / WAPI 之后。
**关联 Issue**:`WDRG,WCP1,WHMS,WHMSLIVE,WOUA,WAPI`。
**最近审计**:2026-05-18。
**不得误读**:本文只控制后续开发节奏、阶段顺序和门禁。WHMSLIVE 后只代表企微 HR 主数据镜像已部署并受控验证;不代表企微考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps 已完成运行时实现。
**不得误读**:本文只控制后续开发节奏、阶段顺序和门禁。WHMSLIVE 后只代表企微 HR 主数据镜像已部署并受控验证;WAPI 后进一步固定企微 API/Secret 口径并转为运行优先,不代表企微考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps 已完成运行时实现。
## 1. 路线图原则
@@ -14,6 +14,7 @@
- 采购、费用、合同、项目、报销、付款批次、AP、代账交接、审计和高敏附件继续 OA 主办;企业微信只做脱敏消息、deeplink、入口和成熟能力参考。
- 阶段未满足退出条件前不得跳阶段。线上缺陷、安全风险、正式使用阻断或真实反馈可以另开独立 Issue CSV,但不得混入企微 HR/考勤主路线。
- ERP 未来不接入 OA;真实支付、图片 OCR、银行流水导入、复杂 BPM、审批设计器和重预算继续不进入当前路线。
- WAPI 后所有企微开发必须先读 [企微 API 能力目录](./企微API能力目录.md),先确认官方 API、Secret、权限、可信 IP、回调/模板和读写边界;运行时代码不得靠多个 Secret 试错。
## 2. 当前阶段
@@ -25,6 +26,8 @@
| `WCP1` | 已完成 | 已完成只读 POC runner 和远端只读探测,历史结论为 `PAUSE_FOR_CONFIG`;后续由 WCPC 复跑收口。 |
| `WCPC` | 已完成 | 已使用既有在职低风险测试号 `SMOKE002/测试员工`(报告只记录 `oa_smoke***`)完成远端只读复跑,结论 `GO_TO_HR_MASTER_SYNC`;该准入已由 WHMS 承接,不得直接跳到考勤/审批镜像、应急、回写或 RuleOps。 |
| `WHMSLIVE` | 已部署并受控公网验证 | 已部署 `wecom_hr_master_sync_v1`,应用 migration,公网对 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply;因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,未写 OA 主数据;普通员工权限负例 403;最终 `force_off`,未执行生产真实员工批量 apply。 |
| `WOUA` | 已执行并由 WAPI 新口径复跑 | 初始报告因旧读取路径阻断;WAPI 后远端只读复跑为 `PASS_WITH_FINDINGS`:OA active 12、企微 active 5、匹配 5、OA active 缺企微 7、已归档仍有绑定 1、缺必填字段 5。该结论明确当前 OA/企微用户不一致,也不批准真实员工批量 apply。 |
| `WAPI` | 已固化运行优先门禁 | 新增本地企微 API 能力目录和 Secret 矩阵:通讯录使用 `WECOM_CONTACT_SECRET`,人事助手/打卡/消息/OAuth 使用 `WECOM_AGENT_SECRET`;WOUA/WHMS 读取改为 `user/list_id` + 可选成员详情 + 花名册读取;后续先利用入口、通知、通讯录 userid、人事助手读取和打卡读取。 |
## 3. 阶段路线
@@ -32,17 +35,18 @@
| --- | --- | --- | --- | --- |
| 1 | `WECOM-CAPABILITY-POC-V1` | 只读或低风险探测企微 API 能力,输出能力矩阵。 | WDRG 完成;企微后台权限、Secret、可信 IP、模板和可见范围按现状记录。 | 人事助手、通讯录、打卡、审批、假期余额、回调和权限配置均有 `CAN_REUSE_NOW`、`NEEDS_CONFIG`、`NEEDS_API_POC`、`KEEP_OA_PRIMARY` 或 `NOT_SUPPORTED_BY_API` 结论。 |
| 2 | `wecom_hr_master_sync_v1` | 企微/人事助手员工、部门、主管、职位、联系方式和在职状态镜像到 OA。 | POC 证明人事助手/通讯录读取和回调能力可用,或明确缺口处理方式。 | WHMSLIVE 后已完成本地门禁、部署和受控公网验证;OA 可自动创建/更新员工档案、账号、角色、企微绑定和审计;最终 `force_off`,真实员工批量 apply 仍需单独批准;缺配置时记录 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。 |
| 3 | `wecom_attendance_approval_mirror_v1` | 企微打卡、请假、补卡、外出、加班、假期余额和审批历史镜像到 OA。 | WHMS 部署并完成受控 dry-run/apply 验证;审批模板、打卡规则和回调能力已完成 POC 或有可解释替代方案。 | OA 可查询企微 HR/考勤镜像、附件摘要、审批历史、考勤解释和审计;不把 OA 主办高敏审批迁到企微。 |
| 4 | `oa_hr_emergency_mode_v1` | 企业微信不可用时,由管理员人工开启 OA HR/考勤应急接管。 | 企微镜像数据可支撑对账;OA 应急入口、权限和审计方案明确。 | 管理员可填写原因、范围、预计结束时间和责任人后开启/关闭;应急单据带 `EMERGENCY` 标记。 |
| 5 | `wecom_recovery_writeback_v1` | 企微恢复后,OA 应急数据先 dry-run,再回写企微。 | 应急接管模式完成;企微写回接口、冲突规则和回滚策略已单独批准。 | 系统生成成功、失败、跳过和冲突清单;无冲突项可自动回写,冲突进入人工确认。 |
| 6 | `wecom_ruleops_v1` | AI 草拟企微规则、生成差异报告和 dry-run,人工确认后写入企微。 | 前述同步/回写链路稳定;企微规则 API 能力和权限已验证。 | 每次写入保存前后快照、确认人、API `errcode`、失败原因、回滚建议和审计记录。 |
| 3 | `runtime_first_wecom_enablement` | 先把已可用企微能力用于系统运行:入口、免登/绑定、脱敏消息、deeplink、通讯录 userid 清单、人事助手读取、打卡读取和只读对账。 | WAPI API 目录和 Secret 矩阵完成;WOUA 按新读取策略复跑。 | WOUA 结论为 `PASS_NO_DRIFT`、`PASS_WITH_FINDINGS` 或可解释阻断;后续按差异决定 WOUA-FIX、WHMS 批量同步窗口或进入考勤/审批镜像准备。 |
| 4 | `wecom_attendance_approval_mirror_v1` | 企微打卡、请假、补卡、外出、加班、假期余额和审批历史镜像到 OA。 | 运行优先门禁完成;审批模板、测试审批单号 `sp_no`、真实回调配置、打卡规则和只读 POC 均明确。 | OA 可查询企微 HR/考勤镜像、附件摘要、审批历史、考勤解释和审计;不把 OA 主办高敏审批迁到企微。 |
| 5 | `oa_hr_emergency_mode_v1` | 企业微信不可用时,由管理员人工开启 OA HR/考勤应急接管。 | 企微镜像数据可支撑对账;OA 应急入口、权限和审计方案明确。 | 管理员可填写原因、范围、预计结束时间和责任人后开启/关闭;应急单据带 `EMERGENCY` 标记。 |
| 6 | `wecom_recovery_writeback_v1` | 企微恢复后,OA 应急数据先 dry-run,再回写企微。 | 应急接管模式完成;企微写回接口、冲突规则和回滚策略已单独批准。 | 系统生成成功、失败、跳过和冲突清单;无冲突项可自动回写,冲突进入人工确认。 |
| 7 | `wecom_ruleops_v1` | AI 草拟企微规则、生成差异报告和 dry-run,人工确认后写入企微。 | 前述同步/回写链路稳定;企微规则 API 能力和权限已验证。 | 每次写入保存前后快照、确认人、API `errcode`、失败原因、回滚建议和审计记录。 |
## 4. 每阶段固定门禁
每个正式阶段必须单独建立 Issue CSV,并至少包含:
- 需求边界:本阶段做什么、不做什么、涉及哪些角色和业务流程。
- 接口边界:新增或复用的企微 API、OA API、schema、feature flag、配置和权限。
- 接口边界:新增或复用的企微 API、OA API、schema、feature flag、配置和权限;企微接口必须引用 [企微 API 能力目录](./企微API能力目录.md) 的对应能力行。
- 数据边界:主事实源、镜像方向、附件归属、脱敏字段和审计责任。
- 失败处理:缺 Secret、权限、模板、可信 IP、回调、外部配置时必须记录 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`,不得伪造成成功。
- 测试门禁:本地静态检查、focused 单测、必要 Playwright、权限负例、文档审计和公网复检。
@@ -52,7 +56,7 @@
- 可以并行:线上缺陷修复、首周运营保障、真实反馈扫尾、QA-only 脚本维护、文档审计修正。
- 必须另开合同:OA 主办域的采购、合同、报销、付款、AP、代账、审计和高敏附件功能变更。
- 不允许混入:在 POC 未完成前开发 HR 主数据镜像;在 HR 主数据未稳定前开发恢复回写;在应急接管未完成前开发 RuleOps 正式写入。
- 不允许混入:在 API 目录和 Secret 矩阵未明确前开发新企微能力;在 WOUA 复跑或可解释阻断未收口前推进深度考勤/审批镜像;在 HR 主数据未稳定前开发恢复回写;在应急接管未完成前开发 RuleOps 正式写入。
- 不允许把企业微信原生审批用于报销、采购、合同、项目、财务付款或高敏附件审批。
## 6. 下一轮 POC 验收口径
@@ -77,13 +81,13 @@ POC 默认只读或低风险探测。任何生产写操作、企微规则写入
- 工作台应用 access token 可获取,核心 CorpID/Agent/Secret/OAuth 配置存在。
- HCLT 已作为历史证据纳入:`HRWDTEST-20260517214548` 已完成企微通讯录 create/update/disable,`errcode=0`。
- 通讯录部门读取通过工作台应用 token fallback 可用。
- 通讯录部门读取曾通过工作台应用 token fallback 验证;该历史口径已被 WAPI supersede,后续通讯录读取固定使用 `WECOM_CONTACT_SECRET`,部门列表和成员详情只作可选富化,`user/list_id` 缺权限才阻断全量候选读取。
- 人事助手字段配置读取可用。
- 审批 access token 可获取,但模板、实例、假期余额和回调配置尚未满足镜像开发准入。
当前阻断:
- `contact.member_read`:测试成员读取返回 `wecom_errcode_48009`,需要核对通讯录读取权限、应用可见范围或测试成员状态。
- `contact.member_read`:测试成员详情读取历史返回 `wecom_errcode_48009`;WAPI 后该接口降级为可选富化,WOUA/WHMS 必需链路改为 `user/list_id` + 人事助手花名册。
- `hr.staff_read`:测试成员花名册读取返回 `wecom_errcode_60021`,需要把可用于 POC 的测试成员纳入应用/人事助手权限范围,或改用在职测试成员复跑。
- `checkin.records_read` / `checkin.rules_read`:远端 `WECOM_CHECKIN_SYNC_ENABLED` 未开启,打卡短窗口和规则读取按 `NEEDS_CONFIG` 记录。
- `approval.template_detail` / `approval.instance_detail`:缺审批模板 ID 和测试审批单号。
@@ -120,7 +124,7 @@ WCPC 已允许进入 `wecom_hr_master_sync_v1`。WHMS 已承接该阶段,完
已实现内容:
- 新增 feature flag `wecom_hr_master_sync_v1`,默认 `force_off`;新增配置 `WECOM_HR_MASTER_SYNC_ENABLED`、`WECOM_HR_ASSISTANT_SECRET`,允许已验证工作台应用 Secret 作为只读 fallback 并记录 token source。
- 新增 feature flag `wecom_hr_master_sync_v1`,默认 `force_off`;WAPI 后读取口径修正为通讯录使用 `WECOM_CONTACT_SECRET`、人事助手/花名册使用 `WECOM_AGENT_SECRET`,`WECOM_HR_ASSISTANT_SECRET` 仅保留为历史兼容项且不参与默认读取。
- 新增管理 API:`POST /api/employees/wecom-hr-master-sync/runs`、`GET /api/employees/wecom-hr-master-sync/runs`、`GET /api/employees/wecom-hr-master-sync/runs/:id`。
- 新增同步批次和逐员工记录,支持 `dry_run` 不写主数据、`apply` 写入 OA 员工、部门映射、默认 `employee` 角色、企业微信绑定、主管关系和审计。
- People/人员管理页新增“企微 HR 主数据镜像”管理卡片,管理员可执行预检、确认写入并查看最近同步摘要;普通员工不可见入口。
@@ -139,4 +143,4 @@ WCPC 已允许进入 `wecom_hr_master_sync_v1`。WHMS 已承接该阶段,完
- 公网验证窗口中临时将 `wecom_hr_master_sync_v1` 设为 `force_on`,对既有低风险测试号 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply;企微读取链路通过,因企微侧缺手机号、邮箱和部门,按规则记录 `missing_required_field` 并跳过,未写入 OA 主数据。
- 已验证普通员工访问同步 API 返回 403;验证结束后 `wecom_hr_master_sync_v1` 已恢复为 `force_off`。
- 未执行生产真实员工批量 `apply`;后续若要正式批量同步真实员工,必须另行批准并先处理企微必填字段质量。
- 下一阶段允许规划 `wecom_attendance_approval_mirror_v1`,但必须另建合同,不能直接跳到应急接管、恢复回写或 RuleOps。
- 下一步先处理 WOUA 用户一致性差异或明确 WHMS 批量同步窗口;`wecom_attendance_approval_mirror_v1` 暂缓到审批模板、测试审批单号、真实回调配置和打卡细项 POC 明确后再另建合同。
+15 -12
查看文件
@@ -69,7 +69,8 @@
| `WCPC` | `issues/2026-05-18_15-01-23-wecom-capability-poc-config-closeout-v1.csv` | `EVIDENCE` | WCP1 配置收口已用既有在职测试号复跑通过,结论 `GO_TO_HR_MASTER_SYNC`,已由 WHMS 承接 |
| `WHMS` | `issues/2026-05-18_18-43-19-wecom-hr-master-sync-v1.csv` | `CURRENT` | 企微 HR 主数据镜像已实现:`wecom_hr_master_sync_v1`、同步批次/逐员工记录、dry-run/apply、管理员 API、People 管理入口和 QA-only runner;默认 `force_off`,不代表考勤/审批镜像已完成 |
| `WHMSLIVE` | `issues/2026-05-18_19-28-25-whms-deploy-live-verify-v1.csv` | `EVIDENCE` | WHMS 已部署并完成受控公网验证:migration 已应用,`SMOKE002` dry-run/apply 管道通过但因缺手机号、邮箱和部门按 `missing_required_field` 跳过,普通员工权限负例 403,最终 `wecom_hr_master_sync_v1=force_off`,未执行生产真实员工批量 apply |
| `WOUA` | `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv` | `EVIDENCE` | OA/企微用户一致性只读审计已部署执行:OA 侧只读计数 13/12/5,企微全量读取因 `wecom_errcode_60011` 阻断,结论 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`,未修改 OA/企微用户,未执行真实员工批量 apply |
| `WOUA` | `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv` | `EVIDENCE` | OA/企微用户一致性只读审计已由 WAPI 新口径复跑:结论 `PASS_WITH_FINDINGS`,OA active 12、企微 active 5、匹配 5、OA active 缺企微 7、已归档仍有绑定 1、缺必填字段 5;未修改 OA/企微用户,未执行真实员工批量 apply |
| `WAPI` | `issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv` | `CURRENT` | 企微 API 能力目录与运行优先纠偏已收口:固定官方 API 目录、Secret 矩阵、WHMS/WOUA 读取策略,成员详情降级为可选富化,公网 WOUA 复跑 `PASS_WITH_FINDINGS`;不执行真实员工批量 apply |
旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。
@@ -77,27 +78,28 @@
| Doc_ID | Path | Category | Authority | Status | Owner | Related_Issues | Evidence | Supersedes | Last_Audited | Audit_Method | Risk | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE,FIN-PROD-ENABLE,ACCOUNTING-HANDOFF-V1,SYSTEM-COMPLETE-GATE,LAUNCH-OPS-V1,REQ-COMPLETE-AUDIT,ATTV1,LAUNCH-AUTO-OPS-V1,AUTO-REMAINING-CLOSEOUT,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA` | `rg` | none | 2026-05-18 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么 |
| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE,FIN-PROD-ENABLE,ACCOUNTING-HANDOFF-V1,SYSTEM-COMPLETE-GATE,LAUNCH-OPS-V1,REQ-COMPLETE-AUDIT,ATTV1,LAUNCH-AUTO-OPS-V1,AUTO-REMAINING-CLOSEOUT,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA` | `audit-docs` | none | 2026-05-18 | `audit-docs --check-registry` | medium | 判断文档权威性 |
| `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE` | Issue CSV | `docs/requirements-specification.md` | 2026-05-18 | `rg` | high | WHMS 后定义 HR 主数据镜像已部署验证和下一阶段边界;ERP 未来不接入 OA |
| `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE` | `rg` | none | 2026-05-18 | `rg` | high | 记录已拍板产品边界、企微/OA 设计门禁、开发路线图、WCPC 准入结论和 WHMS 部署验证状态 |
| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE,FIN-PROD-ENABLE,ACCOUNTING-HANDOFF-V1,SYSTEM-COMPLETE-GATE,LAUNCH-OPS-V1,REQ-COMPLETE-AUDIT,ATTV1,LAUNCH-AUTO-OPS-V1,AUTO-REMAINING-CLOSEOUT,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI` | `rg` | none | 2026-05-18 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么 |
| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE,FIN-PROD-ENABLE,ACCOUNTING-HANDOFF-V1,SYSTEM-COMPLETE-GATE,LAUNCH-OPS-V1,REQ-COMPLETE-AUDIT,ATTV1,LAUNCH-AUTO-OPS-V1,AUTO-REMAINING-CLOSEOUT,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI` | `audit-docs` | none | 2026-05-18 | `audit-docs --check-registry` | medium | 判断文档权威性 |
| `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WAPI` | Issue CSV | `docs/requirements-specification.md` | 2026-05-18 | `rg` | high | WAPI 后定义 HR 主数据镜像读取口径、运行优先和下一阶段边界;ERP 未来不接入 OA |
| `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WAPI` | `rg` | none | 2026-05-18 | `rg` | high | 记录已拍板产品边界、企微/OA 设计门禁、开发路线图、WCPC 准入结论、WHMS 部署验证和 WAPI 运行优先口径 |
| `DESIGN-UI` | `docs/产品设计规则.md` | 设计事实源 | PC/移动/企业微信设计事实源 | `CURRENT` | design/frontend | `MIA7,MIF7,GOV8,DG8,DG9,WFDG` | Playwright/视觉审查/DG8/DG9/WFDG | none | 2026-05-18 | `rg` | high | 页面改动必须读取;DG8 后飞书为主 UI/布局参考;DG9 后按页面分层和样板选型;WFDG 后区分 OA 主办、企微镜像和 OA 应急接管 |
| `ROADMAP-WECOM` | `docs/企微优先开发路线图.md` | 开发治理 | 企微优先开发路线图事实源 | `CURRENT` | product/dev | `WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA` | Issue CSV | none | 2026-05-18 | `rg` | high | WOUA 后记录企微 HR/考勤路线阶段顺序、HR 主数据镜像部署验证和用户一致性只读审计阻断边界 |
| `ROADMAP-WECOM` | `docs/企微优先开发路线图.md` | 开发治理 | 企微优先开发路线图事实源 | `CURRENT` | product/dev | `WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI` | Issue CSV | none | 2026-05-18 | `rg` | high | WAPI 后记录企微 HR/考勤路线阶段顺序、API/Secret 口径、运行优先阶段和用户一致性只读审计复跑前置 |
| `WECOM-API-CATALOG` | `docs/企微API能力目录.md` | 集成事实源 | 企微 API 能力目录和 Secret 矩阵事实源 | `CURRENT` | product/backend/qa | `WAPI` | 官方企业微信开发者中心链接/Issue CSV | none | 2026-05-18 | `rg` | high | 企微开发先读;只保存摘要、链接和项目决策,不保存 Secret、token、完整 userid、手机号、邮箱或原始 payload |
| `DELIVERY` | `docs/开发交付与变更管理规约.md` | 开发治理 | 开发交付事实源 | `CURRENT` | dev/qa | `GOV8,WDRG` | `rg` | none | 2026-05-18 | `rg` | high | Issue、review、commit、交付规则;WDRG 后采用路线图门禁 + 单功能 Issue CSV |
| `QA-GATE` | `docs/测试规约与回归门禁.md` | 测试验收 | 测试和回归门禁事实源 | `CURRENT` | qa | `QAF8,GOV8` | 回归脚本/Playwright | none | 2026-05-13 | `rg` | high | 测试计划和 DONE 门禁 |
| `AGENT-PRACTICE` | `docs/Agent开发交付实践.md` | Agent 治理 | Agent 协作事实源 | `CURRENT` | dev | `GOV8,DG8` | Skill 校验 | none | 2026-05-13 | `rg` | medium | Agent 协作开发实践和设计主参考层级 |
| `AGENTS` | `AGENTS.md` | 执行硬约束 | 项目硬规则 | `CURRENT` | lead | `GOV8` | 人工核对 | none | 2026-05-13 | `rg` | high | 不可违反约束 |
| `SKILL-HYB` | `.agents/skills/hyb-oa-delivery/SKILL.md` | Skill | 项目交付 Skill | `CURRENT` | dev | `GOV8,DG8,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE` | quick_validate | none | 2026-05-18 | `quick_validate.py` | medium | 每轮交付操作入口;企微/OA 后续先读路线图和 WHMSLIVE 状态,不得跳过单功能合同直接做考勤审批镜像 |
| `SKILL-HYB` | `.agents/skills/hyb-oa-delivery/SKILL.md` | Skill | 项目交付 Skill | `CURRENT` | dev | `GOV8,DG8,WFDG,WDRG,WCP1,WCPC,WHMS,WHMSLIVE,WAPI` | quick_validate | none | 2026-05-18 | `quick_validate.py` | medium | 每轮交付操作入口;企微/OA 后续先读 API 能力目录、Secret 矩阵、路线图和 WHMSLIVE/WAPI 状态,不得跳过单功能合同直接做考勤审批镜像 |
| `REQ-WF` | `docs/需求-统一审批中心规约.md` | 需求事实源 | 审批模块规约 | `CURRENT` | product/backend | `AP6,WF8,WFOC,DBWFOC,WFDG` | workflow 回归 | none | 2026-05-18 | `rg` | high | OA 主办审批、企微镜像和 OA 应急接管边界 |
| `REQ-OCR` | `docs/需求-报销发票OCR规约.md` | 需求事实源 | OCR 模块规约 | `CURRENT` | product/backend | `RI6,OCR8,BIZ8` | OCR 回归 | none | 2026-05-13 | `rg` | high | OCR 和发票边界 |
| `RULE-REIMB` | `docs/报销发票附件规则.md` | 需求事实源 | 报销发票附件规则 | `CURRENT` | product/finance | `RI6,BIZ8` | 报销回归 | none | 2026-05-13 | `rg` | high | 报销、发票、附件细则 |
| `REQ-WECOM` | `docs/需求-企业微信基础设施规约.md` | 需求事实源 | 企业微信边界规约 | `CURRENT` | product/backend | `R7E,ORG8,WF8,ATTV1,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA` | live smoke/真机反馈/Issue CSV | none | 2026-05-18 | `rg` | high | 企微优先承担 HR/考勤员工日常审批和人事/打卡能力;WOUA 记录 OA/企微用户一致性只读审计被企微读取权限阻断;采购、合同、项目、报销和财务继续 OA 主办 |
| `REQ-WECOM` | `docs/需求-企业微信基础设施规约.md` | 需求事实源 | 企业微信边界规约 | `CURRENT` | product/backend | `R7E,ORG8,WF8,ATTV1,HR-WECOM-DIRECTORY-V1,WFOC,DBWFOC,WFDG,WCP1,WCPC,WHMS,WHMSLIVE,WOUA,WAPI` | live smoke/真机反馈/Issue CSV | none | 2026-05-18 | `rg` | high | 企微优先承担 HR/考勤员工日常审批和人事/打卡能力;WAPI 固定 API/Secret 口径和运行优先;采购、合同、项目、报销和财务继续 OA 主办 |
| `RUNBOOK-TRIAL` | `docs/试运行与自动化验证手册.md` | 操作手册 | 试运行验证手册 | `CURRENT` | qa/ops | `R7E,QAOPS8` | live readiness | none | 2026-05-13 | `rg` | medium | 本地、公网、企业微信和真实 OCR 验证 |
| `MANUAL-ADMIN` | `docs/管理员手册.md` | 操作手册 | 管理员手册 | `CURRENT` | ops/admin | `ORG8,R7E,WFOC,DBWFOC,WFDG,WCPC,WHMS,WHMSLIVE` | 人工验收 | none | 2026-05-18 | `rg` | medium | 管理员上线、企微优先边界、OA 主办数据边界、HR 主数据镜像、OA 应急接管、恢复回写和 POC 前置 |
| `MANUAL-ADMIN` | `docs/管理员手册.md` | 操作手册 | 管理员手册 | `CURRENT` | ops/admin | `ORG8,R7E,WFOC,DBWFOC,WFDG,WCPC,WHMS,WHMSLIVE,WAPI` | 人工验收 | none | 2026-05-18 | `rg` | medium | 管理员上线、企微优先边界、Secret 矩阵、OA 主办数据边界、HR 主数据镜像、OA 应急接管、恢复回写和 POC 前置 |
| `MANUAL-EMPLOYEE` | `docs/员工快速上手.md` | 操作手册 | 员工手册 | `CURRENT` | product | `R7E,WFOC,DBWFOC,WFDG` | 人工验收 | none | 2026-05-18 | `rg` | low | 员工侧使用说明,HR/考勤优先企业微信,采购/合同/财务仍在 OA;区分企微镜像和 OA 应急接管 |
| `R7-DRAFT` | `docs/trialrun/R7扩大试运行草案.md` | 草案 | R7 准备材料 | `DRAFT` | product/qa | `R7E` | R7EP 报告 | none | 2026-05-13 | `rg` | medium | 不代表当前 R7E 执行状态 |
| `OPS-RUNBOOK` | `docs/trialrun/OPS7运维交接Runbook.md` | 运维草案 | OPS 准备材料 | `DRAFT` | ops | `OPS8,QAOPS8` | 只读检查 | none | 2026-05-13 | `rg` | medium | 不代表恢复演练已执行 |
| `TRIAL-REPORT` | `docs/trialrun/人工试运行报告.md` | 证据材料 | 试运行证据入口 | `EVIDENCE` | qa | `R7EP,OPS8,QAF8,WOUA` | artifacts/qa-flow | none | 2026-05-18 | `rg` | medium | 只证明发生过;WOUA 记录 OA/企微用户一致性只读审计阻断,不直接派工 |
| `TRIAL-REPORT` | `docs/trialrun/人工试运行报告.md` | 证据材料 | 试运行证据入口 | `EVIDENCE` | qa | `R7EP,OPS8,QAF8,WOUA,WAPI` | artifacts/qa-flow | none | 2026-05-18 | `rg` | medium | 只证明发生过;WAPI 记录 OA/企微用户一致性只读审计复跑差异,不直接派工 |
| `FBA-AUDIT` | `docs/trialrun/R7E后功能边界审计.md` | 证据材料 | R7E 后功能边界审计 | `EVIDENCE` | product/dev/qa | `FBA,R7E,QAOPS8,P2T` | Issue CSV | none | 2026-05-15 | `rg` | medium | 只作为功能边界、P2 候选和 Issue 草案证据,不直接派工 |
| `HISTORY-MATRIX` | `docs/历史功能状态矩阵.md` | 历史材料 | 历史功能索引 | `HISTORICAL` | product | `DCL7` | git 历史 | none | 2026-05-13 | `rg` | low | 只追溯历史 |
| `OLD-IMPL-PLAN` | `docs/implementation_plan.md` | 已移除历史 | 早期实施计划 | `REMOVED` | product | `DCL7` | git history | none | 2026-05-13 | manual | low | 从 git 历史恢复 |
@@ -110,7 +112,8 @@
| `docs/文档事实源状态矩阵.md` | 当前事实源 | 文档分类和恢复规则 |
| `docs/海音达OA-MVP需求规格说明书.md` | 当前事实源 | PRD 主事实源 |
| `docs/产品设计规则.md` | 当前事实源 | PC、移动、企业微信设计规则;DG8 主 UI/布局参考层级 |
| `docs/企微优先开发路线图.md` | 当前事实源 | WDRG/WCP1/WCPC/WHMS/WHMSLIVE/WOUA 后企微优先 HR/考勤改造阶段顺序、准入/退出门禁、禁止跳阶段和当前用户一致性只读审计阻断状态 |
| `docs/企微优先开发路线图.md` | 当前事实源 | WDRG/WCP1/WCPC/WHMS/WHMSLIVE/WOUA/WAPI 后企微优先 HR/考勤改造阶段顺序、API/Secret 口径、运行优先和禁止跳阶段 |
| `docs/企微API能力目录.md` | 当前事实源 | WAPI 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界和运行优先决策 |
| `docs/产品口径.md` | 当前事实源 | 产品边界和业务规则 |
| `docs/需求-统一审批中心规约.md` | 当前事实源 | 审批、催办、超时、效率摘要 |
| `docs/报销发票附件规则.md` | 当前事实源 | 报销、发票、附件细则 |
@@ -185,4 +188,4 @@
- `ME6-3` 代表性企业微信真机门禁已关闭。
- MIF7-1..7 与 MIF7T 已完成本地整改、公网部署、公网匿名复检和带凭据 live smoke/OCR 复检。
- R7E 真实扩大试运行阶段验收、P2 排序、R7E 收口、QAOPS8 上线硬化阶段收口、QAOPS9 上线硬化连续合同、PRELAUNCH 正式启用前公网预回归、CASHIER-VOUCHER-V1 财务闭环与银行回单附件、LAUNCH-DEPLOY-FINANCE 部署复检、FIN-PROD-ENABLE 正式常开、ACCOUNTING-HANDOFF-V1 代账交接 SOP、SYSTEM-COMPLETE-GATE 系统完成门禁、LAUNCH-OPS-V1 首周运营保障、REQ-COMPLETE-AUDIT 需求完成度复核、ATTV1 考勤统一入口、AUTO-REMAINING-CLOSEOUT 剩余自动化收口、HRWD-CONTACT-LIVE-TEST 公网企微通讯录真实写入闭环、WFOC 需求重构、DBWFOC 数据边界修正、WFDG 设计门禁、WDRG 路线图门禁和 WCPC 配置复跑均已完成,当前 P0/P1 阻断为 0;P2D 已完成决策分流,P2-ORG 只读巡检已完成且无需写修复合同。LAUNCH-AUTO-OPS-V1 发现的公网 `payment_batch_v1=force_off` 漂移已由 AUTOREM 恢复为 `force_on`,正式自动巡检结论为 `GO_TO_OPERATE`;`erp_sync=force_off`,ERP 未来不接入 OA,正式支付接口、图片 OCR 和银行流水导入继续不进入当前路线。WCP1 已完成企业微信能力 QA-only 只读 POC,历史结论为 `PAUSE_FOR_CONFIG`;WCPC 使用既有在职测试号 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)完成远端只读复跑,报告 `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`,结论 `GO_TO_HR_MASTER_SYNC`,`nextStageAllowed=true`,`blockingCapabilities=[]`。WHMS 已实现并部署验证 `wecom_hr_master_sync_v1`、同步批次/逐员工记录、dry-run/apply、管理员 API、People 管理入口和 QA-only runner;默认和最终均为 `force_off`,公网 `SMOKE002` 因缺手机号、邮箱和部门按 `missing_required_field` 跳过,未执行生产真实员工批量 apply。WOUA 已执行 OA/企微用户一致性只读审计,OA 侧只读计数为 13/12/5,企微全量读取因 `wecom_errcode_60011` 阻断,结论 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`;该结果不代表 OA 和企微用户已一致,也不批准真实员工批量 apply。HR/考勤员工日常审批优先复用企业微信,OA 目标是按域镜像、人工接管、恢复回写和 RuleOps;采购、费用、合同、项目、报销、付款、AP、代账交接、审计和高敏附件继续 OA 主办。考勤/审批镜像、审批回调、OA 应急接管、恢复回写和 RuleOps 尚未实现;审批模板 ID、审批单号 `sp_no`、真实回调配置、HR 写接口和企微用户一致性读权限仍需后续独立合同处理。后续仍须按 [企微优先开发路线图](./企微优先开发路线图.md) 在 WHMSLIVE/WOUA 之后另拆 `wecom_attendance_approval_mirror_v1`、`oa_hr_emergency_mode_v1`、`wecom_recovery_writeback_v1`、`wecom_ruleops_v1`;阶段未退出前不得跳阶段。HRWD 已完成 OA 驱动企业微信通讯录同步,并通过 `HRWDTEST-20260517214548` 真实公网 create/update/disable 验证;测试成员按审计要求保留离职/禁用状态。
- R7E 真实扩大试运行阶段验收、P2 排序、R7E 收口、QAOPS8 上线硬化阶段收口、QAOPS9 上线硬化连续合同、PRELAUNCH 正式启用前公网预回归、CASHIER-VOUCHER-V1 财务闭环与银行回单附件、LAUNCH-DEPLOY-FINANCE 部署复检、FIN-PROD-ENABLE 正式常开、ACCOUNTING-HANDOFF-V1 代账交接 SOP、SYSTEM-COMPLETE-GATE 系统完成门禁、LAUNCH-OPS-V1 首周运营保障、REQ-COMPLETE-AUDIT 需求完成度复核、ATTV1 考勤统一入口、AUTO-REMAINING-CLOSEOUT 剩余自动化收口、HRWD-CONTACT-LIVE-TEST 公网企微通讯录真实写入闭环、WFOC 需求重构、DBWFOC 数据边界修正、WFDG 设计门禁、WDRG 路线图门禁和 WCPC 配置复跑均已完成,当前 P0/P1 阻断为 0;P2D 已完成决策分流,P2-ORG 只读巡检已完成且无需写修复合同。LAUNCH-AUTO-OPS-V1 发现的公网 `payment_batch_v1=force_off` 漂移已由 AUTOREM 恢复为 `force_on`,正式自动巡检结论为 `GO_TO_OPERATE`;`erp_sync=force_off`,ERP 未来不接入 OA,正式支付接口、图片 OCR 和银行流水导入继续不进入当前路线。WCP1 已完成企业微信能力 QA-only 只读 POC,历史结论为 `PAUSE_FOR_CONFIG`;WCPC 使用既有在职测试号 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)完成远端只读复跑,报告 `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`,结论 `GO_TO_HR_MASTER_SYNC`,`nextStageAllowed=true`,`blockingCapabilities=[]`。WHMS 已实现并部署验证 `wecom_hr_master_sync_v1`、同步批次/逐员工记录、dry-run/apply、管理员 API、People 管理入口和 QA-only runner;默认和最终均为 `force_off`,公网 `SMOKE002` 因缺手机号、邮箱和部门按 `missing_required_field` 跳过,未执行生产真实员工批量 apply。WAPI 已将企微官方 API 能力目录、Secret 矩阵和运行优先路线落为当前事实源:通讯录读取使用 `WECOM_CONTACT_SECRET` 和 `user/list_id`,成员详情只作可选富化,人事助手/花名册、打卡、消息、OAuth 默认使用 `WECOM_AGENT_SECRET`,`WECOM_CHECKIN_SECRET` 只作专用打卡 override,`WECOM_HR_ASSISTANT_SECRET` 不参与默认读取;后续企微能力不清楚时先查官方 API 和 [企微 API 能力目录](./企微API能力目录.md),不得靠运行时多个 Secret 试错。WAPI 已复跑 WOUA,远端报告 `/app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json` 结论为 `PASS_WITH_FINDINGS`:OA active 12、企微 active 5、匹配 5、OA active 缺企微 7、已归档仍有绑定 1、缺必填字段 5;该结果明确当前 OA 和企微用户不一致,也不批准真实员工批量 apply。HR/考勤员工日常审批优先复用企业微信,OA 目标是按域镜像、人工接管、恢复回写和 RuleOps;采购、费用、合同、项目、报销、付款、AP、代账交接、审计和高敏附件继续 OA 主办。考勤/审批镜像、审批回调、OA 应急接管、恢复回写和 RuleOps 尚未实现;审批模板 ID、审批单号 `sp_no`、真实回调配置和 HR 写接口仍需后续独立合同处理。后续仍须按 [企微优先开发路线图](./企微优先开发路线图.md) 在运行优先门禁之后另拆 `wecom_attendance_approval_mirror_v1`、`oa_hr_emergency_mode_v1`、`wecom_recovery_writeback_v1`、`wecom_ruleops_v1`;阶段未退出前不得跳阶段。HRWD 已完成 OA 驱动企业微信通讯录同步,并通过 `HRWDTEST-20260517214548` 真实公网 create/update/disable 验证;测试成员按审计要求保留离职/禁用状态。
+8 -5
查看文件
@@ -20,7 +20,7 @@
## 最近事实源链
R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> MIF7/MIF7T(公网部署与带凭据公网复检已收口) -> DG8 -> DG9 -> R7E -> FBA -> QAOPS9/P2D -> P2-ORG -> P2-FIN-DECISION -> P2-FIN-V1 -> P2-OCR-SAMPLE -> P2Q -> P2AUTO -> P2AF -> P2-FIN-LIVE-DECISION -> P2-FIN-LIVE -> PRELAUNCH -> CASHIER-VOUCHER-V1 -> LAUNCH-DEPLOY-FINANCE -> FIN-PROD-ENABLE -> ACCOUNTING-HANDOFF-V1 -> SYSTEM-COMPLETE-GATE -> LAUNCH-OPS-V1 -> REQ-COMPLETE-AUDIT -> ATTV1 -> LAUNCH-AUTO-OPS-V1 -> AUTO-REMAINING-CLOSEOUT -> HR-WECOM-DIRECTORY-V1 -> HRWD-CONTACT-LIVE-TEST -> WCFB -> WFOC -> DBWFOC -> WFDG -> WDRG -> WCP1 -> WCPC -> WHMS -> WHMSLIVE -> WOUA。
R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> MIF7/MIF7T(公网部署与带凭据公网复检已收口) -> DG8 -> DG9 -> R7E -> FBA -> QAOPS9/P2D -> P2-ORG -> P2-FIN-DECISION -> P2-FIN-V1 -> P2-OCR-SAMPLE -> P2Q -> P2AUTO -> P2AF -> P2-FIN-LIVE-DECISION -> P2-FIN-LIVE -> PRELAUNCH -> CASHIER-VOUCHER-V1 -> LAUNCH-DEPLOY-FINANCE -> FIN-PROD-ENABLE -> ACCOUNTING-HANDOFF-V1 -> SYSTEM-COMPLETE-GATE -> LAUNCH-OPS-V1 -> REQ-COMPLETE-AUDIT -> ATTV1 -> LAUNCH-AUTO-OPS-V1 -> AUTO-REMAINING-CLOSEOUT -> HR-WECOM-DIRECTORY-V1 -> HRWD-CONTACT-LIVE-TEST -> WCFB -> WFOC -> DBWFOC -> WFDG -> WDRG -> WCP1 -> WCPC -> WHMS -> WHMSLIVE -> WOUA -> WAPI。
- R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。
- ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。
@@ -61,8 +61,9 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
- WDRG 是 WFDG 后的开发路线图门禁合同:新增 [企微优先开发路线图](./企微优先开发路线图.md),固定“路线图门禁 + 单功能 Issue CSV”的开发节奏和阶段顺序;其下一步 `WECOM-CAPABILITY-POC-V1` 已由 WCP1/WCPC 执行并收口,HR 主数据镜像已由 WHMS/WHMSLIVE 实现并部署验证,不得跳过阶段直接开发考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps。
- WCP1 是 WDRG 后的企业微信能力 POC 合同:新增 QA-only 只读 runner `scripts/qa/check-wecom-capability-poc.mjs`,对人事助手、通讯录、打卡、审批、假期余额、回调和权限配置输出脱敏能力矩阵。远端 live 只读历史结论为 `PAUSE_FOR_CONFIG`,作为初始配置缺口证据保留;当前 HR 主数据镜像准入以 WCPC 复跑结论为准。
- WCPC 是 WCP1 后的配置收口合同:已使用既有在职低风险测试号 `SMOKE002/测试员工`(报告只记录 `oa_smoke***`)完成远端只读复跑,报告 `/tmp/wecom-capability-poc-wcpc-smoke002/summary.json`,结论 `GO_TO_HR_MASTER_SYNC`。WHMS 已承接该准入结论;审批模板 ID、审批单号 `sp_no`、真实回调配置和 HR 写接口仍需后续独立合同处理。
- WHMS/WHMSLIVE 是 WCPC 后的企微 HR 主数据镜像合同和部署验证合同:已实现并部署 `wecom_hr_master_sync_v1`、企微/人事助手读取服务、dry-run/apply、同步批次/逐员工记录、People 管理入口和 QA-only runner;公网对 `SMOKE002`(脱敏 `oa_smoke***`)完成 dry-run/apply 管道验证,缺手机号、邮箱和部门按 `missing_required_field` 跳过;权限负例 403;最终 `force_off`,未执行生产真实员工批量 apply。下一阶段可另建 `wecom_attendance_approval_mirror_v1`。
- WOUA 是 WHMSLIVE 后的 OA/企微用户一致性只读审计合同:已新增 QA-only runner `server/src/scripts/audit-wecom-oa-user-consistency.js`,本地静态/单测门禁通过并以 `SYNC_ENV=0` 部署执行;公网报告 `artifacts/qa-flow/wecom-oa-user-consistency-20260518-2055/summary.json` 为 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`,企微读取阻断 `wecom_errcode_60011`,OA 侧脱敏计数为 `oaEmployees=13`、`oaActiveEmployees=12`、`oaActiveWithBinding=5`。本轮未修改 OA/企微用户,未执行真实员工批量 apply,不代表当前已完成一致性核验;进入考勤/审批镜像前需先处理企微读权限或另建 WOUA-FIX/WHMS 批量同步窗口。
- WHMS/WHMSLIVE 是 WCPC 后的企微 HR 主数据镜像合同和部署验证合同:已实现并部署 `wecom_hr_master_sync_v1`、企微/人事助手读取服务、dry-run/apply、同步批次/逐员工记录、People 管理入口和 QA-only runner;公网对 `SMOKE002`(脱敏 `oa_smoke***`)完成 dry-run/apply 管道验证,缺手机号、邮箱和部门按 `missing_required_field` 跳过;权限负例 403;最终 `force_off`,未执行生产真实员工批量 apply。WAPI 后其读取口径被修正为通讯录 `user/list_id` + 人事助手 `get_staff_info`,不再用运行时 Secret fallback。
- WOUA 是 WHMSLIVE 后的 OA/企微用户一致性只读审计合同:已新增 QA-only runner `server/src/scripts/audit-wecom-oa-user-consistency.js`,本地静态/单测门禁通过并以 `SYNC_ENV=0` 部署执行;初始公网报告因旧读取路径为 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`。WAPI 后按新 API/Secret 口径复跑成功,远端脱敏报告 `/app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json` 为 `PASS_WITH_FINDINGS`,计数为 `oaEmployees=13`、`oaActiveEmployees=12`、`oaActiveWithBinding=5`、`wecomProfiles=5`、`matchedActive=5`、`oaActiveMissingInWecom=7`、`inactiveWithBinding=1`、`missingRequiredFields=5`。本轮未修改 OA/企微用户,未执行真实员工批量 apply,不代表 OA/企微用户已经一致。
- WAPI 是 WOUA 后的企微 API 能力目录与运行优先纠偏合同:新增 [企微 API 能力目录](./企微API能力目录.md),固定通讯录用 `WECOM_CONTACT_SECRET`、人事助手/打卡/消息/OAuth 用 `WECOM_AGENT_SECRET`、`WECOM_CHECKIN_SECRET` 仅作显式打卡 override、`WECOM_HR_ASSISTANT_SECRET` 仅历史兼容;后续企微能力不清楚时先查官方 API 和本地目录,不在运行时代码里轮询多个 Secret 试错。WAPI 已把通讯录成员详情降级为可选富化并完成 WOUA 公网复跑;下一步判断 WOUA-FIX、WHMS 批量同步窗口或考勤/审批镜像准备。
## 当前活跃治理流
@@ -112,7 +113,8 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
| `WCPC` | `issues/2026-05-18_15-01-23-wecom-capability-poc-config-closeout-v1.csv` | 已收口:既有测试号 POC 复跑通过 | 使用 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)完成远端只读复跑,结论 `GO_TO_HR_MASTER_SYNC`,已由 WHMS 承接 | 不开发业务功能、不部署、不持久改服务器 env;考勤/审批镜像仍需后续补审批模板、审批单和回调配置 |
| `WHMS` | `issues/2026-05-18_18-43-19-wecom-hr-master-sync-v1.csv` | 已实现:企微 HR 主数据镜像 | 新增 `wecom_hr_master_sync_v1`、Prisma 同步批次/逐员工记录、企微通讯录/人事助手读取、dry-run/apply、管理员 API、People 管理卡片和 QA-only runner | 默认 `force_off`;不做考勤/审批镜像、审批回调、应急接管、恢复回写或 RuleOps |
| `WHMSLIVE` | `issues/2026-05-18_19-28-25-whms-deploy-live-verify-v1.csv` | 已部署验证:企微 HR 主数据镜像公网受控验证 | 部署 WHMS、应用 migration、公网 dry-run/apply 管道验证、普通员工权限负例、最终 `wecom_hr_master_sync_v1=force_off` | `SMOKE002` 缺手机号、邮箱和部门按 `missing_required_field` 跳过,未写 OA 主数据;未执行生产真实员工批量 apply |
| `WOUA` | `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv` | 已收口:OA/企微用户一致性只读对账外部配置阻断 | OA active 员工 vs 企业微信通讯录 + 人事助手只读审计、脱敏差异报告、本地单测、受控部署和公网执行 | 公网结论 `SKIP / BLOCKED_BY_EXTERNAL_CONFIG`,企微读取 `wecom_errcode_60011`;未改 OA/企微用户,未执行真实员工批量 apply;不据此进入考勤/审批镜像 |
| `WOUA` | `issues/2026-05-18_20-45-58-wecom-oa-user-consistency-audit.csv` | 已收口:OA/企微用户一致性只读对账已由 WAPI 新口径复跑 | OA active 员工 vs 企业微信通讯录 + 人事助手只读审计、脱敏差异报告、本地单测、受控部署和公网执行 | WAPI 复跑结论 `PASS_WITH_FINDINGS`:OA active 12、企微 active 5、匹配 5、OA active 缺企微 7、已归档仍有绑定 1、缺必填字段 5;未改 OA/企微用户,未执行真实员工批量 apply |
| `WAPI` | `issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv` | 已收口:企微 API 能力目录与运行优先纠偏 | 本地 API 能力目录、Secret 矩阵、WHMS/WOUA 读取策略纠偏、文档/skill 回写和公网 WOUA 只读复跑 | 不执行真实员工批量 apply;不打开 `wecom_hr_master_sync_v1`;不修改 OA/企微用户数据;公网只读复跑 `PASS_WITH_FINDINGS` |
旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。
@@ -135,7 +137,8 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
| [海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md) | PRD 主事实源 |
| [文档事实源状态矩阵](./文档事实源状态矩阵.md) | 文档分类、删除恢复和冲突规则 |
| [产品设计规则](./产品设计规则.md) | PC、移动、企业微信 WebView 设计事实源;DG8 后飞书工作台/飞书审批为唯一主 UI/布局参考;DG9 后按页面分层和样板选型 |
| [企微优先开发路线图](./企微优先开发路线图.md) | WDRG/WCP1/WCPC/WHMS/WHMSLIVE/WOUA 后企微优先 HR/考勤改造的阶段顺序、准入/退出门禁、并行规则、禁止跳阶段和当前用户一致性只读审计阻断事实源 |
| [企微优先开发路线图](./企微优先开发路线图.md) | WDRG/WCP1/WCPC/WHMS/WHMSLIVE/WOUA/WAPI 后企微优先 HR/考勤改造的阶段顺序、准入/退出门禁、并行规则、禁止跳阶段、API/Secret 口径和运行优先事实源 |
| [企微 API 能力目录](./企微API能力目录.md) | WAPI 后企微官方 API、Secret 矩阵、权限/可信 IP/回调模板、读写边界和运行优先决策事实源 |
| [产品口径](./产品口径.md) | 已拍板产品边界和业务规则 |
| [需求 - 统一审批中心规约](./需求-统一审批中心规约.md) | OA 主办审批、企微主办 HR/考勤员工日常审批镜像、催办、超时提醒、审批效率 |
| [报销发票附件规则](./报销发票附件规则.md) | 报销、发票、OCR、附件细则 |
+2 -2
查看文件
@@ -119,14 +119,14 @@ WHMS 将企业微信通讯录/人事助手作为 HR 主数据源的读取能力
本地实现后的产品能力:
- `wecom_hr_master_sync_v1` 默认 `force_off`,配置项为 `WECOM_HR_MASTER_SYNC_ENABLED`、`WECOM_HR_ASSISTANT_SECRET`,允许已验证工作台应用 Secret 作为只读 fallback。
- `wecom_hr_master_sync_v1` 默认 `force_off`;WAPI 后读取口径修正为通讯录读取使用 `WECOM_CONTACT_SECRET` 和 `POST /cgi-bin/user/list_id`,人事助手/花名册读取使用 `WECOM_AGENT_SECRET` 和 `POST /cgi-bin/hr/get_staff_info`,不再靠运行时 Secret fallback 试错。
- 管理员可在 People/人员管理页查看“企微 HR 主数据镜像”卡片,执行 dry-run 预检、确认 apply、查看最近批次和失败摘要;普通员工不可见。
- dry-run 只生成差异报告和同步记录,不写 OA 员工主数据;apply 写入员工档案、部门映射、默认 `employee` 角色、企业微信绑定、主管关系和审计。
- 新员工创建必须具备姓名、手机号、邮箱和部门;缺字段记录 `SKIP missing_required_field`,不生成占位数据。
- 企微不在职状态记录 `needs_offboarding_confirmation`,不自动禁用 OA 账号;离职仍走 OA checklist。
- 已有 OA 员工只更新姓名、部门、职位、直属主管、手机号、邮箱和企微绑定,不覆盖银行账户、身份证、工资、合同附件或历史审批。
当前状态为已部署并完成受控公网验证:`WHMSLIVE` 已将 WHMS 部署到 `https://oa.hyinda.com`,远端应用 migration `20260518184319_wecom_hr_master_sync_v1`;验证窗口临时 `force_on` 后对 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply,企微读取链路通过,因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,未写 OA 主数据;普通员工访问同步 API 返回 403;最终 `wecom_hr_master_sync_v1=force_off`。尚未对真实员工执行生产批量 apply。下一阶段可规划 `wecom_attendance_approval_mirror_v1`,但必须另建合同。
当前状态为已部署并完成受控公网验证:`WHMSLIVE` 已将 WHMS 部署到 `https://oa.hyinda.com`,远端应用 migration `20260518184319_wecom_hr_master_sync_v1`;验证窗口临时 `force_on` 后对 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply,企微读取链路通过,因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,未写 OA 主数据;普通员工访问同步 API 返回 403;最终 `wecom_hr_master_sync_v1=force_off`。尚未对真实员工执行生产批量 apply。WAPI 后下一步先运行优先和 WOUA 新口径复跑;`wecom_attendance_approval_mirror_v1` 暂缓到审批模板、测试审批单号和真实回调配置明确后再另建合同。
---
+2 -2
查看文件
@@ -1,6 +1,6 @@
# hyb-oa 管理员手册(10 人以下团队版)
**当前状态(2026-05-18 / WHMSLIVE)**:R7E、PRELAUNCH、CASHIER-VOUCHER-V1、LAUNCH-DEPLOY-FINANCE、FIN-PROD-ENABLE、ACCOUNTING-HANDOFF-V1、ATTV1、HR-WECOM-DIRECTORY-V1、HRWD-CONTACT-LIVE-TEST、DBWFOC、WFDG、WDRG、WCPC、WHMSLIVE 均已收口;SYSTEM-COMPLETE-GATE 结论为 `GO_TO_USE`。WCP1 历史结论为 `PAUSE_FOR_CONFIG`,WCPC 已使用既有在职测试号 `SMOKE002/测试员工`(报告只记录 `oa_smoke***`)复跑通过,结论为 `GO_TO_HR_MASTER_SYNC`。WHMS 已实现并部署验证 `wecom_hr_master_sync_v1`:企微/人事助手 HR 主数据可 dry-run/apply 到 OA 员工镜像、账号、角色、绑定和审计;默认和最终均为 `force_off`,公网 `SMOKE002` 因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,未对真实员工执行生产批量 apply。后续“路线图门禁 + 单功能 Issue CSV”的开发节奏保持不变:下一阶段为 `wecom_attendance_approval_mirror_v1`,必须另建合同;HR/考勤员工日常审批优先复用企业微信,OA 做按域镜像、人工接管、恢复回写、财务闭环和审计中台;采购、费用、合同、项目、报销、付款、AP、代账交接和高敏附件继续 OA 主办,企业微信只做脱敏提醒和 OA deeplink。公网 `payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,且 ERP 未来不接入 OA;财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。
**当前状态(2026-05-18 / WAPI)**:R7E、PRELAUNCH、CASHIER-VOUCHER-V1、LAUNCH-DEPLOY-FINANCE、FIN-PROD-ENABLE、ACCOUNTING-HANDOFF-V1、ATTV1、HR-WECOM-DIRECTORY-V1、HRWD-CONTACT-LIVE-TEST、DBWFOC、WFDG、WDRG、WCPC、WHMSLIVE 均已收口;SYSTEM-COMPLETE-GATE 结论为 `GO_TO_USE`。WCP1 历史结论为 `PAUSE_FOR_CONFIG`,WCPC 已使用既有在职测试号 `SMOKE002/测试员工`(报告只记录 `oa_smoke***`)复跑通过,结论为 `GO_TO_HR_MASTER_SYNC`。WHMS 已实现并部署验证 `wecom_hr_master_sync_v1`:企微/人事助手 HR 主数据可 dry-run/apply 到 OA 员工镜像、账号、角色、绑定和审计;默认和最终均为 `force_off`,公网 `SMOKE002` 因企微侧缺手机号、邮箱和部门按 `missing_required_field` 跳过,未对真实员工执行生产批量 apply。WAPI 后固定企微 API 能力目录和 Secret 矩阵:通讯录使用 `WECOM_CONTACT_SECRET`,人事助手/打卡/消息/OAuth 使用 `WECOM_AGENT_SECRET`,运行时代码不得靠多个 Secret 试错;WOUA 已按新口径复跑为 `PASS_WITH_FINDINGS`,说明 OA/企微用户仍不一致,下一步先处理用户差异或确定 WHMS 批量同步窗口;`wecom_attendance_approval_mirror_v1` 暂缓到审批模板、测试 `sp_no` 和真实回调配置明确后。采购、费用、合同、项目、报销、付款、AP、代账交接和高敏附件继续 OA 主办,企业微信只做脱敏提醒和 OA deeplink。公网 `payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,且 ERP 未来不接入 OA;财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。
## 1. 适用范围
- 适用于 `small_team_internal` 工作区模式。
@@ -443,7 +443,7 @@ WHMS 管理操作口径:
- `APP_ORIGIN=https://oa.example.com`
- `COOKIE_SECURE=true`
- `PUBLIC_SECURITY_HARDENING_ENABLED=true`
3. 若启用企业微信入口,补齐:`WECOM_*` 与 `WECOM_ALLOWED_RETURN_HOSTS`。若启用 OA 驱动企业微信通讯录同步,还需补齐 `WECOM_CONTACT_SYNC_ENABLED=true`、`WECOM_CONTACT_SECRET`、`WECOM_CONTACT_DEFAULT_DEPARTMENT_ID`,并确认该 Secret 具备通讯录写权限。若启用企微打卡同步,确认 `海音达工作台` 已被授权调用打卡 API;`WECOM_CHECKIN_SECRET` 留空时复用 `WECOM_AGENT_SECRET`,单独考勤同步应用才需要填写独立 Secret。若启用企微 HR 主数据镜像,确认 `WECOM_HR_MASTER_SYNC_ENABLED=true` 且人事助手 Secret 或已授权工作台应用 Secret 可读取通讯录和花名册;正式批量 apply 仍由 `wecom_hr_master_sync_v1` feature flag 和单独批准控制。
3. 若启用企业微信入口,补齐:`WECOM_*` 与 `WECOM_ALLOWED_RETURN_HOSTS`。若启用 OA 驱动企业微信通讯录同步,还需补齐 `WECOM_CONTACT_SYNC_ENABLED=true`、`WECOM_CONTACT_SECRET`、`WECOM_CONTACT_DEFAULT_DEPARTMENT_ID`,并确认该 Secret 具备通讯录写权限。若启用企微打卡同步,确认 `海音达工作台` 已被授权调用打卡 API;`WECOM_CHECKIN_SECRET` 留空时复用 `WECOM_AGENT_SECRET`,单独考勤同步应用才需要填写独立 Secret。若启用企微 HR 主数据镜像,按 WAPI 口径确认通讯录读取使用 `WECOM_CONTACT_SECRET`、人事助手/花名册读取使用 `WECOM_AGENT_SECRET`;正式批量 apply 仍由 `wecom_hr_master_sync_v1` feature flag 和单独批准控制。
4. 将 `OPENAPI_DOCS_ENABLED` 保持为 `false`,不要在公网生产暴露文档入口。
5. 定期检查审计日志中的登录失败、Refresh 来源不匹配与涉密资料查看记录。
6. 准备独立 smoke 账号,不使用老板、管理员或真实员工日常账号跑自动化;smoke 账号的角色和直属上级应按正式审批链配置。
+11 -9
查看文件
@@ -4,9 +4,9 @@
**日期**:2026-05-09
**状态**:模块级开发规约
**上级事实源**:[海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md)
**相关事实源**:[需求 - 统一审批中心规约](./需求-统一审批中心规约.md)
**相关事实源**:[需求 - 统一审批中心规约](./需求-统一审批中心规约.md)、[企微 API 能力目录](./企微API能力目录.md)
本规约细化企业微信在 `hyb-oa` 中的基础设施边界、入口、免登/绑定、通讯录身份、消息通知、deeplink、打卡、人事助手、原生审批、OA 镜像、人工接管和恢复回写要求。WFOC 起产品方向调整为“企微优先 + OA 无痛接管”;DBWFOC 后进一步固定为:HR/考勤员工日常审批优先使用企业微信,OA 做按域镜像、备份接管、财务闭环和审计中台;采购、合同、项目、财务和高敏附件继续由 OA 主办。后续企业微信相关开发必须先从本规约拆分 Issue CSV,再进入技术方案和代码实现。
本规约细化企业微信在 `hyb-oa` 中的基础设施边界、入口、免登/绑定、通讯录身份、消息通知、deeplink、打卡、人事助手、原生审批、OA 镜像、人工接管和恢复回写要求。WFOC 起产品方向调整为“企微优先 + OA 无痛接管”;DBWFOC 后进一步固定为:HR/考勤员工日常审批优先使用企业微信,OA 做按域镜像、备份接管、财务闭环和审计中台;采购、合同、项目、财务和高敏附件继续由 OA 主办。WAPI 后,所有企微相关开发必须先读取本规约和 [企微 API 能力目录](./企微API能力目录.md),确认官方 API、Secret、权限、可信 IP、回调/模板和读写边界,再拆 Issue CSV。
---
@@ -40,7 +40,7 @@
| 财务/报销 | 企微不提供本项目需要的 PDF OCR、付款批次、银行回单和代账闭环 | OA 主办 | `KEEP_OA_PRIMARY` |
| 真实支付、ERP、图片 OCR、银行流水导入 | 非本路线目标 | 不进入当前路线 | `NOT_IN_SCOPE` |
官方能力依据包括企业微信开发者中心及文档路径:通讯录创建成员 `90195`,打卡概述 `90261`,获取打卡记录 `90262`,获取员工打卡规则 `90263`,审批概述 `91854`,提交审批申请 `91853`,获取审批详情 `91983`,人事助手概述 `99130`,员工字段配置 `99131`,花名册读取 `99132`,花名册更新 `99133`。真实接口权限、回调配置和可调用应用范围必须在后续 POC 合同中以 `NEEDS_CONFIG` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG` 记录。
官方能力依据以 [企微 API 能力目录](./企微API能力目录.md) 登记的企业微信开发者中心链接为准。首轮已固定:通讯录全量候选使用 `POST /cgi-bin/user/list_id`,花名册读取使用 `POST /cgi-bin/hr/get_staff_info`,花名册更新 `POST /cgi-bin/hr/update_staff_info` 属写接口需单独 POC。真实接口权限、回调配置和可调用应用范围必须在后续 POC 合同中以 `NEEDS_CONFIG` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG` 记录。
---
@@ -215,6 +215,7 @@ REQAUD 复核后,ATTV1 已完成统一入口实现,企业微信打卡边界
- 企业微信/OA 后续改造必须先标注业务域主办方、主事实源、操作端、镜像方向、附件归属和审计责任。
- 企业微信同步必须具备幂等、重试、脱敏日志、审计记录和 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG` 状态;缺 Secret、可信 IP、应用可见范围、模板或 API 权限时不得写成成功。
- WAPI 后新增企微能力必须先更新或引用 [企微 API 能力目录](./企微API能力目录.md),并固定接口路径、方法、Secret 来源、权限范围和读写边界;运行时代码不得靠多个 Secret 试错。
- `WECOM-CAPABILITY-POC-V1` 是正式开发前置,能力矩阵状态固定为 `CAN_REUSE_NOW`、`NEEDS_CONFIG`、`NEEDS_API_POC`、`KEEP_OA_PRIMARY`、`NOT_SUPPORTED_BY_API`。
- `oa_hr_emergency_mode_v1` 只能由管理员人工开启;恢复后由 `wecom_recovery_writeback_v1` 先 dry-run,再对无冲突项回写,冲突、权限不足或 API 不支持进入人工确认。
- `wecom_ruleops_v1` 中 AI 只负责草拟规则、差异报告和 dry-run;正式写入企微审批模板、打卡规则、排班、假期余额或人事字段前必须人工确认,并保存前后快照、确认人、API `errcode`、失败原因和回滚建议。
@@ -222,8 +223,9 @@ REQAUD 复核后,ATTV1 已完成统一入口实现,企业微信打卡边界
## 7.8 WDRG 开发路线图门禁
- WDRG 后,企业微信相关运行时迁移采用“路线图门禁 + 单功能 Issue CSV”,路线图事实源为 [企微优先开发路线图](./企微优先开发路线图.md)。
- 阶段顺序固定为 `WECOM-CAPABILITY-POC-V1` -> `wecom_hr_master_sync_v1` -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1`。
- WAPI 后阶段顺序调整为 `WECOM-CAPABILITY-POC-V1` -> `wecom_hr_master_sync_v1` -> `runtime_first_wecom_enablement` -> `wecom_attendance_approval_mirror_v1` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` -> `wecom_ruleops_v1`。
- `WECOM-CAPABILITY-POC-V1` 未完成前,不得直接开发人事助手主数据镜像、企微 HR/考勤审批镜像、OA 应急接管、恢复回写或 RuleOps 正式写入。
- 运行优先阶段先利用入口、免登/绑定、脱敏消息、deeplink、通讯录 userid 清单、人事助手花名册读取和打卡读取;WOUA 已按 WAPI 新口径复跑为 `PASS_WITH_FINDINGS`,深度考勤/审批镜像需在用户一致性差异处理、审批模板、测试 `sp_no` 和真实回调配置明确后再拆。
- 线上缺陷、正式使用阻断、真实反馈扫尾和 QA-only 脚本维护可另开合同并行;采购、合同、报销、付款、AP、代账和高敏附件继续 OA 主办,不混入企微 HR/考勤路线。
- 本路线图只代表开发节奏和门禁,不代表任何企微 HR/考勤镜像、OA 应急接管、恢复回写或 RuleOps 运行时能力已完成。
@@ -237,8 +239,8 @@ WCP1 当前分类:
| --- | --- | --- |
| 工作台应用 token 和核心配置 | `CAN_REUSE_NOW` | CorpID、Agent、Secret、OAuth 等基础配置存在,access token 可获取。 |
| 通讯录 create/update/disable | `CAN_REUSE_NOW` | 复用 HCLT 证据:`HRWDTEST-20260517214548` create/update/disable 均 `errcode=0`。 |
| 通讯录部门读取 | `CAN_REUSE_NOW` | 工作台应用 token fallback 可读部门列表。 |
| 通讯录测试成员读取 | `NEEDS_CONFIG` | `contact.member_read` 返回 `wecom_errcode_48009`,需核对通讯录读取权限、应用可见范围或测试成员状态。 |
| 通讯录部门读取 | `CAN_REUSE_NOW` | WCP1 曾通过工作台应用 token fallback 读部门;WAPI 后该口径被 supersede,后续通讯录读取固定使用 `WECOM_CONTACT_SECRET`,部门列表只作可选富化。 |
| 通讯录测试成员详情读取 | `OPTIONAL_ENRICHMENT` | `contact.member_read` 历史返回 `wecom_errcode_48009`;WAPI 后成员详情只作富化,`user/list_id` 可读且人事助手花名册可读时不得因此阻断 WOUA/WHMS。 |
| 人事助手字段配置读取 | `CAN_REUSE_NOW` | `hr.fields_read` 可读。 |
| 人事助手测试员工花名册读取 | `NEEDS_CONFIG` | `hr.staff_read` 返回 `wecom_errcode_60021`,需将测试成员纳入应用/人事助手权限范围,或改用在职测试员工复跑。 |
| 人事助手资料更新 | `NEEDS_API_POC` | 属生产写接口,本轮只分类,不调用。 |
@@ -285,7 +287,7 @@ WCPC 已达到 `GO_TO_HR_MASTER_SYNC`。WHMS 已承接该结论并完成 `wecom_
已实现范围:
- 新增 feature flag `wecom_hr_master_sync_v1`,默认 `force_off`。
- 新增配置 `WECOM_HR_MASTER_SYNC_ENABLED`、`WECOM_HR_ASSISTANT_SECRET`;`WECOM_AGENT_SECRET` 仅作为已验证授权应用的只读 fallback,并在同步批次中记录 token source,不输出 Secret。
- WAPI 后 Secret 口径修正为:通讯录读取使用 `WECOM_CONTACT_SECRET`;人事助手/花名册读取使用 `WECOM_AGENT_SECRET`;`WECOM_HR_ASSISTANT_SECRET` 仅保留为历史兼容项且不参与默认读取;同步批次只记录 token source,不输出 Secret。
- 新增后端管理 API:`POST /api/employees/wecom-hr-master-sync/runs`、`GET /api/employees/wecom-hr-master-sync/runs`、`GET /api/employees/wecom-hr-master-sync/runs/:id`。
- 新增 `WecomHrMasterSyncRun`、`WecomHrMasterSyncItem`,记录批次、逐员工动作、脱敏 externalUserId、字段差异、失败原因、重试次数和操作人。
- People/人员管理页新增“企微 HR 主数据镜像”卡片,管理员可执行 dry-run、确认 apply、查看最近同步和失败摘要;普通员工不可见。
@@ -304,10 +306,10 @@ WCPC 已达到 `GO_TO_HR_MASTER_SYNC`。WHMS 已承接该结论并完成 `wecom_
当前状态:
- WHMS 已通过 `WHMSLIVE` 部署并完成公网验证;部署时应用 migration `20260518184319_wecom_hr_master_sync_v1`,`/ready`、live smoke、public bundle 和 feature flag 基线复检通过。
- 公网验证临时开启 `wecom_hr_master_sync_v1=force_on`,对 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply;通讯录 Secret 遇到 `48009` 时已按 WCPC 逻辑 fallback 到已授权工作台应用 Secret。
- 公网验证临时开启 `wecom_hr_master_sync_v1=force_on`,对 `SMOKE002/测试员工`(脱敏 `oa_smoke***`)执行 dry-run 和显式 userids apply;WAPI 后该历史 fallback 口径已被 supersede,后续只读全量读取使用通讯录 `user/list_id` 和人事助手 `WECOM_AGENT_SECRET`。
- `SMOKE002` 企微侧缺手机号、邮箱和部门,apply 按 `missing_required_field` 跳过,未写 OA 主数据;这符合“缺 OA 必填字段默认跳过待补”的边界,不代表真实员工批量同步已启用。
- 已验证普通员工访问同步 API 返回 403;验证结束后 `wecom_hr_master_sync_v1` 已恢复为 `force_off`。
- WHMS 不解除考勤/审批镜像前置缺口;`wecom_attendance_approval_mirror_v1` 仍需另建合同补审批模板、审批单号和回调配置。
- WHMS 不解除考勤/审批镜像前置缺口;下一步先处理 WOUA 发现的用户一致性差异或明确 WHMS 批量同步窗口,`wecom_attendance_approval_mirror_v1` 暂缓到审批模板、审批单号、回调配置和打卡细项 POC 明确后再另建合同。
---
@@ -0,0 +1,10 @@
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
"WAPI-0","合同建档与边界冻结","建立 WAPI 企微 API 本地能力目录与运行优先纠偏合同,锁定本轮只调研官方文档、固化 Secret/API 口径、纠正只读读取策略并复跑 WOUA,不执行真实员工批量 apply、不打开 wecom_hr_master_sync_v1、不修改 OA 或企微用户数据。","1) CSV 校验通过;2) 范围覆盖 API 能力目录、Secret 矩阵、WHMS/WOUA 只读读取纠偏、文档/skill 回写和公网只读复跑;3) 不新增公开 API、Prisma schema、前端 UI 或 feature flag;4) 记录现有 WOUA/WHMS 口径被本轮 supersede 的范围。","受影响流程:企微开发需求 -> Issue 合同 -> API 调研和读取纠偏 -> 后续功能派工;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv。","python3|manual","DONE","DONE","DONE","issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv","[]","回滚路径:删除本 CSV 和本轮文档/代码回写;本轮不涉及数据库迁移或生产数据写入。"
"WAPI-1","企微官方 API 能力目录","调研本项目会用到的企业微信官方开发者文档,新增 docs/企微API能力目录.md,按能力记录官方链接、接口路径、方法、Secret 来源、权限/可见范围、可信 IP、回调/模板要求、读写边界、项目状态和适用业务。","1) 覆盖基础认证、工作台/OAuth、应用消息、通讯录、人事助手、打卡、审批、回调与安全;2) 区分产品 UI 支持和开放 API 支持;3) 不复制官方文档全文,只沉淀本项目摘要和决策;4) 不包含 Secret、token、完整 userid、手机号、邮箱或原始 payload。","受影响流程:企微能力不清楚 -> 先读本地 API 目录 -> 再拆实现合同;验证步骤:rg -n 'WECOM_CONTACT_SECRET|WECOM_AGENT_SECRET|list_id|get_staff_info|update_staff_info|产品 UI|API 支持|BLOCKED_BY_EXTERNAL_CONFIG' docs/企微API能力目录.md。","curl|rg|manual","DONE","DONE","DONE","docs/企微API能力目录.md","[""WAPI-0""]","回滚路径:删除新增目录文档和索引登记;官方文档以企业微信开发者中心为准,每条能力记录最后核对日期。"
"WAPI-2","Secret 矩阵固化","将企微 Secret 使用规则固化为通讯录专用 WECOM_CONTACT_SECRET、工作台/多数开放 API 使用 WECOM_AGENT_SECRET、WECOM_CHECKIN_SECRET 仅作显式打卡 override、WECOM_HR_ASSISTANT_SECRET 当前不作为标准配置。","1) 文档、skill 和代码口径一致;2) 运行时代码不再用多个 Secret 试错;3) 缺权限或缺配置输出 SKIP/BLOCKED_BY_EXTERNAL_CONFIG;4) 保留历史兼容项时必须标记 deprecated 且不参与默认读取。","受影响流程:企微配置维护 -> 后端读取能力 -> 错误归因和后续排障;验证步骤:rg -n 'WECOM_CONTACT_SECRET|WECOM_AGENT_SECRET|WECOM_CHECKIN_SECRET|WECOM_HR_ASSISTANT_SECRET|deprecated|不得.*试错|BLOCKED_BY_EXTERNAL_CONFIG' docs .agents/skills/hyb-oa-delivery/SKILL.md server/src/services/integrations/wecomHrMaster.js。","rg|node","DONE","DONE","DONE","docs|.agents/skills/hyb-oa-delivery/SKILL.md|server/src/services/integrations/wecomHrMaster.js","[""WAPI-1""]","回滚路径:回退 Secret 矩阵文档和读取服务改动;不修改远端 env。"
"WAPI-3","WHMS 读取策略纠偏","纠正 wecomHrMaster 只读读取策略:通讯录全量使用 POST /cgi-bin/user/list_id 加可选成员详情富化,通讯录 token 固定 WECOM_CONTACT_SECRET;人事助手读取固定 WECOM_AGENT_SECRET;移除运行时 fallback 试错语义。","1) 全量候选 userid 来自 user/list_id;2) 指定 userids 时按可选成员详情和花名册详情读取;3) tokenSource 不再出现 WECOM_AGENT_SECRET_FALLBACK 作为默认路径;4) 错误摘要脱敏且不含 Secret/token/完整手机号/邮箱。","受影响流程:WHMS/WOUA 读取企微通讯录和人事助手 -> 生成同步或审计候选;验证步骤:cd server && node --check src/services/integrations/wecomHrMaster.js;cd server && npx vitest run tests/unit/wecomHrMaster.service.test.js --reporter=verbose。","node|vitest","DONE","DONE","DONE","server/src/services/integrations/wecomHrMaster.js|server/tests/unit/wecomHrMaster.service.test.js","[""WAPI-2""]","回滚路径:回退读取服务和 focused 单测;不回退既有通讯录写入服务。"
"WAPI-4","WOUA 对账复跑纠偏","让 OA/企微用户一致性只读对账复用 WAPI 后的固定读取策略,保持 BEGIN READ ONLY、脱敏报告和不调用 WHMS dry-run/apply API 的边界。","1) WOUA 不写 OA/企微用户数据;2) 本地脱敏单测仍通过;3) live 缺配置时只输出 SKIP/BLOCKED_BY_EXTERNAL_CONFIG;4) 若读取成功,报告分类仍保持 matchedActive、missing、mismatch 和 conflict 口径。","受影响流程:OA active 员工 -> 企微通讯录 userid 清单 + 人事助手花名册 -> 脱敏一致性报告;验证步骤:cd server && node --check src/scripts/audit-wecom-oa-user-consistency.js;cd server && npx vitest run tests/unit/scripts/audit-wecom-oa-user-consistency.test.js --reporter=verbose。","node|vitest|pg|wecom","DONE","DONE","DONE","server/src/scripts/audit-wecom-oa-user-consistency.js|server/tests/unit/scripts/audit-wecom-oa-user-consistency.test.js","[""WAPI-3""]","回滚路径:回退 WOUA runner 相关改动;报告不得输出 Secret、token、完整 userid、手机号、邮箱或原始 payload。"
"WAPI-5","运行优先路线回写","更新企微优先开发路线图、企业微信基础设施规约、产品口径和管理员手册,将下一步从深度镜像/功能对齐调整为优先用入口、免登/绑定、脱敏消息、deeplink、通讯录 userid 清单、人事助手读取和打卡读取。","1) 明确 wecom_attendance_approval_mirror_v1、应急接管、恢复回写、RuleOps 暂缓;2) 明确 WOUA 复跑和审批/回调配置是后续前置;3) 不宣称企业微信产品 UI 功能都有开放 API;4) 保留 OA 主办高敏业务边界。","受影响流程:项目路线判断 -> 后续企微 Issue 拆分 -> 首批系统运行;验证步骤:rg -n '运行优先|暂缓|企微API能力目录|wecom_attendance_approval_mirror_v1|产品 UI|开放 API|OA 主办' docs/企微优先开发路线图.md docs/需求-企业微信基础设施规约.md docs/产品口径.md docs/管理员手册.md。","rg|manual","DONE","DONE","DONE","docs/企微优先开发路线图.md|docs/需求-企业微信基础设施规约.md|docs/产品口径.md|docs/管理员手册.md","[""WAPI-2""]","回滚路径:回退路线口径文档;不改变运行时配置或业务数据。"
"WAPI-6","索引矩阵与 Skill 回写","登记 docs/企微API能力目录.md 到文档索引和事实源矩阵,并更新 hyb-oa-delivery skill,要求企微开发先读 API 目录和 Secret 矩阵。","1) 文档索引可从当前事实源入口找到 API 能力目录;2) 事实源矩阵新增 WAPI/API-CATALOG 记录;3) skill quick_validate 通过;4) 未来企微任务不会只读路线图而跳过 API 目录。","受影响流程:后续 agent 处理企微任务 -> 先读本地 API 目录 -> 避免重复调研或 Secret 试错;验证步骤:node scripts/qa/audit-docs.mjs --check-registry;python3 /home/shunli/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/hyb-oa-delivery。","node|python3|rg","DONE","DONE","DONE","docs/文档索引.md|docs/文档事实源状态矩阵.md|.agents/skills/hyb-oa-delivery/SKILL.md","[""WAPI-5""]","回滚路径:回退索引、矩阵和 skill 改动;新增事实源不替代官方文档。"
"WAPI-7","公网只读复跑","本地门禁通过后,以 SYNC_ENV=0 受控部署 QA-only 变更,在远端 server 容器执行 OA/企微用户一致性只读复跑,验证 WAPI 固化后的通讯录和人事助手读取路径。","1) 部署不覆盖远端 env;2) 不打开 wecom_hr_master_sync_v1;3) 不执行真实员工批量 apply;4) 报告结论只能为 PASS_NO_DRIFT、PASS_WITH_FINDINGS 或真实 SKIP/BLOCKED_BY_EXTERNAL_CONFIG。","受影响流程:公网 OA 员工和企业微信可见成员 -> 只读一致性审计 -> 后续修复或运行路线决策;验证步骤:SYNC_ENV=0 bash deploy/trialrun/deploy.sh;ssh 远端在 server 容器内执行 npm run audit:wecom-oa-users -- --live --format=json 并核对脱敏报告。","bash|ssh|node","DONE","DONE","DONE","external|artifacts/qa-flow/wecom-oa-user-consistency-*","[""WAPI-4"",""WAPI-6""]","公网复跑报告 /app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json,结论 PASS_WITH_FINDINGS;远端 wecom_hr_master_sync_v1=force_off;回滚路径:部署回滚按既有部署流程;本 Issue 不写生产用户数据。"
"WAPI-8","状态回填与中文提交","完成 WAPI 状态回填、本地门禁、公网只读报告核对和中文提交。","1) WAPI-0..7 按实测回填;2) validate_issues_csv、文档审计、skill 校验、node --check、focused vitest、prisma validate、git diff --check 均通过或记录明确阻断;3) 中文 commit 引用 WAPI;4) 最终说明是否已部署和用户是否可测。","受影响流程:Issue 合同 -> 验证证据 -> 中文提交 -> 后续企微开发依据;验证步骤:git status --short;git log -1 --oneline。","git|manual","DONE","DONE","DONE","issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv|docs|server/src/services/integrations/wecomHrMaster.js|server/tests/unit/wecomHrMaster.service.test.js|server/src/scripts/audit-wecom-oa-user-consistency.js|server/tests/unit/scripts/audit-wecom-oa-user-consistency.test.js|.agents/skills/hyb-oa-delivery/SKILL.md","[""WAPI-7""]","本地门禁通过,公网 WOUA 复跑 PASS_WITH_FINDINGS;提交前确认 diff 不包含 Secret、token、手机号、邮箱、完整 userid 或企微原始 payload。"
1 ID Title Description Acceptance Test_Method Tools Dev_Status Review1_Status Regression_Status Files Dependencies Notes
2 WAPI-0 合同建档与边界冻结 建立 WAPI 企微 API 本地能力目录与运行优先纠偏合同,锁定本轮只调研官方文档、固化 Secret/API 口径、纠正只读读取策略并复跑 WOUA,不执行真实员工批量 apply、不打开 wecom_hr_master_sync_v1、不修改 OA 或企微用户数据。 1) CSV 校验通过;2) 范围覆盖 API 能力目录、Secret 矩阵、WHMS/WOUA 只读读取纠偏、文档/skill 回写和公网只读复跑;3) 不新增公开 API、Prisma schema、前端 UI 或 feature flag;4) 记录现有 WOUA/WHMS 口径被本轮 supersede 的范围。 受影响流程:企微开发需求 -> Issue 合同 -> API 调研和读取纠偏 -> 后续功能派工;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv。 python3|manual DONE DONE DONE issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv [] 回滚路径:删除本 CSV 和本轮文档/代码回写;本轮不涉及数据库迁移或生产数据写入。
3 WAPI-1 企微官方 API 能力目录 调研本项目会用到的企业微信官方开发者文档,新增 docs/企微API能力目录.md,按能力记录官方链接、接口路径、方法、Secret 来源、权限/可见范围、可信 IP、回调/模板要求、读写边界、项目状态和适用业务。 1) 覆盖基础认证、工作台/OAuth、应用消息、通讯录、人事助手、打卡、审批、回调与安全;2) 区分产品 UI 支持和开放 API 支持;3) 不复制官方文档全文,只沉淀本项目摘要和决策;4) 不包含 Secret、token、完整 userid、手机号、邮箱或原始 payload。 受影响流程:企微能力不清楚 -> 先读本地 API 目录 -> 再拆实现合同;验证步骤:rg -n 'WECOM_CONTACT_SECRET|WECOM_AGENT_SECRET|list_id|get_staff_info|update_staff_info|产品 UI|API 支持|BLOCKED_BY_EXTERNAL_CONFIG' docs/企微API能力目录.md。 curl|rg|manual DONE DONE DONE docs/企微API能力目录.md ["WAPI-0"] 回滚路径:删除新增目录文档和索引登记;官方文档以企业微信开发者中心为准,每条能力记录最后核对日期。
4 WAPI-2 Secret 矩阵固化 将企微 Secret 使用规则固化为通讯录专用 WECOM_CONTACT_SECRET、工作台/多数开放 API 使用 WECOM_AGENT_SECRET、WECOM_CHECKIN_SECRET 仅作显式打卡 override、WECOM_HR_ASSISTANT_SECRET 当前不作为标准配置。 1) 文档、skill 和代码口径一致;2) 运行时代码不再用多个 Secret 试错;3) 缺权限或缺配置输出 SKIP/BLOCKED_BY_EXTERNAL_CONFIG;4) 保留历史兼容项时必须标记 deprecated 且不参与默认读取。 受影响流程:企微配置维护 -> 后端读取能力 -> 错误归因和后续排障;验证步骤:rg -n 'WECOM_CONTACT_SECRET|WECOM_AGENT_SECRET|WECOM_CHECKIN_SECRET|WECOM_HR_ASSISTANT_SECRET|deprecated|不得.*试错|BLOCKED_BY_EXTERNAL_CONFIG' docs .agents/skills/hyb-oa-delivery/SKILL.md server/src/services/integrations/wecomHrMaster.js。 rg|node DONE DONE DONE docs|.agents/skills/hyb-oa-delivery/SKILL.md|server/src/services/integrations/wecomHrMaster.js ["WAPI-1"] 回滚路径:回退 Secret 矩阵文档和读取服务改动;不修改远端 env。
5 WAPI-3 WHMS 读取策略纠偏 纠正 wecomHrMaster 只读读取策略:通讯录全量使用 POST /cgi-bin/user/list_id 加可选成员详情富化,通讯录 token 固定 WECOM_CONTACT_SECRET;人事助手读取固定 WECOM_AGENT_SECRET;移除运行时 fallback 试错语义。 1) 全量候选 userid 来自 user/list_id;2) 指定 userids 时按可选成员详情和花名册详情读取;3) tokenSource 不再出现 WECOM_AGENT_SECRET_FALLBACK 作为默认路径;4) 错误摘要脱敏且不含 Secret/token/完整手机号/邮箱。 受影响流程:WHMS/WOUA 读取企微通讯录和人事助手 -> 生成同步或审计候选;验证步骤:cd server && node --check src/services/integrations/wecomHrMaster.js;cd server && npx vitest run tests/unit/wecomHrMaster.service.test.js --reporter=verbose。 node|vitest DONE DONE DONE server/src/services/integrations/wecomHrMaster.js|server/tests/unit/wecomHrMaster.service.test.js ["WAPI-2"] 回滚路径:回退读取服务和 focused 单测;不回退既有通讯录写入服务。
6 WAPI-4 WOUA 对账复跑纠偏 让 OA/企微用户一致性只读对账复用 WAPI 后的固定读取策略,保持 BEGIN READ ONLY、脱敏报告和不调用 WHMS dry-run/apply API 的边界。 1) WOUA 不写 OA/企微用户数据;2) 本地脱敏单测仍通过;3) live 缺配置时只输出 SKIP/BLOCKED_BY_EXTERNAL_CONFIG;4) 若读取成功,报告分类仍保持 matchedActive、missing、mismatch 和 conflict 口径。 受影响流程:OA active 员工 -> 企微通讯录 userid 清单 + 人事助手花名册 -> 脱敏一致性报告;验证步骤:cd server && node --check src/scripts/audit-wecom-oa-user-consistency.js;cd server && npx vitest run tests/unit/scripts/audit-wecom-oa-user-consistency.test.js --reporter=verbose。 node|vitest|pg|wecom DONE DONE DONE server/src/scripts/audit-wecom-oa-user-consistency.js|server/tests/unit/scripts/audit-wecom-oa-user-consistency.test.js ["WAPI-3"] 回滚路径:回退 WOUA runner 相关改动;报告不得输出 Secret、token、完整 userid、手机号、邮箱或原始 payload。
7 WAPI-5 运行优先路线回写 更新企微优先开发路线图、企业微信基础设施规约、产品口径和管理员手册,将下一步从深度镜像/功能对齐调整为优先用入口、免登/绑定、脱敏消息、deeplink、通讯录 userid 清单、人事助手读取和打卡读取。 1) 明确 wecom_attendance_approval_mirror_v1、应急接管、恢复回写、RuleOps 暂缓;2) 明确 WOUA 复跑和审批/回调配置是后续前置;3) 不宣称企业微信产品 UI 功能都有开放 API;4) 保留 OA 主办高敏业务边界。 受影响流程:项目路线判断 -> 后续企微 Issue 拆分 -> 首批系统运行;验证步骤:rg -n '运行优先|暂缓|企微API能力目录|wecom_attendance_approval_mirror_v1|产品 UI|开放 API|OA 主办' docs/企微优先开发路线图.md docs/需求-企业微信基础设施规约.md docs/产品口径.md docs/管理员手册.md。 rg|manual DONE DONE DONE docs/企微优先开发路线图.md|docs/需求-企业微信基础设施规约.md|docs/产品口径.md|docs/管理员手册.md ["WAPI-2"] 回滚路径:回退路线口径文档;不改变运行时配置或业务数据。
8 WAPI-6 索引矩阵与 Skill 回写 登记 docs/企微API能力目录.md 到文档索引和事实源矩阵,并更新 hyb-oa-delivery skill,要求企微开发先读 API 目录和 Secret 矩阵。 1) 文档索引可从当前事实源入口找到 API 能力目录;2) 事实源矩阵新增 WAPI/API-CATALOG 记录;3) skill quick_validate 通过;4) 未来企微任务不会只读路线图而跳过 API 目录。 受影响流程:后续 agent 处理企微任务 -> 先读本地 API 目录 -> 避免重复调研或 Secret 试错;验证步骤:node scripts/qa/audit-docs.mjs --check-registry;python3 /home/shunli/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/hyb-oa-delivery。 node|python3|rg DONE DONE DONE docs/文档索引.md|docs/文档事实源状态矩阵.md|.agents/skills/hyb-oa-delivery/SKILL.md ["WAPI-5"] 回滚路径:回退索引、矩阵和 skill 改动;新增事实源不替代官方文档。
9 WAPI-7 公网只读复跑 本地门禁通过后,以 SYNC_ENV=0 受控部署 QA-only 变更,在远端 server 容器执行 OA/企微用户一致性只读复跑,验证 WAPI 固化后的通讯录和人事助手读取路径。 1) 部署不覆盖远端 env;2) 不打开 wecom_hr_master_sync_v1;3) 不执行真实员工批量 apply;4) 报告结论只能为 PASS_NO_DRIFT、PASS_WITH_FINDINGS 或真实 SKIP/BLOCKED_BY_EXTERNAL_CONFIG。 受影响流程:公网 OA 员工和企业微信可见成员 -> 只读一致性审计 -> 后续修复或运行路线决策;验证步骤:SYNC_ENV=0 bash deploy/trialrun/deploy.sh;ssh 远端在 server 容器内执行 npm run audit:wecom-oa-users -- --live --format=json 并核对脱敏报告。 bash|ssh|node DONE DONE DONE external|artifacts/qa-flow/wecom-oa-user-consistency-* ["WAPI-4","WAPI-6"] 公网复跑报告 /app/artifacts/qa-flow/wecom-oa-user-consistency-20260518T142544Z/summary.json,结论 PASS_WITH_FINDINGS;远端 wecom_hr_master_sync_v1=force_off;回滚路径:部署回滚按既有部署流程;本 Issue 不写生产用户数据。
10 WAPI-8 状态回填与中文提交 完成 WAPI 状态回填、本地门禁、公网只读报告核对和中文提交。 1) WAPI-0..7 按实测回填;2) validate_issues_csv、文档审计、skill 校验、node --check、focused vitest、prisma validate、git diff --check 均通过或记录明确阻断;3) 中文 commit 引用 WAPI;4) 最终说明是否已部署和用户是否可测。 受影响流程:Issue 合同 -> 验证证据 -> 中文提交 -> 后续企微开发依据;验证步骤:git status --short;git log -1 --oneline。 git|manual DONE DONE DONE issues/2026-05-18_21-46-41-wecom-api-capability-catalog-runtime-first.csv|docs|server/src/services/integrations/wecomHrMaster.js|server/tests/unit/wecomHrMaster.service.test.js|server/src/scripts/audit-wecom-oa-user-consistency.js|server/tests/unit/scripts/audit-wecom-oa-user-consistency.test.js|.agents/skills/hyb-oa-delivery/SKILL.md ["WAPI-7"] 本地门禁通过,公网 WOUA 复跑 PASS_WITH_FINDINGS;提交前确认 diff 不包含 Secret、token、手机号、邮箱、完整 userid 或企微原始 payload。
@@ -182,6 +182,7 @@ function parseDateOnly(value) {
function sanitizeWecomPayload(payload) {
const base = sanitizeWecomResponse(payload) ?? {};
if (Array.isArray(payload?.userlist)) base.userCount = payload.userlist.length;
if (Array.isArray(payload?.dept_user)) base.deptUserCount = payload.dept_user.length;
if (Array.isArray(payload?.department)) base.departmentCount = payload.department.length;
if (payload?.userid != null) base.useridPrefix = maskExternalUserId(payload.userid);
if (payload?.enable != null) base.enable = payload.enable;
@@ -289,21 +290,15 @@ async function getTokenFromCandidates({ config, candidates, timeoutMs, fetchImpl
function contactTokenCandidates(config) {
return [
{ source: "WECOM_CONTACT_SECRET", secret: config?.wecomContactSecret },
{ source: "WECOM_AGENT_SECRET_FALLBACK", secret: config?.wecomAgentSecret },
];
}
function hrTokenCandidates(config) {
return [
{ source: "WECOM_HR_ASSISTANT_SECRET", secret: config?.wecomHrAssistantSecret },
{ source: "WECOM_AGENT_SECRET_FALLBACK", secret: config?.wecomAgentSecret },
{ source: "WECOM_AGENT_SECRET", secret: config?.wecomAgentSecret },
];
}
function canFallbackToAgentSecret(error) {
return error instanceof WecomHrMasterSyncError && error.code === "wecom_errcode_48009";
}
function normalizeDepartmentMap(payload) {
const map = new Map();
const departments = Array.isArray(payload?.department) ? payload.department : [];
@@ -332,17 +327,65 @@ async function fetchDepartmentMap({ accessToken, fetchImpl, timeoutMs }) {
return normalizeDepartmentMap(result.payload);
}
async function listContactMembers({ accessToken, departmentId, limit, fetchImpl, timeoutMs }) {
const result = await fetchWecomJson("/cgi-bin/user/list", accessToken, {
query: { department_id: departmentId, fetch_child: 1 },
async function listContactMembers({ accessToken, limit, fetchImpl, timeoutMs }) {
const membersByUserid = new Map();
let cursor = "";
do {
const result = await fetchWecomJson("/cgi-bin/user/list_id", accessToken, {
body: {
cursor,
limit: Math.min(10000, Math.max(1, limit - membersByUserid.size)),
},
fetchImpl,
timeoutMs,
});
if (!result.ok) {
const error = classifyWecomResult(result, "wecom_user_list_failed");
const error = classifyWecomResult(result, "wecom_user_list_id_failed");
throw new WecomHrMasterSyncError(error.code, error.message, { responseSummary: error.responseSummary });
}
return (Array.isArray(result.payload?.userlist) ? result.payload.userlist : []).slice(0, limit);
const batch = Array.isArray(result.payload?.dept_user) ? result.payload.dept_user : [];
for (const item of batch) {
const userid = toSafeString(item?.userid, 128);
if (!userid) continue;
const member = membersByUserid.get(userid) || { userid, department: [] };
if (item?.department != null && !member.department.includes(item.department)) {
member.department.push(item.department);
}
membersByUserid.set(userid, member);
if (membersByUserid.size >= limit) break;
}
cursor = toSafeString(result.payload?.next_cursor, 256);
} while (cursor && membersByUserid.size < limit);
const members = Array.from(membersByUserid.values()).slice(0, limit);
const contacts = await Promise.all(members.map(async (member) => {
const detail = await fetchOptionalContactMember({
accessToken,
userid: member.userid,
fetchImpl,
timeoutMs,
});
return {
...detail,
userid: detail.userid ?? member.userid,
department: Array.isArray(detail.department) && detail.department.length ? detail.department : member.department,
};
}));
return contacts;
}
async function fetchOptionalDepartmentMap({ accessToken, fetchImpl, timeoutMs }) {
try {
return await fetchDepartmentMap({ accessToken, fetchImpl, timeoutMs });
} catch (error) {
if (error instanceof WecomHrMasterSyncError) {
return new Map();
}
throw error;
}
}
async function fetchContactMember({ accessToken, userid, fetchImpl, timeoutMs }) {
@@ -358,6 +401,17 @@ async function fetchContactMember({ accessToken, userid, fetchImpl, timeoutMs })
return result.payload;
}
async function fetchOptionalContactMember({ accessToken, userid, fetchImpl, timeoutMs }) {
try {
return await fetchContactMember({ accessToken, userid, fetchImpl, timeoutMs });
} catch (error) {
if (error instanceof WecomHrMasterSyncError) {
return { userid };
}
throw error;
}
}
async function fetchHrStaffInfo({ accessToken, userid, fetchImpl, timeoutMs }) {
const result = await fetchWecomJson("/cgi-bin/hr/get_staff_info", accessToken, {
body: { userid, get_all: true },
@@ -424,14 +478,13 @@ function normalizeWecomHrProfile({ contact = {}, staff = {}, departmentMap = new
}
async function fetchContactProfilesInput({ config, token, userids, limit, fetchImpl, timeoutMs }) {
const departmentMap = await fetchDepartmentMap({
const departmentMap = await fetchOptionalDepartmentMap({
accessToken: token.accessToken,
fetchImpl,
timeoutMs,
});
const departmentId = toSafeString(config.wecomContactDefaultDepartmentId, 32) || "1";
const contacts = userids.length
? await Promise.all(userids.map((userid) => fetchContactMember({
? await Promise.all(userids.map((userid) => fetchOptionalContactMember({
accessToken: token.accessToken,
userid,
fetchImpl,
@@ -439,7 +492,6 @@ async function fetchContactProfilesInput({ config, token, userids, limit, fetchI
})))
: await listContactMembers({
accessToken: token.accessToken,
departmentId,
limit,
fetchImpl,
timeoutMs,
@@ -448,55 +500,9 @@ async function fetchContactProfilesInput({ config, token, userids, limit, fetchI
return { departmentMap, contacts, tokenSource: token.source };
}
async function fetchContactProfilesInputWithFallback({ config, contactToken, userids, limit, fetchImpl, timeoutMs }) {
try {
return await fetchContactProfilesInput({
config,
token: contactToken,
userids,
limit,
fetchImpl,
timeoutMs,
});
} catch (error) {
if (!canFallbackToAgentSecret(error) || contactToken.source === "WECOM_AGENT_SECRET_FALLBACK") {
throw error;
}
const fallback = await getTokenFromCandidates({
config,
candidates: [{ source: "WECOM_AGENT_SECRET_FALLBACK", secret: config?.wecomAgentSecret }],
timeoutMs,
fetchImpl,
});
if (!fallback.ok) {
throw error;
}
try {
return await fetchContactProfilesInput({
config,
token: fallback,
userids,
limit,
fetchImpl,
timeoutMs,
});
} catch (fallbackError) {
if (canFallbackToAgentSecret(fallbackError)) {
fallbackError.details = {
...(fallbackError.details ?? {}),
firstTokenSource: contactToken.source,
fallbackTokenSource: fallback.source,
};
}
throw fallbackError;
}
}
}
async function fetchWecomHrProfiles({ config, userids, limit, fetchImpl = fetch }) {
const timeoutMs = config.wecomRequestTimeoutMs ?? 10000;
const readLimit = normalizeLimit(limit);
const contactToken = await getTokenFromCandidates({
config,
candidates: contactTokenCandidates(config),
@@ -517,11 +523,11 @@ async function fetchWecomHrProfiles({ config, userids, limit, fetchImpl = fetch
throw new WecomHrMasterSyncError(hrToken.code, hrToken.message, { missing: hrToken.missing });
}
const contactInput = await fetchContactProfilesInputWithFallback({
const contactInput = await fetchContactProfilesInput({
config,
contactToken,
token: contactToken,
userids,
limit,
limit: readLimit,
fetchImpl,
timeoutMs,
});
+47 -29
查看文件
@@ -111,66 +111,84 @@ describe("wecom HR master sync helpers", () => {
expect(changes.externalUserId.after).toBe(maskExternalUserId("oa_smoke002"));
});
it("falls back to the authorized app secret when the contact secret is forbidden for read APIs", async () => {
it("uses fixed contact and agent secrets with list_id for full contact inventory", async () => {
const calls = [];
const fetchImpl = async (url, init = {}) => {
const parsed = new URL(String(url));
calls.push({ path: parsed.pathname, token: parsed.searchParams.get("access_token") });
calls.push({
path: parsed.pathname,
token: parsed.searchParams.get("access_token"),
method: init.method ?? "GET",
body: init.body ? JSON.parse(init.body) : null,
secret: parsed.searchParams.get("corpsecret"),
});
if (parsed.pathname === "/cgi-bin/gettoken") {
const secret = parsed.searchParams.get("corpsecret");
if (secret === "contact-secret-whms-fallback") {
if (secret === "contact-secret-wapi") {
return jsonResponse({ errcode: 0, access_token: "contact-token", expires_in: 7200 });
}
if (secret === "agent-secret-whms-fallback") {
if (secret === "agent-secret-wapi") {
return jsonResponse({ errcode: 0, access_token: "agent-token", expires_in: 7200 });
}
if (secret === "hr-secret-whms-fallback") {
return jsonResponse({ errcode: 0, access_token: "hr-token", expires_in: 7200 });
}
}
if (parsed.pathname === "/cgi-bin/department/list" && parsed.searchParams.get("access_token") === "contact-token") {
return jsonResponse({ errcode: 48009, errmsg: "api forbidden for contact assistant" });
return jsonResponse({ errcode: 48009, errmsg: "department list not visible to contact app" });
}
if (parsed.pathname === "/cgi-bin/department/list" && parsed.searchParams.get("access_token") === "agent-token") {
return jsonResponse({ errcode: 0, department: [{ id: 1, name: "管理部", parentid: 0 }] });
}
if (parsed.pathname === "/cgi-bin/user/get" && parsed.searchParams.get("access_token") === "agent-token") {
if (parsed.pathname === "/cgi-bin/user/list_id" && parsed.searchParams.get("access_token") === "contact-token") {
expect(init.method).toBe("POST");
expect(JSON.parse(init.body)).toEqual({ cursor: "", limit: 2 });
return jsonResponse({
errcode: 0,
userid: "oa_smoke002",
name: "测试员工",
mobile: "19912345678",
email: "[email protected]",
department: [1],
enable: 1,
dept_user: [
{ userid: "oa_smoke002", department: 1 },
{ userid: "oa_smoke002", department: 2 },
],
next_cursor: "",
});
}
if (parsed.pathname === "/cgi-bin/hr/get_staff_info" && parsed.searchParams.get("access_token") === "hr-token") {
if (parsed.pathname === "/cgi-bin/user/get" && parsed.searchParams.get("access_token") === "contact-token") {
return jsonResponse({ errcode: 48009, errmsg: "api forbidden for contact assistant" });
}
if (parsed.pathname === "/cgi-bin/hr/get_staff_info" && parsed.searchParams.get("access_token") === "agent-token") {
expect(init.method).toBe("POST");
return jsonResponse({ errcode: 0, staff_info: [{ fieldid: "在职状态", value: "在职" }] });
return jsonResponse({
errcode: 0,
staff_info: [
{ fieldid: "name", value: "测试员工" },
{ fieldid: "mobile", value: "19912345678" },
{ fieldid: "email", value: "[email protected]" },
{ fieldid: "department", value: "研发部" },
{ fieldid: "在职状态", value: "在职" },
],
});
}
return jsonResponse({ errcode: 40001, errmsg: "unexpected mock request" });
};
const result = await fetchWecomHrProfiles({
config: {
wecomCorpId: "corp-whms-fallback",
wecomCorpId: "corp-wapi",
wecomAgentId: "1000002",
wecomContactSecret: "contact-secret-whms-fallback",
wecomAgentSecret: "agent-secret-whms-fallback",
wecomHrAssistantSecret: "hr-secret-whms-fallback",
wecomContactSecret: "contact-secret-wapi",
wecomAgentSecret: "agent-secret-wapi",
wecomHrAssistantSecret: "deprecated-hr-secret-should-not-be-used",
wecomContactDefaultDepartmentId: "1",
},
userids: ["oa_smoke002"],
limit: 1,
userids: [],
limit: 2,
fetchImpl,
});
expect(result.tokenSource.contact).toBe("WECOM_AGENT_SECRET_FALLBACK");
expect(result.tokenSource.hr).toBe("WECOM_HR_ASSISTANT_SECRET");
expect(result.tokenSource.contact).toBe("WECOM_CONTACT_SECRET");
expect(result.tokenSource.hr).toBe("WECOM_AGENT_SECRET");
expect(result.profiles).toHaveLength(1);
expect(result.profiles[0].externalUserId).toBe("oa_smoke002");
expect(result.profiles[0].name).toBe("测试员工");
expect(result.profiles[0].missingRequiredFields).toEqual([]);
expect(calls.some((call) => call.path === "/cgi-bin/department/list" && call.token === "contact-token")).toBe(true);
expect(calls.some((call) => call.path === "/cgi-bin/department/list" && call.token === "agent-token")).toBe(true);
expect(calls.some((call) => call.path === "/cgi-bin/user/list_id" && call.token === "contact-token")).toBe(true);
expect(calls.some((call) => call.path === "/cgi-bin/user/get" && call.token === "contact-token")).toBe(true);
expect(calls.some((call) => call.path === "/cgi-bin/hr/get_staff_info" && call.token === "agent-token")).toBe(true);
expect(calls.some((call) => call.secret === "deprecated-hr-secret-should-not-be-used")).toBe(false);
});
});