文件
hyb-oa/.trellis/tasks/08-01-reimbursement-notification-progress-clarity/design.md
T
wangsli 185d64dcd6
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
收紧付款复核资料与阶段编辑(08-01-reimbursement-notification-progress-clarity)
2026-08-02 15:15:01 +08:00

14 KiB

报销通知与处理进度简化:设计

1. 设计目标

用最少改动恢复真实待办通知,并让员工看到职责清晰的报销全流程。工作流审批、付款批次和付款执行仍是三个独立状态源;页面只做汇总,不把它们合成一个状态机。

2. 通知只读诊断

按以下顺序诊断目标财务任务,任何一步都只记录布尔状态、计数、固定结果码和脱敏结论:

  1. 从目标报销的最新工作流实例定位主管已完成任务与当前财务任务,不保存内部 ID 到仓库证据。
  2. 以当前财务任务为 target_id 查询 workflow.task_assigned 站内通知,并确认收件人是否为预期财务人员。
  3. 若通知不存在,回到主管审批后的下一任务创建/收件人解析路径;若存在,继续查询 delivery 状态与固定 result_code。
  4. 只核对收件员工 active、notification_push_enabled、企微绑定是否存在,及启用渠道能否匹配 workflow 域和财务角色;不读取 userid、渠道 Secret、消息正文或响应原文。
  5. 只读结论不足以授权配置修改、补发、重启或生产写入,后续动作单独申请。

3. 审批动作标签

  • client/src/lib/approvalDisplay.ts 的 workflowActionLabel() 为唯一共享映射。
  • PC WorkflowTaskDetailPage.tsx 和移动 MobileApprovalsPage.tsx 删除局部副本并导入共享函数。
  • 保持未知动作的原值兜底用于诊断,但所有当前受支持动作必须有中文映射;测试覆盖 start 与 submit。

4. 员工进度表达

  • 后端处理进度阶段键保持兼容,只把普通详情的标签调整为“付款复核”“付款完成”。
  • 付款完成 当前节点/待处理说明明确“由财务登记,非审批环节”;已完成时继续显示付款人、时间及回单是否留存。
  • “查看所在付款批次”只对已有批次读取权限的人显示,普通员工不接触内部批次对象名。
  • 五阶段继续使用一条竖向进度线,因为它表达全流程进展;文案区分审批与付款,不另加 Tab、折叠或第二张流程图。

5. 付款阶段通知

本节实施以第 2 节诊断结论为前置门:若原链路是投递配置问题,付款阶段只新增复用既有 dispatcher 的发送点;若原链路是通知创建或 side-effect flush 的代码问题,先修复并用既有主管→财务通知证明公共路径,再让两个付款发送点复用同一修复。禁止在根因未定时并行新增后再混合排障。

5.1 批次创建后

  • 新建批次事务内创建“待付款复核”站内通知,事务提交后走现有 IM dispatcher。
  • 收件人由 active 员工的真实角色权限计算:必须有 payment.batch.review 且角色包含 manager 或 ceo,并排除批次创建人;不得写死姓名或向所有管理角色泛发。
  • 通知目标是 payment_batch,deeplink 进入现有付款页面的目标批次。

5.2 批次复核后

  • 复核事务内创建“待执行付款”通知,收件人优先为仍 active 且具备 payment.batch.manage 的批次创建人;创建人不可用时,才解析其他具备管理权限的 active 财务人员,并排除当前复核人。
  • 不把付款动作通知给普通申请人;申请人在批次执行成功后继续收到既有 reimbursement.paid。

5.3 合同与幂等

  • 新增明确的付款待办通知类型,不复用“报销已付款”或“工作流待审批”伪装语义。当前付款批次只开放报销实体,因此通知映射到既有 reimbursement domain,复用现有渠道配置;未来若重新开放其他实体付款,再按实体拆分 domain,不在本轮新建需要生产改配的 payment domain。
  • 通知创建与业务状态变更在同一事务内;投递失败不回滚已完成业务动作,但保留 delivery 失败证据。
  • 重复创建/复核请求由既有状态锁拒绝,测试证明不会产生重复通知。

6. 兼容与回滚

  • 不修改数据库 schema、历史审批快照、既有通知记录或付款批次状态。
  • 文案改动可直接回滚;共享映射回滚不会影响服务端事实。
  • 付款通知若出现收件范围错误,关闭对应通知类型/代码路径即可,不撤销已经创建或复核的付款批次。
  • 本轮不合并财务复核与组批;累计 5–10 张真实单后的用户访谈是重新立项条件,不是当前发布门禁。

7. 付款入口与正式常开防复发

7.1 已确认根因

  • 当前生产库 payment_batch_v1 为版本 1,创建时间等于更新时间,updated_by 为空,且没有任何 FEATURE_FLAG_UPDATED 审计。
  • 这证明当前库是在初始化时由 seed 首次创建默认 force_off,不是 seed 覆盖已有值,也不是 QA runner 或控制台在当前库执行了关闭。
  • 当前运行环境同时为 PAYMENT_BATCH_ENABLED=false,因此数据库即使改成 force_on 也会被 env_hard_off 拦截。
  • 当前唯一 active 财务人员具备 payment.batch.manage,但付款入口和已批准候选均因功能门控被隐藏;权限不是根因。

7.2 稳定基线

  • 试运行正式基线把 PAYMENT_BATCH_ENABLED 设为 true,并把 seed 中 payment_batch_v1 的首次创建默认值设为全局 force_on;seed 继续只创建缺失记录,不更新已有记录。
  • 部署/runtime smoke 明确断言付款批次有效开启,防止换库、换 .env 或恢复后再次静默关闭。
  • QA 短窗口保存进入前完整快照并在 finally 恢复该快照;只有明确的正式启用流程在成功时保留 force_on,失败才恢复调用前状态。

7.3 入口规则

  • PC 继续使用现有“付款”导航。
  • 移动财务工作台在付款能力开启时固定显示财务卡片,不再要求先存在批次或异常;卡片进入受同一付款权限保护的 /mobile/payment-batches,复用现有响应式付款页,避免企微小屏把 PC 路径重定向回首页。
  • 入口只对真实 finance/finance_mgr 且具备付款权限的人员显示;不向普通员工或仅审批人开放创建能力。
  • 本轮不自动打开创建面板、不自动选择报销、不代替真实财务动作。

8. 付款批次创建与复核闭环

8.1 已确认的状态事实

  • POST /payment-batches 已在同一事务内生成 batch_no、创建批次与批次项、写创建审计,并创建 payment_batch.review_requested;事务提交后立即进入企微投递。
  • 当前没有 submit API。draft 不是“尚未提交”,而是“已经提交付款复核、尚未被非制单人复核”。继续向用户显示“草稿”会制造一个不存在的提交动作。
  • 不新增状态和接口:数据库继续使用 draft -> reviewed -> executed/failed,用户语义改为 待付款复核 -> 待财务付款 -> 已付款/登记失败。

8.2 KISS 创建合同

  • 创建页只保留候选搜索、报销选择、已选数量和一个主按钮“创建并提交复核”。
  • 服务端 name 改为可选;未传时按 APP_TIMEZONE 生成 YYYY-MM-DD 付款批次。现有调用方显式传入名称时继续兼容。
  • 唯一批次编号继续由服务端生成,不把编号输入暴露给用户;本轮不引入数据库序列表或编号迁移。
  • 未传付款渠道时默认 bank,从而保持银行回单门禁;付款日期不在创建日预填,登记付款完成时未覆盖则按公司时区当天写入。凭证前缀保持可选且默认空。
  • 既有报销批次若历史渠道为空,API 展示和执行门禁按 bank 解释但不回写数据库;因此当前真实批次也会先要求回单,不需要生产数据修正。
  • 日期、渠道、凭证号的异常覆盖继续留在批次项,不让低频字段占据首次创建主路径。

8.3 当前责任与动作

  • 创建成功反馈:已提交付款复核;详情说明:等待负责人复核,制单人不可复核自己的批次。
  • draft:复核责任人显示“负责人”;符合 maker-checker 的复核账号显示主动作“复核通过”,制单人不显示伪装成可用动作的禁用复核按钮。
  • reviewed:财务责任人显示“财务”;页面说明先在线下银行完成付款并上传回单,主动作改为“登记付款完成”。
  • reviewed + bank + 0 份有效回单:唯一主动作是“上传银行回单”,点击直接打开既有文件选择器;上传成功刷新附件计数后,主动作切换成“登记付款完成”。非银行渠道不强制回单,直接显示登记动作。
  • executed:显示“已付款”,不再提供付款动作;failed 显示“付款登记失败”并允许财务按既有规则重试。
  • PC 与移动复用同一响应式页面、状态函数和责任说明,不建立第二套移动状态机。
  • 付款批次状态中文映射放入客户端共享模块,付款列表、详情和其他客户端调用方统一导入;服务端报销处理进度继续输出业务阶段,但用跨层测试断言其“待付款复核”语义与客户端 draft 映射一致。

8.4 单一复核责任人

  • 解析 active、具备 payment.batch.review 且不是制单人的候选人。
  • 先按稳定顺序选择 ceo 一人;不存在时再选择 manager 一人。一个员工同时有多个角色仍只产生一个收件人。
  • 创建事务在写批次之前完成候选解析;没有合格人员时返回可解释的业务错误,保证不会出现“批次已创建但无人能复核”。
  • 无合格人员错误面向财务显示“当前没有可复核人(复核人必须是非制单人且在职),请联系管理员”,并保留固定错误详情供自动化断言。
  • 页面显示职责而不预猜姓名;通知只发给最终选中的真实责任人。历史通知不补发、不删除。

8.5 兼容与回滚

  • 不改 Prisma schema、历史批次或报销事实;既有 draft 批次部署后直接按“待付款复核”展示。
  • 若新创建合同需回滚,客户端可恢复旧表单,服务端仍接受显式名称和可选默认字段;状态机与历史数据不变。
  • 任何真实批次复核、回单上传、付款登记仍由业务人员操作,不纳入部署自动化。

9. 付款详情与日序号收口

9.1 页面信息层级

  • 保留 /payment-batches?id=<batch-id> 的现有显式选择详情,不为低频付款批次增加独立路由。
  • 详情连续顺序固定为:批次概要、当前处理/下一步、报销明细、银行回单。状态和职责先于长明细,避免财务再次寻找“现在该做什么”。
  • 顶部操作组是不变的主动作坐标:draft 的合格非制单人可“复核通过”;reviewed + bank + 无有效回单 为“上传银行回单”;回单具备后为“登记付款完成”。回单章节只承载证据与次要说明,不复制同等级主按钮。
  • 当前责任只显示职责,不预猜未来姓名。状态信息缺失时降级为可理解的“待处理”,不让下游对象缺失打空详情。

9.2 金额单一展示合同

  • client/src/lib/displayFormat.ts 的 formatMoney() 是付款页唯一人民币格式化入口。
  • 列表、移动卡片、详情总额、批次项、复核/付款确认文案和高风险阈值均传原始数值给共享函数;币种字段仍可作为业务事实保留,但 CNY 不再以裸值加代码拼接。

9.3 编号和自动标题

  • 新格式为 PB-YYYYMMDD-NNN,日期使用 fastify.config.appTimezone,序号为当日三位正整数。既有长编号不迁移,导出、AP 与外部交接继续把编号视为不透明字符串。
  • 自动标题只能在取得编号后生成:YYYY-MM-DD 付款批次 NNN。请求显式传 name 时保持原值,不追加序号。
  • 当日序号大于 999 时返回稳定校验错误并回滚,不改成四位或随机后缀,避免格式在边界时静默漂移。

9.4 咨询锁与事务边界

  • 使用 PostgreSQL 双 int4 事务级咨询锁:第一参数为项目登记的固定命名空间“付款批次日序号”,第二参数为公司时区 YYYYMMDD。
  • 在同一个 Prisma 交互事务中按固定顺序执行:取得咨询锁 → 查询当日严格新格式 ^PB-YYYYMMDD-[0-9]{3}$ 的最大序号 → 生成编号/自动标题 → 创建批次及既有项、审计、通知。
  • 不使用 hashtext,不把旧长格式纳入最大值,不在 P2002 后原事务内重试;PostgreSQL 唯一冲突会使事务进入 aborted 状态,任何兜底只能重试整个事务。本设计通过咨询锁消除正常同日并发冲突。
  • 无 schema 迁移;回滚时可恢复旧生成器,已经生成的新短编号仍满足唯一字符串合同,无需改写。

10. 付款复核对象与阶段编辑边界

10.1 状态门禁

  • draft 是已提交待复核,不允许批次项 PATCH、回单上传或回单作废;财务仅可查看和取消,合格非制单人可复核。
  • reviewed/failed 允许财务登记凭证号、实际付款日期以及上传/作废回单;付款渠道已是复核事实,不再接受批次项覆盖。
  • executed/cancelled 的付款资料和回单全部只读;下载继续受既有权限、用途和附件安全门禁保护。
  • 客户端按状态渐进展示当前阶段控件,服务端以同一矩阵拒绝绕过请求。删除 draft 的可编辑路径后,不新增一条“待复核修改审计”来延续旧草稿语义。

10.2 渠道与复核事实

  • 创建面板保留“选择报销”主任务,同时增加一个批次级付款渠道下拉框,默认 bank,请求继续使用既有 defaultPaymentChannel 字段。
  • 渠道和批次金额、收款信息一起构成复核输入;创建后锁定。确需改变渠道时取消未执行批次并重新创建,不在复核后静默改写。

10.3 收款信息 DTO

  • 报销付款对象取 ReimbursementForm.owner,返回姓名、开户银行和经既有加密服务解密后立即脱敏的账户摘要;响应、快照、日志和测试均不得包含完整账号或密文。
  • 新批次把上述低敏摘要写入既有 snapshot_json,不新增 schema;既有批次缺快照字段时按 entity_id 查询当前报销归属人并只读补齐响应,不回写历史批次。
  • 复核详情继续显示单号、摘要、金额和状态,并补充付款渠道与收款摘要;页面四段结构和二次确认弹窗保持不变。