文件
hyb-oa/.trellis/tasks/08-28-reimbursement-detail-material-preview-fixes/design.md
T

4.7 KiB

设计:报销详情权限与材料预览缺陷修复

边界与原则

本任务是两个小型缺陷修复加一项生产只读诊断,不改变业务权限、审批状态或数据模型。前端修复负责把服务端已有的对象资格正确投影为可见动作;后端修复只校正图片规范化结果的尺寸事实。真实材料诊断与代码修复分离,诊断结果不能成为扩大实现范围的默许授权。

R1 动作资格数据流

报销详情 DTO ownerId / submitterId + 当前用户 me.id
  -> isReimbursementParticipant
  -> ReimbursementActions.showWithdrawAction
  -> reviewing 状态下按“参与人 AND 状态”显示撤回
  • 复用 ReimbursementDetailPage 已有 isReimbursementParticipant,不新增页面级权限枚举或 API 字段。
  • ReimbursementActions.showWithdrawAction 改为必填 prop 并移除默认值。全仓库当前只有 PC 报销详情一个调用点,由 TypeScript 强制调用方显式传入参与人资格,后续新增调用也默认 fail-closed。
  • owner/submitter 的既有交互、默认撤回说明、mutation 和反馈不变。
  • Playwright 使用两类合成身份验证:现有 owner 夹具继续看到撤回;复用 r102-reimbursement-main-flow.spec.ts 中 adminMe + ownerId=employee-other-r102 + status=reviewing + scope=all 的非参与管理员夹具断言撤回不可见。管理员覆盖当前 fail-open 权限面的最宽视角,服务端既有 403 测试继续作为真正安全边界。

R2 图片规范化数据流

源 JPG/PNG/WebP
  -> sharp(source).rotate() 应用 EXIF
  -> flatten + JPEG 编码
  -> toBuffer({ resolveWithObject: true })
  -> info.width / info.height(实际输出尺寸)
  -> fitPage
  -> PDF page + drawImage
  • 不再在输出前使用 Sharp 输入 metadata 作为页面尺寸。
  • resolveWithObject 同时返回规范化 buffer 和实际输出 info,避免为了取尺寸再解码一次。
  • 输出宽高仍执行存在性、整数性和 maxImagePixels 检查;输入解码继续由 Sharp 的 limitInputPixels 限制。若 Sharp 输出缺少合法尺寸,沿用 material_preview_image_invalid。
  • 合并器版本从 pdf-v1 升级为下一稳定版本,使集合哈希和 cache key 明确区分旧算法。缓存仍只在进程内、按 grant 生命周期管理,不迁移数据。
  • 测试用 Sharp 生成无敏感内容的方向 6/8 JPEG。除页数和确定性外,读取合并 PDF page size,断言其纵横方向等于自动旋转后的实际输出;保留普通 PNG/JPG/WebP 基线。

R3 真实单据只读诊断

受控生产只读定位业务单号
  -> 冻结有序材料最小元数据
  -> 必要时流式读取材料到安全临时目录/内存
  -> 读取 EXIF 和规范化输出尺寸
  -> 用当前合并器生成临时派生 PDF
  -> 核对页序、页数、目标页尺寸与是否存在图像对象
  -> 删除临时材料
  -> 仅保留脱敏结论
  • 现有顺序与单据事实已确认两张单页 PDF 发票在前、两张 JPG 附件在后,因此用户口中的“第一张图”固定对应合并 PDF 第 3 页;诊断直接从第 3 页开始,同时核对总页数,避免把 PDF 第 1 页误作目标图片。
  • 只在元数据不足以定因时读取最小原件字节;不进行 OCR、不展示内容、不输出完整文件名和存储路径。
  • 诊断记录只允许:材料类别、相对顺序、MIME、脱敏尺寸/方向、合并页数/页尺寸、错误类别和修复覆盖结论。
  • 若现象可由旋转后尺寸不一致完整解释,R2 为修复;若 PDF 页本身缺少可复制内容或存在表单/可选内容依赖,停止在证据结论,另行规划兼容方案。

兼容性与回滚

  • 无 API、schema、迁移、权限或状态机变更。
  • 前端回滚:同时恢复 prop 默认值和调用点合同才会恢复旧投影;正常修复不允许只移除调用点参数,因为必填类型应在编译期阻断该回退错误。服务端安全边界始终不变。
  • 后端回滚:恢复旧图片尺寸读取并回退 merger version。已签发短时 bundle 本就随进程/TTL 失效,无持久化产物迁移。
  • 未部署前仅能报告本地验证与只读诊断结论;公网修复需要单独批准部署并完成构建号和关键路径检查。

关键权衡

  • 选择复用现有参与人布尔值,而不是增加新的通用授权抽象:当前只有一个明确调用缺口,扩大抽象不会增加安全性。
  • 选择读取 Sharp 输出 info,而不是交换 metadata 宽高:EXIF 不只有 6/8,输出事实比枚举推断更可靠。
  • 不在本任务替换 pdf-lib:首图的 PDF 特殊内容兼容仍未被证据确认,且更换渲染链会改变确定性、资源上限和签名事实边界。