- 报销一页式审批工作台父任务及 B-2a/B-2b/只读一致性子任务(AC11 依用户 2026-09-27 确认)
- 报销详情材料预览修复及其部署子任务(由候选 59dc5f6 发布覆盖,原步骤未执行,记录保留)
- 资产事前批准纠偏、Trellis 流程对齐、ERP 遗留精简、预算池并发、采购提交超时
- 各任务 prd 顶部新增收口记录,实现均包含于生产 4a5cd8f2;原未勾清单保留不补勾
- B 组与有真实待办的任务保持进行中
Co-Authored-By: Claude Opus 5.5 <[email protected]>
8.8 KiB
8.8 KiB
报销详情权限与材料预览缺陷修复
2026-10-11 收口归档
撤回展示与 EXIF 尺寸修复 46bfa7e0(回填 174cb7ac)随候选 59dc5f6 上线,现包含于当前生产 4a5cd8f2;部署子任务由该发布覆盖。BX-20260828-0017 首图未显示在只读复现中未重现,按原记录保留。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
Goal
修复报销只读查看者误看到“撤回报销”操作,以及带 EXIF 方向信息的图片进入材料合并预览后页面尺寸错误的问题;同时对 BX-20260828-0017 的首张图片未正常显示现象做受控、脱敏、只读诊断,让财务只看到职责内动作,并让图片材料在合并 PDF 中保持正确方向和比例。
Background
- PC 报销详情已经计算
isReimbursementParticipant,但调用ReimbursementActions时未传showWithdrawAction;该组件默认允许在reviewing状态显示撤回,因此具备全量查看权限的财务人员也会看到撤回入口。 - 服务端撤回接口仍按 owner/submitter 做对象资格校验,非参与人会被拒绝;当前问题是前端动作资格投影错误,不是服务端越权或数据损坏。
- 移动报销详情当前没有撤回操作,本轮只修复存在缺陷的 PC 入口,不增加移动操作。
- 材料合并服务先对图片执行 Sharp
rotate()自动应用 EXIF 方向,但页面尺寸读取自旋转前 metadata。方向为 6/8 的手机图片会出现“输出像素已转向、PDF 页面仍按原宽高”的不一致。 - 现有合并预览按发票在前、补充附件在后排序;
BX-20260828-0017有两张 PDF 发票和两张 JPG 补充附件。此前只读核查已确认 JPG 实际为发票图片,但未打开或持久化原件,尚不能仅凭附件列表断定“首张图片未显示”的最终技术原因。 - 现行业务规则要求:只读审批人不得看到编辑、解绑、删除等数据变更动作;图片各占合并 PDF 一页,合并失败时保留逐份原件查看路径。
Scope
- 修复 PC 报销详情中 owner/submitter 与财务只读查看者的撤回动作可见性。
- 修复服务端材料合并时 EXIF 自动旋转后仍沿用输入尺寸的问题,并更新合并器版本。
- 使用合成数据覆盖前后端回归;对
BX-20260828-0017仅执行受控、脱敏、生产只读诊断。 - 保持服务端授权、审批状态机、材料顺序、原件查看与失败降级合同不变。
Requirements
R1 PC 撤回动作资格投影
- PC 报销详情仅在当前用户是报销单 owner 或 submitter 且状态为
reviewing时显示“撤回报销”。 - 财务、审批人、管理员等因
expense.view_all或当前审批职责获得只读详情访问时,不得看到撤回输入框或按钮。 - owner/submitter 在
reviewing状态下继续看到并可使用现有撤回入口;草稿、驳回、已通过、已支付等既有状态行为不变。 ReimbursementActions.showWithdrawAction必须改为无默认值的必填 prop,让遗漏资格投影在 TypeScript 编译期失败,不保留 fail-open 默认行为。- 服务端撤回资格校验、权限矩阵、审批状态机和审计合同保持不变,前端不能把隐藏按钮当作安全边界。
R2 EXIF 图片合并尺寸修复
- 图片经 Sharp 自动旋转、扁平化和 JPEG 规范化后,必须使用实际输出的宽高创建 PDF 页面和绘制图片,不得使用旋转前 metadata。
- 方向为 6/8 的合成 JPEG 应在输出 PDF 中保持正确的纵横方向和比例;无 EXIF 或无需旋转的 PNG/JPG/WebP 行为保持不变。
- 继续执行现有图片像素、页数、输出大小、确定性字节、整包失败和逐份原件降级合同;不引入新依赖、不把合并件当作原件。
- 合并算法的可见输出发生变化后应更新 merger version,避免新旧算法产物共用同一集合缓存身份。
R3 BX-20260828-0017 首图只读诊断
- 在实施获批后,使用既有受控生产只读访问,确认材料实际顺序、MIME、字节/像素元数据、EXIF 方向、合并页数和对应页尺寸;现有事实已将用户所称“第一张图”锁定为合并 PDF 第 3 页,诊断从该页开始,仅在必要时生成短生命周期的本地临时派生预览。
- 诊断不得写生产数据库、报销、审批、付款、对象存储或企业微信;不得把原始票据、图片、完整票号、存储位置、签名 URL、原始 payload 或凭据写入仓库、任务文档或聊天。
- 临时原件或渲染页若确有必要,只能放在明确的安全临时目录,诊断完成即删除;交付只记录脱敏元数据和结论。
- 若证据确认问题由 EXIF 尺寸错误造成,使用 R2 修复覆盖;若属于 PDF
AcroForm、可选内容组、损坏源文件或其他超出当前图片合并边界的原因,只形成证据并返回规划,不临时替换 PDF 引擎或扩大任务。
Acceptance Criteria
- 复用现有
adminMe + ownerId=employee-other-r102 + status=reviewing + scope=all合成场景;管理员作为非参与只读查看者打开报销详情时,页面不存在reimbursement-withdraw和撤回意见输入框。 - 同一状态下,合成 owner/submitter 仍能看到现有撤回入口;其他状态的既有动作投影不变。
- 服务端非 owner/submitter 撤回拒绝合同保持通过,未修改权限矩阵、状态机、工作流或数据库结构。
- 带 EXIF orientation 6 和 8 的合成图片合并后,PDF 页面宽高与规范化输出一致,方向和比例正确;普通横图、普通竖图和 PNG/WebP 回归通过。
- 相同有序材料在同一 merger version 内重复生成时字节完全一致;跨 merger version 允许因修复产生不同字节。材料顺序、页数上限、整包失败和原件降级合同不变。
- merger version 随算法修复更新,新的集合缓存身份不会复用旧算法产物。
BX-20260828-0017首图形成一份不含原件和敏感字段的只读诊断结论,明确材料顺序、问题类别、是否由 EXIF 修复覆盖,以及未覆盖时的下一步。- 前端 typecheck/build、后端聚焦 Vitest/语法检查、相关 Playwright、Trellis 治理和
git diff --check通过。 - 未经另行批准不部署公网;部署与公网检查完成前不告知用户线上可测试。
Out of Scope
- 财务审批页一页式重构、按钮“处理”的改名或待办信息结构调整。
- 费用类型、会计科目、发票—明细关联、票面金额差异门禁、多票拆分、图片发票上传提示或附件用途治理;这些属于 Task B。
- 修改、撤回、退回、审批或付款任何真实报销;重分类或重新上传
BX-20260828-0017的图片附件。 - 修改服务端撤回授权、角色/权限矩阵、审批规则、状态机、schema、迁移或生产配置。
- 引入 PDF.js、Poppler、新 PDF 引擎,或在未重新规划时处理 PDF 表单域/可选内容组兼容。
- 修改
ReimbursementActions现有撤回默认说明文案“申请撤回并补充说明后重提”。 - 对
BX-20260828-0017执行真实单据处置,包括退回、例外接受、重传 PDF、重分类附件或修正票面合计;该单据应按现行规则另行作出业务处理决定。 - 公网部署、生产写入、企微写入和真实付款。
Risks and Constraints
- 隐藏动作只能改善正确性和体验,安全仍由服务端对象资格校验负责。
- EXIF 修复可以解决旋转后尺寸不一致,但不能预先证明
BX-20260828-0017的首图问题只有一个原因。 - 真实材料含敏感信息,诊断必须最小读取、只保留脱敏摘要,并对临时文件 fail closed。
- 现有视觉回归夹具中 owner 视角明确期待撤回按钮可见,修复不能简单删除该操作;需要新增非参与高权限查看者断言。
Test Plan
- 后端聚焦:
cd server && npx vitest run tests/unit/services/reimbursementMaterialPreview.test.js tests/unit/routes/reimbursements.attachments.test.js。 - 后端基础:
cd server && node --check src/services/reimbursementMaterialPdf.js && node --check src/services/reimbursementMaterialPreview.js && npm run prisma:validate。 - 前端:
cd client && npm run typecheck && npm run build。 - Playwright:复用
r102-reimbursement-main-flow.spec.ts的 admin 非参与人全范围夹具增加撤回不可见断言,并保留现有 owner 可见断言;route mock 场景继续阻断 Service Worker。 - 只读诊断:以脱敏元数据核对
BX-20260828-0017材料顺序、EXIF/像素和合并页尺寸,不保留真实材料证据。 - 治理:
node scripts/qa/check-process-contract.mjs --baseline --json、node scripts/qa/audit-docs.mjs --check-registry、node scripts/qa/check-trellis-governance.mjs、git diff --check。