文件
hyb-oa/.trellis/tasks/archive/2026-09/08-28-reimbursement-nonparticipant-readonly-projection/design.md
T
wangsli和Claude Sonnet 5 7ad022ce4b
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
补齐验收记录并归档非参与人报销只读投影任务 [08-28-reimbursement-nonparticipant-readonly-projection]
发现并修正文档滞后:业务提交 e8d6766 已确认合入并随后续发布上线(是当前
生产 b1d25d3c 的祖先提交),但 PRD 的 9 条 AC 一直保持未勾选、task.json
备注仍写"未推送、未部署"。implement.md 的服务端/前端验证清单与独立只读
复核记录逐条对应 PRD 各项,现据实补勾 AC 并更正 notes,再归档。

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-25 20:52:12 +08:00

4.7 KiB

设计:非参与人报销详情只读投影修复

设计边界

本任务修正“访问资格”和“维护资格”被混用的问题,不新建报销详情页面、不重构审批工作台,也不改变角色/权限码。详情读取继续支持 owner/submitter、当前审批职责和 expense.view_all;报销人自助写入必须额外通过对象参与资格。

资格模型

可查看详情
  = owner/submitter 的 expense.view_own
  OR 当前审批/职责访问
  OR expense.view_all

可维护报销
  = 当前用户是 owner/submitter
  AND 当前状态允许该动作
  AND 具备对应自助权限码

职责动作
  = workflow 审批/退回、财务付款、付款批次等专用接口
  != 报销人自助维护
  • 前端 PC 与移动端都从同一参与资格推导 canMaintainReimbursement,所有新增、编辑、删除、绑定、解绑、提交、撤回、催办和项目维护入口使用该事实。
  • 只读视图不渲染禁用的维护按钮;它保留可访问的事实信息和材料查看动作,并用“只读查看”标明当前上下文。
  • owner 与 submitter 都是参与人,支持代填/代提交的现有合同;其他角色不因 admin 或全量查看权限获得自助维护资格。

服务端数据流

请求权限码通过
  -> 锁定/读取目标报销
  -> 校验 actor.id == owner_id OR submitter_id
  -> 校验动作状态
  -> 执行业务写入与副作用
  • reimbursements.js 将 ensureApplicantOrAdmin 收紧为无 admin 豁免的参与人检查,统一用于报销头、删除、附件、明细和提交;link-project 补 expense.edit_own,从可查看检查改为参与人写检查,并增加 draft/rejected 状态门。
  • invoicePool.js 的 reimbursement bind/unbind 分支移除 expense.view_all 写入豁免,只保留 owner/submitter。travel order 分支继续按 travel.payment.manage,不受本任务影响。
  • 对象资格失败统一返回 403 PERM_001,并尽量使用稳定的 only_owner_or_submitter 详情码;测试断言事务内没有 update/create/delete、文件落盘、审计、workflow 或通知副作用。
  • ensureOwnOrCanViewAll 继续用于详情、附件原件和材料预览等读取接口,并补到会返回预算、费用标准和风险结果的 precheck;这些读取接口不改为参与人专属。
  • expense.finance.pay 和 workflow 路由是显式职责动作,保持原授权和状态机。

前端角色化呈现

参与人维护视图

  • draft/rejected:保留报销头、明细、项目、发票、附件、提交和删除等现有动作。
  • reviewing:保留撤回和催办;不恢复草稿编辑动作。
  • 其他状态:沿用现有只读和付款结果呈现。

非参与人只读视图

  • 概况区标明“只读查看”,下一步只描述状态事实,不给报销人操作指令。
  • 明细、材料对应情况、处理进度、风险、原件和合并预览继续可见。
  • 发票卡显示实际关联事实:关联明细:...、已关联;没有 linkedItemId 时明确为 未关联,不推断自动生成或匹配结果。已关联 适用于所有当前不可维护视角,不只限于非参与人。
  • 不渲染任何维护控件,避免出现按钮后再依赖 403 纠错。
  • PC 与移动端使用同一文案和资格含义;交互继续使用现有语义化 Button/Link、焦点样式和移动触控尺寸,不引入新的视觉系统。

兼容性与回滚

  • 无 schema、migration、API payload 或角色定义变化;读取 DTO 不变。
  • 权限收紧会移除管理员/财务通过通用详情维护他人报销的旧行为,这是本任务的预期破坏性修正。历史数据不迁移,已经发生的审计记录不改写。
  • 回滚仅恢复本任务前后端代码;回滚会重新开放已确认的不安全资格,因此只有新版本造成更高等级阻断且获得明确回滚批准时才执行。
  • 本任务提交后仍不部署;部署子任务必须冻结新的候选 SHA,不能继续使用 174cb7a。

关键权衡

  • 复用同一详情页面但切换角色化模式,避免复制 PC/移动两套详情和数据查询;资格合同通过共享事实和回归保证,不用 URL 区分安全边界。
  • 只隐藏动作不足以修复问题,因此同步收紧服务端对象资格。
  • 不给 admin 保留隐式后门。若未来确有代维护需要,应新建设计明确、带原因与审计的管理动作,而不是复用员工自助接口。
  • admin 豁免关闭后,离职/长期缺席员工的卡住草稿暂不提供代维护路径;真实需求由 Task B 的专用纠错动作或独立运营修复任务承接。
  • 本轮保留票据和明细事实展示,为财务核验提供上下文;一页式审批的信息结构和金额门禁留给 Task B。