7.3 KiB
AI 合同平台 V2 Phase 3 UI 缓存字段 cleanup prestate 固化
对齐范围
本记录对齐《AI 合同平台 V2 下一阶段开发优化方案》的 Phase 3 schema 硬化方向,承接 Slice 3.4 的 UI 展示字段派生和旧 UI 缓存治理。
当前结论不是进入删列 migration,而是先把“旧 UI 缓存字段仍保留但已一致”固化为 cleanup migration 前状态。
当前 smoke 基线
- 权威 V2 smoke PostgreSQL:
192.168.3.90上的yuqei_contract - Contract API:
http://192.168.3.90:18100 - Alembic actual/head:
20260630_0021 display_cache_consistency.status:okdisplay_cache_consistency.checked_rows:12010display_cache_consistency.mismatch_count:0- 回滚 snapshot:
/opt/apps/ai-contract-platform-v2-smoke/backups/display-cache/display-cache-backfill-candidate-2026-06-30T17-37-24.json - 本地备份:
project-docs/qa-artifacts/display-cache-governance/2026-06-30T17-37-24/display-cache-backfill-candidate-2026-06-30T17-37-24.json
这表示 smoke 数据已经从“旧 UI 缓存存在 8000 条不一致”推进到“旧列仍保留、旧值与派生值一致”的可控状态。
本次工程化固化
新增 runtime schema-check 检查项:
display_cache_cleanup_prestate
检查目标:
contracts.status_labelcontracts.status_tonecontracts.next_actioncontracts.next_actions
检查语义:
- 四个旧列仍存在,且 display cache mismatch 为 0:
ok - 四个旧列仍存在,但 mismatch 不为 0:
warning contracts表缺失:warning,由core_tables继续报error- 任一旧列在正式 cleanup migration 前缺失:
error
新增此检查的原因是:cleanup migration 前,旧列仍是兼容/回滚面;如果有人提前删列,应明确报错,而不是让 display cache consistency 变成模糊的 SQL/连接异常。
Cleanup migration 进入条件
只有同时满足以下条件,才建议设计删列 migration:
- API 响应中的
status_label/status_tone/next_action/next_actions已完全由 mapper/serializer 派生。 - 后端业务写路径不再直接依赖四个旧列。
- 前端不再提交旧 UI 缓存字段作为业务输入。
- 搜索索引重建可以从事实字段和派生规则生成展示文案。
- smoke 环境连续多轮
display_cache_consistency与display_cache_cleanup_prestate均为ok。 - 保留并验证可回滚 snapshot。
建议拆分顺序
- Slice 3.5.1:确认
next_action人工编辑入口是否保留。 - Slice 3.5.2:先停止手写
status_label/status_tone,但保留列和 API 字段。 - Slice 3.5.3:拆分
next_action/next_actions语义,必要时迁移到action_hint或 action override 结构。 - Slice 3.5.4:再进入 cleanup migration 设计评审。
本次验证
python -m pytest yuqei-contract-platform\services\contract-api\tests\test_health.py yuqei-contract-platform\services\contract-api\tests\test_display_cache_governance.py -q
结果:13 个测试通过。
覆盖场景:
- 本地 auto-created schema 中旧列保留且一致,
display_cache_cleanup_prestate=ok。 - 缺少核心表时,prestate 检查只 warning,核心表检查继续负责 error。
- 存在 stale display cache 时,prestate 检查 warning。
- 提前删除旧 UI 缓存列时,prestate 检查 error,并明确指出缺失列。
下一步
下一步仍按 Phase 3 小刀推进:先做 next_action 人工编辑入口的产品语义决策,然后进入停写 status_label/status_tone 的实现。不要直接删除旧列。
Slice 3.5.1 / 3.5.2 实施记录
本次继续沿 Phase 3 cleanup 路径收口,没有进入删列 migration。
next_action 语义确认
审计结果:
- 前端合同编辑面板
apps/contract-web/src/app/contracts/[id]/contract-edit-panel.tsx仍展示并提交next_action输入。 - Contract API 的
ContractUpdateRequest仍允许更新next_action与next_actions。 - 文件上传、审查任务、条款修复、审批实例和履约任务仍会影响
next_action/next_actions的上下文派生。
结论:
next_action/next_actions不能在本片作为纯 UI 缓存清理。- 后续若要删除旧列,必须先决定人工提示是否迁移为
action_hint或 action override 结构。
status_label/status_tone 停写第一片
本次新增统一 helper:
sync_contract_status_display_cache(row)
语义:
- 只根据
row.status派生并写回旧兼容列status_label/status_tone。 - 不处理
next_action/next_actions。 - 旧列仍保留,用于兼容、回滚和 schema-check。
已覆盖的生产 SQLAlchemy 写路径:
- 合同创建:
ContractCoreRepository.add_contract_in_session - 合同状态流:
ContractAuditRepository.transition_contract/transition_contract_in_session - 审批提交、重提、通过、退回:经
SqlAlchemyTransitionOrchestration统一进入派生写入 - 归档:经同一 transition 路径派生写入
- 发起审查:设置
status=reviewing后统一派生写入
本片没有做:
- 没有删除数据库列。
- 没有修改 OpenAPI/前端响应字段。
- 没有移除
next_action前端编辑入口。 - 没有重构 in-memory demo 仓储,因为本片目标是生产 SQLAlchemy 持久化路径停写。
本次验证
python -m compileall -q yuqei-contract-platform\services\contract-api\src\yuqei_contract_api
python -m pytest yuqei-contract-platform\services\contract-api\tests\test_contract_workflow.py yuqei-contract-platform\services\contract-api\tests\test_display_cache_governance.py yuqei-contract-platform\services\contract-api\tests\test_health.py -q
结果:通过。
新增回归:
test_contract_status_display_cache_is_derived_on_write_paths
验证点:
- 真实审批流执行后,直接读取数据库行。
contracts.status_label等于status_label_for_status(row.status)。contracts.status_tone等于status_tone_for_status(row.status)。
下一步
下一步建议先部署到远端 smoke 容器并复跑:
/api/v1/runtime/schema-check- 合同创建、提交审批、退回、重提、发起审查抽样
display_cache_cleanup_prestate
通过后再进入 next_action/action_hint 的语义设计,不要直接删列。
2026-07-01 prestate 重新固化
本记录中的早期 smoke 基线基于“状态默认派生值”检查。2026-07-01 排查详情页日志后,确认 cleanup prestate 必须与真实读路径一致,也就是使用完整 ContractActionContext。
最新 smoke 结论:
- Alembic actual/head:
20260701_0024 display_cache_consistency.checked_rows:12017- 完整上下文口径 backfill 前 mismatch:
12006 - 已执行 backfill apply:
updated_rows=12006 - 回滚 snapshot:
/data/contract-files/display-cache-context-snapshot-20260701.json - backfill 后
display_cache_consistency.mismatch_count=0 display_cache_cleanup_prestate.status=okcontract-api、contract-review-worker、contract-web均为 healthy
同时,contract-api Docker healthcheck 已从重型 /api/v1/runtime/schema-check 调整为轻量 /api/v1/health。runtime/schema-check 仍是 smoke/CI 质量门,但不再作为 5 秒容器探针,避免 12017 行完整上下文检查导致健康状态误报。
详细记录见:
project-docs/V2-phase3-display-cache-context-prestate-2026-07-01.md