文件
hyb-oa/.trellis/tasks/08-30-reimbursement-expense-policy-invoice-guard-deploy/design.md
T
wangsli 0f9f40389e
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
收紧生产烟测身份治理(08-30-reimbursement-expense-policy-invoice-guard-deploy)
2026-08-30 00:55:08 +08:00

90 行
5.5 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 设计:报销费用政策与票据门禁生产部署
## 发布边界
本任务不再修改业务代码。候选包含父任务业务提交 `6e999d5` 和本部署合同;规划文档提交完成后以最终干净 `HEAD` 冻结,并在外部审批记录写完整 SHA 与 12 位 bundle marker。当前生产 `59dc5f6` 是固定应用回滚基线。
生产切换分为三个独立状态:
```text
B1 部署:新 schema + 主数据 + 表单合同
env hard gate = off(或 env on 但 DB force_off)
DB flag = force_off
B2 effective = false
B2 隔离灰度:同一应用候选
env hard gate = on
DB flag = force_on + department_allowlist + 隔离空部门
B2 只对 smoke employee 生效
B2 全局启用:同一应用候选
env hard gate = on
DB flag = force_on + global
B2 对全局提交生效
```
任何状态跃迁都必须先完成上一状态的验收,并取得对应生产授权。
## 验收身份与数据隔离
- admin smoke:只用于系统设置、非参与人视图和 flag 操作,不代行财务审批。
- employee smoke:从当前 5 人部门迁入已确认零成员的隔离部门,建立合成草稿并验证 owner 合同;终态常驻该部门,不再迁回。
- manager smoke:新建 manager + employee 的 OA-only 专用身份并设为 employee smoke 的直属 manager。普通 employee 没有直属 manager 时,提交会被 `no_manager_found` 阻断,不会走 finance fallback。
- finance smoke:新建 finance + employee 的 OA-only 专用身份,放入隔离部门,用于财务只读投影;不恢复已消失旧账号,不绑定真人身份。因为完整 finance 角色包含付款能力,账号在验收窗口外保持 disabled,每次启停均需生产账号写入授权。
- 真实 finance:仅负责 4 张真人单据的业务退回和重提复核,凭据不交给自动化。
隔离部门改名为“系统测试专用(勿分配员工)”。名称用于降低 HR 误分配概率,但每次灰度仍必须以实时成员集合为准:只允许 employee/manager/finance 三个 smoke,任何第四个成员都使灰度门关闭。
合成材料必须是无真实抬头、号码、账户和业务正文的测试 PDF/图片。成功提交的合成单据通过 owner 撤回回到草稿再删除;阻断场景直接删除草稿。审计记录保留。
## Migration 与 seed
唯一 migration:`20260829120000_reimbursement_expense_policy_invoice_guard`。
- migration 新增向前兼容列并回填两项免票类型。
- `seed:config` 落地费用类型改名、停用、新建、description、requires_invoice 和 B2 DB `force_off` 初值。
- seed 更新路径不写 `account_code_default`,部署前后须按 code 对比,防止既有财务映射丢失。
- deploy init 同时运行 `seed:trialrun`,因此 pre/post 证据必须覆盖 migration 与主数据,而不能只看 `_prisma_migrations`。
## B2 配置数据流
生产环境不由 `SYNC_ENV=0` 自动同步。正确顺序为:
1. B1 init 后确认 DB flag 记录已由 seed 显式创建为 `force_off`,不得接受 `missing_flag_record`,并保存完整快照。
2. 经生产配置授权把 env hard gate 设为 true,重启同一候选。
3. 验证 `envEnabled=true`、`effectiveEnabled=false`、reason=`flag_force_off`。
4. 经灰度授权前实时确认隔离部门只有三个 smoke,并在获批窗口临时启用 finance smoke;随后把 DB 更新为 department allowlist,仅含隔离部门。
5. 执行合成验证;失败恢复调用前快照,优先 `force_off`。
6. 灰度 PASS 后,经全局授权更新为 global force_on;验收窗口结束立即 disabled finance smoke。
flag 更新使用 API 的 expectedVersion 并发控制并自动写审计。关闭 flag 只改变后续行为,不删除已建立的关联。
门禁状态证据使用精确 reason:`env_hard_off` → `flag_force_off` → 名单外 `department_not_allowed` / 隔离部门 `flag_force_on_department_allowed` → 全局 `flag_force_on_global`。普通 flag 列表无法按 actor 完成 strict department 判断,须在受控容器内调用同一 evaluator 做只读诊断,并与详情 DTO 的 `invoiceGuardEnabled` 交叉核对。
## 真实单据处置
B2 全局启用后再退回 4 张真人单据,使员工只重做一次并获得系统关联/金额提示。日期粒度仍由退回说明与财务人工复核承担:
| 单号 | 系统可拦 | 人工要求 |
| --- | --- | --- |
| 0014 | 否 | 5 个日期拆 5 条明细,重提后人工确认 |
| 0016 | 未关联票据 | 至少按 3 个日期拆分并逐票关联 |
| 0017 | 未关联、超票面 | 2 个日期重做、正式 PDF、差额说明 |
| 0018 | 未关联票据 | 修正日期;同日同类可保留一条 |
已付款 0008 与空草稿 0013 不进入处置动作。
## 回滚与补偿
回滚分三层,不能混用:
1. B2 行为异常:DB flag 立即 `force_off`,关联数据保留。
2. 应用异常:确保 B2 off,使用 existing 入口回滚到 `59dc5f6`;不逆 migration。
3. B1 主数据错误:应用回滚不会恢复 seed 结果。只在单独批准后,依据部署前快照对受影响 code 做精确补偿;不执行整库 restore,不删除新增类型或历史外键。
回滚后重复 build、migration status、runtime/external smoke、挂载、备份合同与公网入口检查。
## 证据与隐私
报告只保存 PASS/FAIL、SHA、migration 名、类型 code/状态、flag 状态、合成标识和计数。不得保存密码、token、完整账号、真实票据、姓名、业务正文或原始 API payload。材料预览只记录页数、页序和比例,允许保留系统生成的审计记录。