From 68600ec2d5358b9afc6c29e54bb326a6150b42f1 Mon Sep 17 00:00:00 2001 From: shunli Date: Mon, 27 Jul 2026 19:39:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=9D=A8=E4=BF=8A=E6=8A=A5?= =?UTF-8?q?=E9=94=80=E5=B0=B1=E7=BB=AA=E9=85=8D=E7=BD=AE=E4=B8=8E=E4=BC=81?= =?UTF-8?q?=E5=BE=AE=E8=BE=B9=E7=95=8C=E8=AE=B0=E5=BD=95=EF=BC=8807-27-yan?= =?UTF-8?q?g-jun-reimbursement-readiness=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .trellis/spec/hyb-oa/wecom-boundary.md | 85 ++++++++++++++++++ .../check.jsonl | 1 + .../design.md | 87 +++++++++++++++++++ .../implement.jsonl | 1 + .../implement.md | 66 ++++++++++++++ .../prd.md | 61 +++++++++++++ .../task.json | 37 ++++++++ docs/产品口径.md | 7 ++ 8 files changed, 345 insertions(+) create mode 100644 .trellis/tasks/07-27-yang-jun-reimbursement-readiness/check.jsonl create mode 100644 .trellis/tasks/07-27-yang-jun-reimbursement-readiness/design.md create mode 100644 .trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.jsonl create mode 100644 .trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.md create mode 100644 .trellis/tasks/07-27-yang-jun-reimbursement-readiness/prd.md create mode 100644 .trellis/tasks/07-27-yang-jun-reimbursement-readiness/task.json diff --git a/.trellis/spec/hyb-oa/wecom-boundary.md b/.trellis/spec/hyb-oa/wecom-boundary.md index 690d9b87..015816e5 100644 --- a/.trellis/spec/hyb-oa/wecom-boundary.md +++ b/.trellis/spec/hyb-oa/wecom-boundary.md @@ -41,6 +41,91 @@ or administration workflow has already migrated. rule, applicant-visible mobile submission page and actual post-submission approval chain; API template metadata alone is insufficient. +## Scenario: WeCom Member Data Is A Read-Only Source For OA + +### 1. Scope / Trigger + +- Trigger: importing directory members, changing OA employee/profile or manager + routes, enabling directory sync, or adding any WeCom member create/update/disable + call. +- Purpose: keep WeCom as the member-account fact source and prevent an ordinary + OA account-management action from unexpectedly changing the real WeCom directory. + +### 2. Signatures + +- Approved inbound CLI: `npm run import:wecom-directory-export -- --input=- + --dry-run|--apply`; apply retains its existing dual gate and writes OA only. +- OA workflow manager field: `Employee.direct_manager_id`; it is not the WeCom + directory supervisor field. +- Outbound safety gates: `WECOM_CONTACT_SYNC_ENABLED=false` and effective feature + flag `hr_wecom_directory_sync_v1=force_off` for formal internal operation. +- Existing outbound API family `/cgi-bin/user/create|update` and disable payloads + remain unavailable to ordinary OA user/manager maintenance. + +### 3. Contracts + +- A directory export or approved read API may create/update the minimum OA + employee mirror and `WecomIdentityBinding`; it must not create, update or + disable the WeCom member. +- WeCom name, department, supervisor, phone, email and active state are edited by + the member in an allowed WeCom surface or by a WeCom administrator. OA learns + later changes through another approved inbound read/import. +- Setting `direct_manager_id` changes only OA workflow routing. It does not imply + or trigger a WeCom supervisor update. +- An OA account route may record a skipped directory-sync run while the outbound + gate is disabled, but it must stop before token acquisition or an external + member-write request. The skipped state is evidence of zero external writes. +- Any future reverse member sync requires a separate Trellis task, per-field + side-effect design, dry-run, explicit production approval and rollback. It may + not be introduced as an implicit side effect of an existing OA route. + +### 4. Validation & Error Matrix + +- Import input conflicts or is ambiguous -> block the OA write; never merge or + repair the WeCom member automatically. +- OA manager/profile action with outbound gate disabled -> OA transaction may + succeed, outbound sync is `skipped`, zero WeCom access-token/member-write calls. +- Effective outbound flag unexpectedly enabled during ordinary internal operation + -> deployment/operation finding; do not exercise the write path to diagnose it. +- A requested WeCom profile change -> direct the member or WeCom administrator to + WeCom; do not patch the OA mirror and push it outward. + +### 5. Good/Base/Bad Cases + +- Good: administrator exports one existing member, OA imports one `employee` + account and identity binding, then sets an OA-only manager while outbound sync + remains skipped. +- Base: a member later changes phone/email in WeCom; OA keeps the previous mirror + until the next approved inbound refresh. +- Bad: `POST /api/users/batch/set-manager` silently calls `/cgi-bin/user/update`, + or OA profile editing becomes a back door for changing the WeCom directory. + +### 6. Tests Required + +- Directory-export tests prove stdin-only parsing, dual apply gate, employee-only + role and no outbound client invocation. +- User manager/profile route tests assert an effective disabled outbound gate + produces `skipped` before token acquisition/fetch while preserving OA audit. +- Env/feature-contract checks keep the formal default off and reject a production + claim that relies on an unapproved reverse-sync flag. +- A separately proposed reverse-sync task must add mocked API payload assertions, + negative permission/conflict cases and explicit live approval evidence. + +### 7. Wrong vs Correct + +#### Wrong + +```text +OA set-manager/profile update -> WeCom /user/update -> real member changed +``` + +#### Correct + +```text +WeCom member/admin edits source data -> approved inbound read/import -> OA mirror +OA direct_manager_id update -> OA workflow only; outbound member sync skipped +``` + ## Scenario: Compensatory Leave Is A WeCom Leave Category ### 1. Scope / Trigger diff --git a/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/check.jsonl b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/check.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/check.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/design.md b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/design.md new file mode 100644 index 00000000..402f203a --- /dev/null +++ b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/design.md @@ -0,0 +1,87 @@ +# 杨俊报销就绪:技术设计 + +## 1. 设计目标 + +以最小生产变更打通“杨俊企微登录 → 杨俊提交真实报销 → 马建勇审批 → 王芝玲财务处理”。本任务不新增产品能力,复用现有通讯录导入、用户主管写入、报销工作流和异机备份能力。 + +## 2. 已确认边界 + +- 企业微信成员与 OA 员工是两个独立事实:杨俊已在企微通讯录,但生产尚无 OA 员工记录和企微绑定。 +- 单人通讯录输入可唯一筛出杨俊;手机号为空符合现有 nullable 合同,企业邮箱存在,`incomplete=0`。 +- 生产已有 37 个 active 费用类型和 1 个 active 报销工作流定义。 +- active 法体为 0,但当前报销创建、提交和费用控制不以法体为硬门禁,本任务不创建法体。 +- 杨俊的真实直属主管由用户确认是马建勇。 +- 自动备份 daily/watchdog/drill-reminder 与 WARCH3 service 的 `BUILD_SHA` 均已漂移,必须先修复。 + +## 3. 数据流与执行门禁 + +```text +现场只读复核活动构建与 4 个 unit + → 重装 automatic-backup 三项 timer/service 与 WARCH3 timer + → 验证 unit BUILD_SHA、enabled、active + → 手工触发一份真实自动异机备份 + → 验证源端成功与 ARM 恢复点完整/新鲜 + → 从管理员导出通讯录在内存中仅筛选杨俊 + → production dry-run(create=1、conflict/skip/fail=0) + → production apply(初始角色仅 employee) + → 验证 OA 员工 active、企微绑定唯一、其他成员未导入 + → 通过现有用户管理接口设置 direct_manager_id=马建勇 + → 验证主管 active、不同人、具备 expense.approve,并核对审计 + → 杨俊本人企微登录并提交真实报销 + → 马建勇审批,随后进入王芝玲财务处理链路 +``` + +任一门禁失败即停止后续步骤,不用后续成功掩盖前序失败。 + +## 4. 复用合同 + +### 4.1 通讯录导入 + +- 复用 `server/src/scripts/import-wecom-directory-export.js` 的 stdin 输入,不把筛选后的 XLSX 或成员原始 payload 写入磁盘、日志、Trellis 或聊天。 +- dry-run 只记录人数与结果计数。 +- apply 必须同时满足脚本 `--apply` 参数和容器内 `WECOM_DIRECTORY_EXPORT_APPLY=1`。当前 Compose v1 调用必须显式使用 `docker-compose exec -T -e WECOM_DIRECTORY_EXPORT_APPLY ...`,不能只在宿主 shell export。 +- 输入严格只有杨俊一人;成功创建后的初始角色集合严格为 `employee`,不导入其他通讯录成员。 +- 此路径是“管理员导出企微通讯录 → OA 员工/身份绑定”的单向导入。OA 侧主管字段仅服务 OA 工作流;不调用企微成员 create/update/disable,也不把 OA 主管、姓名、部门、手机或邮箱写回企微。 +- 企微成员资料如需变化,由成员本人在企微允许的入口修改,或由企微管理员在管理后台修改;OA 后续只通过重新导出/受控同步读取变化。 + +### 4.2 直属主管 + +- 复用 `POST /api/users/batch/set-manager`,不直写数据库。 +- 写入目标仅杨俊,主管仅马建勇;接口负责 active、自指等现有校验。 +- 写后核对 `SYSTEM_USERS_BATCH_SET_MANAGER` 审计增量,不记录完整账号或原始请求体。 +- 马建勇必须是 active、与杨俊不同且按有效角色解析出 `expense.approve`。 + +### 4.3 报销业务 + +- 杨俊无需先存在差旅申请;`linked_travel_request_id` 可空。 +- 提交仍须满足既有硬门禁:至少一条费用明细、至少一张已绑定且状态为 `bound` 的真实票据、可匹配工作流并能解析审批人。 +- 第一笔真实报销必须由杨俊本人操作;Codex 不代建生产报销、发票、付款或测试业务数据。 + +### 4.4 timer 与备份 + +- 使用现有 `install-automatic-backup-timer.sh` 和 `install-warch3-ops-timer.sh`,从活动部署现场解析并显式传入合同参数;不在任务文档中固化 Secret、宿主路径或完整账号。 +- 重装后核对 4 个 service 的 `BUILD_SHA` 与活动 server 一致,并确认对应 timers 为 enabled/active。 +- 随后手工触发 daily 自动备份路径,验证源端 service 成功、ARM 批次完成且 dead-man/新鲜度检查通过。 +- 新鲜异机恢复点是人员数据写入的前置条件,但不会用于回滚单个人员变更。 + +## 5. 失败与回退 + +- timer 重装或异机备份失败:不写杨俊数据,保留本地诊断并走既有脱敏群告警。 +- dry-run 不满足 `target=1 / active=1 / incomplete=0 / planned create=1 / conflicts=0 / skipped=0 / failed=0`:不 apply,重新核对输入或生产现状。 +- apply 失败:停止;按脚本结果诊断,不手工拼接残缺账号。 +- apply 成功但主管写入失败:保留已创建的 OA 员工和企微绑定,不删除、不恢复整库;主管仍为空时不能提交报销,可安全重试同一主管接口。 +- 主管写错:通过同一接口恢复写入前的 `direct_manager_id`,不恢复整库。 +- 真实报销提交失败:保留草稿和真实票据状态,按返回的业务错误定位;不得通过扩权、代批或伪造数据绕过。 + +## 6. 隐私与审计 + +- 终端与任务证据只保留布尔值、计数、角色 slug、审计 action 和脱敏状态。 +- 不输出手机号、企业邮箱、企微 userid、完整账号、通讯录原始行、票据内容、银行资料、Secret 或完整宿主路径。 +- 账号导入与主管写入分别保留现有系统审计,确保每一步可独立追踪和撤销。 + +## 7. 不做的设计扩展 + +- 不新增导入 API、主管兜底规则、CEO 自审批能力或报销状态。 +- 不启用 OA → 企微通讯录写回,不修改任何企微成员资料。 +- 不创建法体、不补银行账号、不导入其余成员。 +- 不把首次真实业务扩成管理员代办的生产自动化测试。 diff --git a/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.jsonl b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.md b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.md new file mode 100644 index 00000000..5a861549 --- /dev/null +++ b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/implement.md @@ -0,0 +1,66 @@ +# 杨俊报销就绪:执行计划 + +## 1. 生产写入前门禁 + +- [x] 只读复核活动 server 构建标识、Compose 项目、4 个 systemd service 当前 `BUILD_SHA`、timer enabled/active、最近源端备份与 ARM 新鲜度;输出仅限状态和计数。 +- [x] 使用活动部署的显式合同参数重装 automatic backup 的 daily/watchdog/drill-reminder units。 +- [x] 使用活动部署的显式合同参数重装 WARCH3 unit。 +- [x] 验证以下 4 个 service 的 `BUILD_SHA` 全部等于活动 server,相关 timers 为 enabled/active: + - `hyb-oa-automatic-backup-daily.service` + - `hyb-oa-automatic-backup-watchdog.service` + - `hyb-oa-automatic-backup-drill-reminder.service` + - `hyb-oa-warch3-ops.service` +- [x] 手工启动一次 automatic backup daily service;确认源端 `Result=success`、`ExecMainStatus=0`。 +- [x] 在 ARM 只读验证新批次完成、manifest/文件完整性与新鲜度检查通过;任何失败均停止人员写入。 + +## 2. 杨俊单人导入 + +- [x] 从管理员导出的通讯录以管道/内存方式筛选杨俊一人,不生成持久化子集文件,不输出个人字段。 +- [x] 在生产容器运行 dry-run,验收计数:输入 1、active 1、incomplete 0、planned create 1、conflict 0、skip 0、failure 0。 +- [x] dry-run 符合预期,无需进入偏差处理分支。 +- [x] 经本任务最终实施批准后,使用 `docker-compose exec -T -e WECOM_DIRECTORY_EXPORT_APPLY ... --input=- --apply` 写入唯一目标。 +- [x] 只读验证杨俊 OA 员工为 active、企微绑定唯一、初始角色严格为 `employee`,且其余通讯录成员未进入本轮输入或写入。 +- [x] 确认生产企微通讯录写回门禁关闭;本轮同步结果为 skipped,未调用企微成员写接口。 + +## 3. 设置真实直属主管 + +- [x] 写入前记录杨俊原 `direct_manager_id` 的脱敏状态,并确认马建勇为 active、不同员工且具有 `expense.approve`。 +- [x] 通过现有 `POST /api/users/batch/set-manager` 仅将杨俊的主管设置为马建勇;不直写数据库、不调整任何人的角色。 +- [x] 写后验证主管解析成功,并核对 `SYSTEM_USERS_BATCH_SET_MANAGER` 审计增量;该接口合同不递增目标员工 token version 或撤销会话。 +- [x] 主管写入成功,未进入失败或误配回退分支。 + +## 4. 聚焦验证 + +- [x] 运行通讯录导入相关测试,覆盖手机号为空、单目标和 apply 双门禁。 +- [x] 运行 users set-manager 路由测试,覆盖 active、自指、审计和权限边界。 +- [x] 运行 workflow/reimbursement 聚焦测试,覆盖主管解析、`expense.approve`、无差旅申请和绑定票据提交门禁。 +- [x] 运行 `cd server && npm run prisma:validate`;代码未变化时仍将结果作为当前合同基线记录。 +- [x] 运行 Trellis task/governance、流程/文档、敏感扫描和 `git diff --check`。 +- [x] 按项目规则对权限、workflow 和生产写入证据做一次只读 review;Codex inline 主代理复核通过,未发现角色扩权、企微反向写入、审批自审或生产测试业务数据。 + +## 5. 用户真实验收 + +- [ ] 请杨俊本人从企业微信进入 OA,确认身份解析成功并能看到报销入口。 +- [ ] 杨俊使用真实业务数据创建报销,至少添加一条费用明细并上传/绑定状态有效的真实票据后提交。 +- [ ] 确认马建勇能看到该真实审批待办并按业务事实处理。 +- [ ] 审批通过后确认单据进入王芝玲负责的财务处理链路;后续付款批次继续遵守既有 maker-checker。 +- [ ] 用户验收期间不在聊天、截图或任务证据中粘贴票据、金额、银行资料或完整账号。 + +## 6. 完成条件 + +- [ ] PRD 全部验收项有脱敏证据或明确由用户完成的真实业务验收结果。 +- [ ] 本任务未导入其他成员、未创建法体、未补银行资料、未生成测试生产业务数据。 +- [ ] 若只完成系统就绪而真实报销尚未提交,任务保持未完成并明确剩余用户操作,不用合成测试替代。 +- [ ] 完成后归档本任务,返回 R63–R66 产品主线;不从本任务继续长出新的运维加固范围。 + +## 7. 已完成的脱敏证据 + +- timer:4 个 service 构建合同一致,4 个 timer 均 enabled/active。 +- 备份:源端真实批次 `success/0`,ARM 独立 dead-man `success/0`。 +- 通讯录 dry-run:输入 1、active 1、缺手机号 1、incomplete 0、计划创建 1、冲突/跳过/失败 0。 +- 通讯录 apply:创建 1、成功 1、冲突/跳过/失败 0;角色严格为 `employee`,企微绑定唯一。 +- 主管接口:状态 200、更新 1、`SYSTEM_USERS_BATCH_SET_MANAGER` 审计新增 1;企微外部写入 0。 +- 主数据:active 费用类型 37,active 报销工作流定义 1。 +- 本地回归:5 个测试文件、61 项测试通过;Prisma schema 校验通过。 +- 公网检查:`/ready` 为 ok、构建标识匹配,首页 HTTP 200。 +- 只读 review:本轮只写 OA 员工、身份绑定和 OA 主管关系;未修改角色矩阵、workflow 代码、状态机、数据库 schema 或企微成员资料。 diff --git a/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/prd.md b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/prd.md new file mode 100644 index 00000000..45650e32 --- /dev/null +++ b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/prd.md @@ -0,0 +1,61 @@ +# 杨俊报销就绪 + +## Goal + +让已在企业微信通讯录中的杨俊尽快具备真实报销能力:能够通过企微进入 OA、创建并提交出差后的报销,由其真实直属主管马建勇审批,再进入王芝玲负责的财务处理链路;先修复生产 timer 构建合同并取得新鲜异机恢复点,再执行最小范围的账号与主管写入。 + +## Background + +- 杨俊已存在于管理员导出的企业微信通讯录,因此具备企微成员账号;生产只读体检已确认他尚无 OA 员工记录和企微绑定。 +- 上一轮通讯录 apply 只包含两名财务职责目标,明确没有把其余四名成员送入生产输入。 +- 报销是当前急需的真实业务,不等待站内通知、入职向导、审批流简化或月度报表等后续产品项。 +- 生产写入前需同时确认自动异机备份 timer 的构建合同未因最近部署发生漂移。 +- 通讯录单人子集唯一、active、企业邮箱完整、手机号缺失但满足现有 nullable 合同。 +- 当前有 37 个 active 费用类型和 1 个 active 报销工作流定义;active 法体为 0,但代码确认报销创建/提交不读取法体,法体不是本次硬门禁。 +- 当前自动备份 daily/watchdog/drill-reminder 和日常巡检 service 的 BUILD_SHA 均与活动 server 不一致;最近一次备份成功发生在该漂移被今晚任务触发之前,不能视为今晚安全。 + +## Requirements + +- 生产只读体检必须一次性核对:OA 员工、企微绑定、active 状态、角色、直属主管、主管 active/审批权限、可用费用类型、报销功能开关和审批定义;法体仅记录现状,不作为报销阻断项。 +- 同时比较自动备份 systemd unit 中烤入的 BUILD_SHA 与活动 server 构建标识;只输出是否一致,不输出环境文件或宿主路径。 +- 若 OA 账号缺失,复用现有通讯录 stdin 路径只筛选目标员工,先 dry-run,另行获批后 apply;初始角色严格为 `employee`,其余通讯录成员不进入输入。 +- 通讯录导入只允许把企微管理员导出的成员资料写入 OA 并建立身份绑定;OA 不调用企微成员 create/update/disable,不反向修改企微姓名、部门、主管、手机号或邮箱。企微成员资料由本人在企微或企微管理员在管理后台维护。 +- 将杨俊的 `direct_manager_id` 设置为用户已确认的真实直属主管马建勇;写入前仍需只读确认马建勇为不同、active 且具有 `expense.approve` 权限的 OA 员工,不得为了报销绕过审批或扩宽角色。 +- 若费用类型或报销工作流缺失,只补足完成报销所需的最小主数据;不为了本次报销新建法体,不顺带整理全部组织架构或导入其余员工。 +- 写入后只做账号、绑定、主管解析、费用类型和提交流程的安全验证;不创建虚假生产报销、发票或付款数据。 +- 真实报销提交前至少需要一条费用明细和一张已绑定、状态有效的票据;这由员工在真实业务中提供,不用合成数据代替。 +- 最近部署后必须重装自动备份三项 service/timer 和日常巡检 timer,使 unit BUILD_SHA 与活动 server 一致;重装后手工触发一次真实自动异机备份,确认源端成功且 ARM 恢复点新鲜,再允许写入杨俊账号或主管关系。 +- 第一笔真实业务由员工本人正常发起,目标链路为“员工提交 → 真实主管审批 → 财务处理”;付款批次仍遵守既有 maker-checker。 + +## Scope + +本轮范围只包括:修复生产 automatic-backup/WARCH3 timer 的构建合同并取得新鲜异机恢复点;从通讯录中仅导入杨俊为 `employee`;将杨俊的真实直属主管设置为马建勇;验证企微登录和现有报销链路已就绪。所有真实报销内容由杨俊本人提供和提交,所有审批与财务处理由对应真实人员完成。 + +## Acceptance Criteria + +- [x] 只读体检给出脱敏、可执行的缺口清单,并确认 timer BUILD_SHA 是否与活动构建一致。 +- [x] 目标员工在 OA 中为 active,企微绑定唯一,初始业务角色不超过 `employee`。 +- [x] 至少有一个 active 费用类型可用于填单;法体缺失已确认不阻断当前报销创建/提交。 +- [x] `direct_manager_id` 能解析到真实、active 且具有 `expense.approve` 权限的不同员工。 +- [x] 报销相关功能开关和工作流定义可用;无需差旅申请也能创建报销。 +- [x] 自动备份三项 timer 与日常巡检 timer 的 BUILD_SHA 均与活动 server 一致,且 timer 保持 enabled/active。 +- [x] timer 修复后生成一份新的成功异机批次,ARM 侧完整性/新鲜度检查通过;该门禁先于任何杨俊生产数据写入。 +- [ ] 目标员工完成一次真实企微登录并能看到报销入口;主管能看到相应审批入口。 +- [x] 不持久化通讯录筛选文件,不导入其他成员,不产生测试报销、测试发票或测试付款。 +- [x] 本轮没有调用企微成员写接口;企微成员资料未被 OA 反向修改。 + +## Out of Scope + +- 不修改 CEO 本人无主管报销限制,不通过授予 `finance_mgr` 或管理员代批绕过。 +- 不重构报销状态机、审批引擎、企微 OAuth 或权限模型。 +- 不从 OA 创建、更新或停用企微成员;需要修改企微成员资料时,由本人或企微管理员在企微侧处理。 +- 不提前实现 R63–R66,不扩大为全员入职或组织架构治理。 +- 不在未获单独批准时执行通讯录 apply、主管/法体写入、生产报销或付款操作。 +- 不把手机号缺失视为导入阻断项;该员工使用企微或企业邮箱登录,后续按需补手机号。 + +## Test Plan + +- Trellis task/governance、敏感扫描、流程与文档门禁、`git diff --check`。 +- 通讯录聚焦 dry-run 仅输出人数、计划、冲突、跳过和失败计数。 +- 生产只读 SQL/Prisma 只返回布尔值和计数,不输出手机号、邮箱、企微 userid、完整账号或原始 payload。 +- 使用既有报销 workflow/权限聚焦回归验证主管解析和 `expense.approve`,不写生产业务单据。 diff --git a/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/task.json b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/task.json new file mode 100644 index 00000000..ce96ef5e --- /dev/null +++ b/.trellis/tasks/07-27-yang-jun-reimbursement-readiness/task.json @@ -0,0 +1,37 @@ +{ + "id": "yang-jun-reimbursement-readiness", + "name": "yang-jun-reimbursement-readiness", + "title": "杨俊报销就绪", + "description": "修复生产 timer 构建合同并取得新鲜异机恢复点,随后最小化导入杨俊并将真实直属主管设置为马建勇,打通本人报销链路。", + "status": "in_progress", + "dev_type": null, + "scope": "production timer contract repair, fresh off-host backup, single-member WeCom directory import, manager assignment, and reimbursement readiness verification", + "package": null, + "priority": "P2", + "creator": "shunli", + "assignee": "shunli", + "createdAt": "2026-07-27", + "completedAt": null, + "branch": null, + "base_branch": "main", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [ + "deploy/trialrun/install-automatic-backup-timer.sh", + "deploy/trialrun/install-warch3-ops-timer.sh", + "server/src/scripts/import-wecom-directory-export.js", + "server/src/routes/users.js", + "server/src/routes/reimbursements.js", + "server/src/utils/workflow.js", + ".trellis/spec/hyb-oa/reimbursement-invoice-ocr.md", + ".trellis/spec/hyb-oa/approval-workflow.md", + ".trellis/spec/hyb-oa/wecom-boundary.md", + ".trellis/spec/hyb-oa/deployment-trialrun.md" + ], + "notes": "", + "meta": {} +} \ No newline at end of file diff --git a/docs/产品口径.md b/docs/产品口径.md index 633a0657..64e23d40 100644 --- a/docs/产品口径.md +++ b/docs/产品口径.md @@ -66,6 +66,13 @@ - WAPI 后 WHMS/WOUA 读取策略修正为通讯录 `user/list_id` + 可选成员详情 + 人事助手 `get_staff_info`,通讯录用 `WECOM_CONTACT_SECRET`,人事助手用 `WECOM_AGENT_SECRET`,不再靠 fallback 试错。 - WHMS/WOUA-FIX 不代表考勤/审批镜像、OA 应急接管、恢复回写或 RuleOps 已完成;WRA1 后 WRV1 的 `PASS_WITH_CONFIG_GAPS` 只代表运行优先主链路可用且曾有审批/回调配置缺口;WMSG1 只证明消息和 Deeplink 可用;WAPC1 只证明审批配置准备清单已固化;WAPC2 只证明只读 runner 已具备;WAPC2FLIVE 只证明 OA-POC 样件可创建并可读;WCBK1 已证明真实回调 URL 验证、验签/解密和一条现有 OA-POC 测试审批单事件脱敏留证;WACP3R 已证明补偿拉取和审批详情读取可用但曾发现回调证据文件未持久化;WEGOV1 只修正路线和治理;WADM1/WARCH1 已补齐最小自建留存设计和实现。WROAD2-BIZ1 只补充测试账号业务冒烟,WAPPR1 只固定审批人自动带出和禁止申请人任意选审批人的真实试用前门禁。后续仍不能直接扩大到完整 `wecom_attendance_approval_mirror_v1`,需另建远端收口、正式模板外出/加班测试、真实小范围试用、打卡细项、审批规则和权限验收合同。 +### P0-9 企微成员资料单向维护边界 + +- 企业微信是成员账号和通讯录资料的主事实源。姓名、企微部门、企微直属关系、手机号、邮箱、启用/停用等成员资料,只由成员本人在企微允许的入口修改,或由企微管理员在管理后台修改。 +- OA 可以读取管理员导出的通讯录或经批准的企微只读接口,把成员资料导入/镜像为 OA 员工并建立企微身份绑定;该动作只写 OA,不等于创建或修改企微成员。 +- OA 内的 `direct_manager_id` 是 OA 工作流路由事实,不自动回写企微直属关系。两边需要一致时,企微管理员先在企微侧修正,再由 OA 重新读取或受控同步。 +- 正式运行保持 OA → 企微成员 create/update/disable 写回关闭。任何未来确需反向写企微成员的能力都必须另建 Trellis task、逐字段说明副作用并再次获得生产写入批准;不能作为 OA 用户管理接口的隐式副作用。 + --- ## 请假模块