- 报销一页式审批工作台父任务及 B-2a/B-2b/只读一致性子任务(AC11 依用户 2026-09-27 确认)
- 报销详情材料预览修复及其部署子任务(由候选 59dc5f6 发布覆盖,原步骤未执行,记录保留)
- 资产事前批准纠偏、Trellis 流程对齐、ERP 遗留精简、预算池并发、采购提交超时
- 各任务 prd 顶部新增收口记录,实现均包含于生产 4a5cd8f2;原未勾清单保留不补勾
- B 组与有真实待办的任务保持进行中
Co-Authored-By: Claude Opus 5.5 <[email protected]>
4.7 KiB
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 特殊内容兼容仍未被证据确认,且更换渲染链会改变确定性、资源上限和签名事实边界。