3.9 KiB
3.9 KiB
设计说明
1. 问题与根因
真实创建链路的状态序列为:
资产表单 dirty=true
-> POST /api/assets 成功并返回 asset.id
-> setSavedAssetFormSignature(...)
-> 同一回调内 navigate(/assets/:id)
-> React 尚未提交 saved signature,UnsavedChangesProvider 仍认为 dirty=true
-> useBlocker 拦截导航并打开“放弃未保存的修改?”
-> URL 留在 /assets/new
Playwright 失败 DOM 已出现该确认对话框,因此 await invalidateQueries(...) 可能放大跳转延迟,但不是直接拦截原因。修复必须处理共享 guard 的同步状态边界,不能只交换两个 await 的顺序后碰运气。
2. 设计选择
2.1 共享 post-save 导航入口
在 UnsavedChangesProvider 内复用现有 allowNextNavigationRef,增加语义收窄的同步入口,例如:
runAfterSuccessfulSave(action: () => void): void
并通过专用 hook(例如 usePostSaveNavigation)暴露。入口执行时:
- 同步设置
allowNextNavigationRef.current = true; - 执行一次传入的导航动作;
- 在 microtask 中恢复为 false;
- 不修改、清空或伪造其他表单的 dirty registration。
这个入口只用于服务端已经确认持久化成功后的程序化导航。普通离开继续使用 runGuarded;失败路径不调用该入口。
2.2 资产创建成功顺序
创建分支按以下顺序处理:
清空表单错误 / 记录已保存签名 / 成功反馈
-> runAfterSuccessfulSave(() => navigate(canonicalDetail, replace + from))
-> 后台 invalidate asset list/detail caches
编辑既有资产不发生路由切换,可继续等待其详情/列表缓存刷新。本任务不重构其他 mutation。
关键不变量:
- canonical 导航是成功创建后的用户可见主结果,不能依赖缓存刷新成功。
replace保证历史栈不保留已提交创建页。- post-save bypass 只开放一个 navigation window,不能永久关闭 guard。
- API 失败仍由 mutation
onError处理,表单与 dirty state 保持。
2.3 测试断言
assets-flow.spec.ts继续使用真实 API,验证 POST 成功后 URL、无 unsaved dialog、返回/前进和后续流转。- 聚焦运行既有“资产长表单切回记录列表”用例,证明取消/放弃保护未退化。
procurement-flow.spec.ts同时断言“申请人”字段和EMPLOYEE.name,不再依赖英文角色词。business-detail-routing.spec.ts保持 canonical path、replace来源与移动回落的纯契约覆盖。
3. 证据更新
归档材料按 append-only delta 更新:
08-05-business-detail-routing:保留原 AC 的PAUSED历史,在文末追加 2026-08-07 真实回归 delta,标明该 AC 当前为失败并由本任务修复。08-07-local-database-migration-readiness:保留原 16/10/0 记录,追加分类修正:资产跳转是产品回归;采购英文断言及 8 条登录态/mock 失败属于测试侧。
不回写“当时已通过”,不删除原始发现与时间线。
4. 兼容与风险
- 共享 guard 入口若命名过宽,可能被误用于绕过真实未保存保护;因此 API 必须带 post-save 语义,并在 spec 中限定调用条件。
- 导航先于缓存失效可能让详情页短暂进入正常加载态,但不会丢失创建结果;当前详情路由已有独立实体查询。
- 真实资产/采购 E2E 会写入带 Playwright 标记的本地合成记录;不清理,避免误删开发数据。
- 本任务不碰权限、状态机、API 或数据库,因此不需要 migration/permission reviewer;仍需
trellis-check做共享组件与跨页面一致性复核。
5. 回滚
- 代码回滚:撤回共享 post-save 入口及资产页调用,恢复原 mutation 时序。
- 测试回滚:恢复对应断言,但不得把已证伪的资产 AC 改写为通过。
- 证据 delta 为历史事实,不随代码回滚删除;若修复回滚,应追加新的状态说明。