16 KiB
16 KiB
B-2b 统一报销工作台迁移
Goal
把报销从“填写详情页、审批详情页、旧只读详情页分别组织同一业务对象”收敛为唯一的交互式报销工作台。工作台以报销内容为主体,通过 edit、readonly、approval 三个模式适配器注入数据、状态和能力;审批只是叠加在报销对象上的流程与决策能力,不再复制一套报销页面壳。
本任务优先服务当前唯一有真实流转的“报销 → 审批 → 付款”链路。请假/补卡继续以企业微信为主,采购/差旅保持冻结的低频能力,不因追求形式一致而同步重构。
Background and Confirmed Facts
- B-2a 已部署到生产并通过公网真实 bundle 的 PC/移动合成验收,解决了审批人必须跳转才能取得明细、票据和附件的问题。
- B-2a 是经批准的过渡交付:先恢复核心内容可达性并产出
ReimbursementItemEvidenceList、快照投影、离线白名单与 fail-closed 合同;B-2b 负责根据真人目视反馈收敛页面结构。 - 真人目视反馈确认:通用审批壳的流程摘要、报销六字段摘要、新证据区和右侧检查计数存在重复;解释性文字按系统内部状态平铺,审批人需要的“哪条明细、什么问题、是否阻断、如何处理”反而不突出。
ReimbursementDetailPage.tsx与MobileReimbursementDetailPage.tsx已是高状态、高 mutation 的大型页面。继续在其中加入if (approvalMode)会把查询、权限、编辑和审批职责耦合成更难维护的巨型组件。- 项目定位决定采用方案 A:只有报销任务进入业务对象工作台的审批模式;其他 workflow 实体继续使用通用审批详情。
/workflow/tasks/:taskId与/mobile/approvals/:taskId仍是审批深链和任务身份入口;报销任务在任务元数据解析后交给报销审批适配器,不要求用户再跳到另一业务路由。- B-2a 的提交时费用标准/预算快照与当前票据事实仍是两类不同时间语义,不因页面合并而混用。
Entry Conditions
- B-2a 已部署并通过生产
/ready、公网 bundle 与合成业务验收。 - 已取得一次真人目视反馈,并据此冻结方案 A、信息收敛规则和壳/适配器边界。
- 本 PRD、
design.md与implement.md经用户复核批准后,B-2b 才可进入实施。 - 三张真实
rejected单据的员工修改与反馈可以并行推进,不是本任务启动或完成的阻塞条件;反馈如改变已批准产品行为,必须回到规划阶段修订。
Scope
- 建立 PC/移动共享内容合同的唯一交互式报销工作台;PC 与移动允许使用各自布局壳,但必须消费同一展示模型、区块顺序、语义组件和 capability 契约。
- 建立
edit、readonly、approval三类模式适配器;查询、业务状态、权限判断、mutation、错误恢复和 fail-closed 均归适配器。 /reimbursements/:reimbursementId的参与人填写态和非参与人只读态迁入工作台。/workflow/tasks/:taskId与/mobile/approvals/:taskId的报销任务进入同一工作台的审批适配器;其他实体继续走通用审批详情。- 复用并扩展 B-2a 的明细—票据证据组件、票据抽屉、提交前检查和未归属恢复。
- 删除 PC/移动通用审批页中的报销业务分支,以及被工作台替代的旧报销明细/材料布局。
- 收敛顶部摘要、检查提示、右侧动作摘要和退回/驳回解释。
Product Requirements
R1 一个业务对象只保留一个交互式工作台
- 报销详情的主内容顺序在
edit、readonly、approval三种模式中一致:紧凑报销头 → 明细及所属票据 → 未归属票据 → 补充附件 → 必要检查/差异 → 处理历史或处理进度。 - PC 宽屏使用主列证据 + 右侧单一 sticky 动作区;窄屏和移动采用同一内容顺序的卡片/分区详情布局,动作区转到底部且不遮挡内容。PC 与移动不强求同一份 DOM,不得用密集的
isMobile业务分支把两种布局重新耦合成巨型壳。 /workflow/tasks/:taskId保留为审批入口和浏览器地址。任务解析为reimbursement后由报销审批适配器渲染工作台;不得先渲染通用报销摘要再嵌入工作台,也不得要求用户点击“查看报销单”才能取得核心内容。/reimbursements/:reimbursementId使用同一工作台。参与人处于draft/rejected时获得编辑能力;其他获准查看者只得到只读能力。- “打开完整原始表单”不再作为独立核心内容页面入口;打印/PDF 仍通过专用打印路由提供。
R2 壳与模式适配器必须严格分层
- PC/移动布局壳只接收同一份已经计算完成的展示模型、区块内容和 capability/action slots,分别负责各端布局、响应式结构和语义标题;内容顺序和业务组件合同不得分叉。
- 工作台壳不得调用
useQuery、useMutation或 API;不得持有编辑、审批、权限、状态机、票据上传或错误恢复等业务状态;不得根据mode决定是否发请求。 - 壳内不以
roleCodes、permission code、workflow 状态、报销状态或if (mode === ...)推导业务能力。模式差异由适配器注入了什么数据、capability 和 action slot 体现。 edit适配器拥有参与人判断、draft/rejected状态、表头/明细/票据/附件维护状态和 mutation、提交前检查及 B2 错误恢复。- 任务路由边界唯一获取用于授权和实体分派的 workflow task detail,并把同一份查询结果及受控刷新入口移交给
approval适配器;不得为分派和审批内容各发一次相同 task detail 请求。 approval适配器拥有 workflow task detail 的业务解释、allowed actions、提交快照、审批 mutation、刷新策略、fail-closed、退回引用、处理历史与联网要求;不得获得报销维护 mutation。readonly适配器只执行获准的只读查询和材料查看,不注入新增、编辑、删除、关联、解绑、提交或审批能力。- 共享投影和展示组件接收显式最小字段或纯展示模型,不接收完整 Prisma/API 记录,也不在组件内部判断角色。
R3 通用审批页的报销旧分支必须删除
- PC
WorkflowTaskDetailPage中报销专用的六字段DetailField、报销证据区、提交/当前检查区、报销右栏计数和报销审批动作接线,在新工作台路由生效后全部删除。 - 移动
MobileApprovalsPage中对应的报销详情、证据、检查和报销 fail-closed UI 分支迁入审批适配器后删除。 - 通用审批页只保留请假、补卡、采购、差旅等现有实体的通用投影与动作,不保留休眠报销路径、隐藏兜底或复制文案“以防万一”。
- 最终结构检查必须证明通用审批页不再导入报销证据组件、不再包含报销检查文案,也不再根据
entityType === "reimbursement"渲染业务内容。该检查与壳层禁用 API/权限/模式判断的检查必须实现于scripts/qa/check-ui-design-rules.mjs,由部署 preflight 的--strict门禁执行,不得仅存在于单测。 - 任务类型分派层可以识别
reimbursement并选择适配器;它只负责路由,不得重新实现报销摘要或决策逻辑。
R4 信息必须按审批人决策需要收敛
- 顶部只显示报销单号、状态、报销人、日期、合计;事由作为紧接标题的正文展示。不得再用六张字段卡重复下面已经出现的信息,也不显示“业务类型=报销”等自明字段。
- 主列直接进入明细和所属票据;附件随后展示。相同报销头、事由、票据或检查事实不得在不同区块重复完整表达。
- 正常项不解释、不计数。只有存在异常或人工决策需要时才显示提示,并就近说明“对象、原因、影响、下一步”;不得默认平铺“0 条”状态或仅显示无法定位对象的总数。
- 提交时快照与当前票据事实一致时,不并排展示两个“检查摘要”;只有出现异常、快照缺失或时间语义差异时才显示必要内容。
- 历史提交缺少预算快照时,以低强调度说明“该次提交未留存预算快照”;它不得使用告警视觉冒充当前业务异常,也不得用实时预算结果补写历史。
- PC 右栏只保留当前节点/状态、是否可决策、异常锚点和审批动作。报销合计如已在顶部清晰可见,不在右栏重复;创建/发起/完成等时间进入处理历史或按需展开,不平铺为决策摘要。
- “退回”和“驳回”的后果说明只在用户展开相应动作或进入确认时出现;默认右栏不常驻解释卡。错误和提交结果继续提供明确反馈,不能为了降噪删除必要恢复信息。
- 警告不得只依赖颜色;标题层级、DOM 顺序和读屏顺序必须与视觉顺序一致。
R5 编辑态围绕明细完成票据维护与提交恢复
edit适配器向同一明细—票据组件注入新增、编辑、删除、上传/选择 PDF 发票、关联、解绑和未归属恢复能力。- 员工可在当前明细上下文内查看和维护所属票据;未归属票据直接提供选择明细的恢复入口,不依赖口头操作顺序或跳到独立材料区。
- 整单补充附件继续作为独立证据,不伪造与某条明细的结构化关系。
- 填写过程中持续计算可解释的提交前检查;汇总区只提示仍有多少可操作问题并提供锚点,具体错误就近落到明细或票据。
- 服务端 B2 门禁继续为权威。前端不得因工作台迁移放宽
ocr_failed、manual review、金额超票面、缺票或未归属合同。 - 发生日期不取发票日期;系统不自动决定业务是否应拆分,页面继续按既有业务事实源提示员工和财务人工复核。
R6 审批态保留快照、实时事实与 fail-closed
- 费用标准/预算使用 workflow 提交快照;票据归属、金额证据和附件使用当前实时报销详情。两者不得互相替代。
- 完整报销内容、关键当前检查或提交快照解析未就绪时,通过、退回、驳回、转审和报销离线草稿均不可执行;动作入口除禁用外仍有中文原因。
- 审批适配器只注入查看原件、查看附件、引用到退回意见及 workflow 决策能力,不注入报销维护能力。
- 报销证据继续排除在移动 IndexedDB workflow cache 之外;其他审批类型的离线草稿能力保持不变。
- 已处理任务查看当前报销版本时保留时间语义提示;历史意见和提交快照不被当前内容覆盖。
R7 路由、权限与回退必须保持稳定
- 通用任务路由以一次已授权 task detail 查询选择报销或非报销渲染器,并把该查询结果移交所选渲染器继续使用。非参与人不能从分派错误、报销加载状态或材料错误推断无权对象信息。
- 报销详情、票据、附件、短时材料 URL 和审批动作继续使用既有后端授权、用途审计和状态机;本任务不以纯前端隐藏替代服务端校验。
- PC/移动从待办列表进入、刷新、浏览器返回、深链打开和已处理任务查看均保持可预测;工作台返回目标由入口上下文决定,不形成“返回审批详情 → 又回到同一工作台”的循环。
- 页面级加载失败显示重试和安全返回;单个材料加载失败只影响该材料,不清空已加载证据。
Acceptance Criteria
- AC1:PC 与移动的
edit、readonly、approval使用同一展示模型、区块顺序、明细—票据语义组件和 capability 契约;PC 可使用右栏 sticky 布局壳,移动可使用卡片 + 底部动作布局壳,不要求同一份 DOM,模式差异只来自适配器注入的数据与能力。 - AC2:PC/移动布局壳中
useQuery、useMutation、API 调用、角色/权限判断、workflow/报销状态机判断和if (mode === ...)业务分支均为 0;scripts/qa/check-ui-design-rules.mjs --strict静态扫描并报 error,且该命令作为部署 preflight 执行。 - AC3:
WorkflowTaskDetailPage与MobileApprovalsPage的报销业务渲染分支、六字段摘要、重复检查区和休眠兜底全部删除;通用审批页仍通过非报销回归。 - AC4:报销审批首屏只有一套紧凑报销头;事由、明细、所属票据、附件、异常和历史各有唯一主展示位置,不再出现两套摘要或四处检查计数。
- AC5:无异常时不显示“0 条”检查卡或常驻退回/驳回解释;异常时每条提示可定位到具体明细/票据,包含原因、影响和下一步。快照/实时不一致时才额外显示差异。
- AC6:PC 右栏只含决策所需状态、异常锚点和单一动作区;窄屏/移动动作区不遮挡内容,触控区域不小于 44×44,键盘和读屏顺序正确。
- AC7:员工可在明细上下文完成新增/编辑/删除、PDF 发票添加、关联/解绑、未归属恢复、提交阻断修复和再次提交;服务端 B2 结果仍为最终权威。
- AC8:审批内容或快照失败时所有报销决策 fail-closed;审批适配器没有任何报销维护能力;readonly 适配器没有编辑或审批能力。
- AC9:报销证据不进入 IndexedDB;非报销审批离线草稿基线、PC/移动非报销详情和 workflow 状态机均无回归。
- AC10:参与人/非参与人、
draft/rejected/reviewing、pending/已处理任务和材料权限矩阵通过;没有新增 schema、migration、flag、seed 或生产数据改写。 - AC11:生产部署后使用公网真实 bundle + 合成 API 完成三模式、失败关闭和离线验收;真实员工重提反馈记录后,父任务才允许归档。
Out of Scope
- 请假、补卡、采购、差旅改造成“业务对象页 + 审批模式”;它们继续使用通用审批详情。
/reimbursements/:id/print打印/PDF 页面重构。该页面是格式化交付视图,不是交互式工作台;本任务有意保留其独立布局,但不得把它当作编辑/只读/审批的第四套交互实现,也不得向其中复制工作台 mutation。- 自动从发票推断发生日期、自动判断业务拆分正确性或替财务作真实性结论。
- 结构化退回原因、统计模型、财务代员工编辑、批量改票或新审批状态。
- 修改 workflow 状态机、B2 flag、付款流程、数据库 schema、生产配置或真人单据。
- 为当前冗余页面单独发布临时删减版;信息收敛随 B-2b 独立候选一次交付。
Test Plan
- 结构边界:
check-ui-design-rules.mjs --strict扫描 PC/移动布局壳,禁止 query/mutation/API/角色/状态机/mode 请求分支,并扫描通用审批页报销分支和旧文案为 0;三适配器能力集合另做组件/类型验证。 - 纯函数/组件:统一报销头、明细—票据投影、异常过滤、快照/实时差异、异常锚点和 capability 注入覆盖正常/异常/缺失边界。
- PC/移动 Playwright:三模式内容顺序;无异常降噪;异常就近文案;右栏/底部动作;票据抽屉;未归属恢复;B2 阻断后修复并再次提交;退回引用;深链/返回。
- 权限/状态:participant/nonparticipant × draft/rejected/reviewing;pending/已处理审批;无权 task、跨单材料、无维护能力和并发关联回归。
- 离线:直接读取 IndexedDB 禁存字段/哨兵值;报销动作和草稿 fail-closed;非报销审批离线草稿仍可形成和同步。
- 后端:原则上不改合同;复跑 B2 门禁、报销详情只读投影、workflow task detail、材料查看审计、票据 bind/unbind/link 和审批动作聚焦测试。
- 前端/治理:typecheck、build、UI strict/self-test、route manifest、相关 Playwright、process contract、Trellis governance、敏感内容检查、docs registry(如改事实源)和
git diff --check。 - 部署:另行批准独立候选;切换前 UI strict;部署后核
/ready、bundle marker、公网文案、三模式合成路径、B2 状态和真人单据状态不变。
Risks and Deferred Items
- 大型旧页面拆分容易在中间阶段形成双实现;允许在开发分支短暂并存,但最终候选必须通过旧分支为 0 的结构门禁。
- task 路由先取元数据再分派可能产生加载闪烁;设计必须用稳定骨架而不是先渲染通用页再切换。
- 打印页继续独立,未来如字段语义漂移可另建“打印投影复用”任务;本轮不扩大交付面。
- 全局错误色 token、部署脚本
--no-deps、.claudegitignore 和其他治理债不并入本任务。