a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
9.4 KiB
9.4 KiB
AI 合同平台 V2 Phase 3 UI 缓存字段 cleanup 设计评审
当前基线
本设计继续对齐《AI 合同平台 V2 下一阶段开发优化方案》的 Phase 3 schema 硬化方向,目标是把 contracts 表中的旧 UI 缓存字段从“历史兼容缓存”逐步治理到“可安全停写、可选删列”的状态。
截至本记录:
- 远端 smoke PostgreSQL:
192.168.3.90 - Contract API:
http://192.168.3.90:18100 - Alembic actual/head:
20260630_0021 display_cache_consistency.status:okchecked_rows:12010mismatch_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 环境已经处于 cleanup migration 前的稳定状态:旧列仍保留,但值已与派生规则一致。
字段分层判断
目标字段:
contracts.status_labelcontracts.status_tonecontracts.next_actioncontracts.next_actions
分层结论:
status_label/status_tone是纯展示字段,可以完全由contracts.status派生。next_action/next_actions不是纯展示字段,当前混合了三类含义:- 状态默认动作,例如待提交、待审批、已审批。
- 结构化上下文动作,例如审查任务、审批实例、条款修复、履约任务。
- 少量人工/业务上下文提示,例如合同编辑页仍允许编辑
next_action。
因此不建议直接创建一次性 migration 删除四列。更稳的路径是先把 status_label/status_tone 和 next_action/next_actions 拆开治理。
当前仍在写旧列的位置
合同主写路径
persistence/contract_core.py- 创建合同时仍从
ContractCreateData写入status_label/status_tone/next_action/next_actions。 - 更新合同时仍允许更新
next_action/next_actions。
- 创建合同时仍从
状态流/审计/时间线
persistence/audit.pytransition_contract与transition_contract_in_session仍将状态流参数写回四个旧列。
sqlalchemy_transition_orchestration.py- 状态流仍传递四个展示字段。
sqlalchemy_approval_orchestration.py- 审批提交、重提、通过、退回仍提供展示字段。
审查/文件/条款修复
sqlalchemy_review_orchestration.py- 审查开始、完成、失败后仍写
status_label/status_tone/next_action/next_actions。
- 审查开始、完成、失败后仍写
sqlalchemy_file_orchestration.py- 文件上传后仍写
next_action/next_actions。
- 文件上传后仍写
sqlalchemy_clause_repair_orchestration.py- 条款修复后仍写
next_action/next_actions。
- 条款修复后仍写
支撑路径
sqlalchemy_support_orchestration.py- 归档动作仍写展示字段。
seed_data.py- demo/seed 数据仍包含旧字段。
in_memory_repository.py- 测试/demo 内存仓储仍维护旧字段。
不属于本轮删除目标的字段
review_tasks.status_label:这是审查任务自己的状态标签,不是contracts表 UI 缓存列。contract_search_documents.status_label:这是搜索索引/搜索展示冗余字段,后续可以单独治理,但不应与contracts表删列混在同一刀。- OpenAPI 和前端 TypeScript 响应字段:仍应保留兼容,因为前端合同列表、详情、搜索、dashboard 仍依赖这些响应字段。
当前读路径状态
读路径已经基本完成派生化:
- 合同列表 mapper 使用
derive_contract_display。 - 合同详情 mapper 使用
derive_contract_display。 - 搜索文档同步/重建使用
derive_contract_display。 ContractActionContext已纳入审查、审批、条款修复、履约任务等上下文。runtime/schema-check已复用同一套 display cache governance 逻辑做只读一致性检查。
这意味着 API 响应可以继续返回 status_label/status_tone/next_action/next_actions,但来源应逐步变为 serializer/domain 派生,而不是依赖数据库旧列。
推荐 cleanup 顺序
Slice 3.5.1:停写前设计锁定
目标:
- 保留数据库旧列。
- 保留 API/OpenAPI/前端字段。
- 明确
next_action/next_actions中人工提示的归属。
建议动作:
- 审计前端合同编辑页的
next_action输入是否仍是产品需求。 - 如果仍需要人工提示,新增事实字段或结构:
- 方案 A:
contracts.action_hint文本字段。 - 方案 B:
contract_action_overridesJSON/子表。 - 方案 C:完全移除人工编辑入口,只保留结构化派生动作。
- 方案 A:
- 不在此片写 migration。
验收:
- 形成明确决策:
next_action人工提示是保留、迁移还是废弃。 schema-check仍为ok。
Slice 3.5.2:停止写 status_label/status_tone
目标:
contracts.status_label/status_tone不再由业务编排手写。- 仍保留列,用于兼容、回滚和 schema-check。
建议动作:
- 创建集中 helper,例如
apply_contract_display_cache(row, action_context=None)或sync_contract_display_cache(row)。 - 对状态流写路径,改为只写事实字段
status,展示字段由 helper/派生层统一刷新。 - 对 create/seed/in-memory 路径,改为由
status派生默认展示字段。 - 增加测试:制造错误旧值后,读路径仍返回派生值;写路径不会再写手写 status label/tone。
验收:
schema-check.display_cache_consistency保持ok。- 合同列表、详情、workspace、搜索抽样通过。
- OpenAPI/前端响应字段不变。
Slice 3.5.3:拆分 next_action/next_actions 语义
目标:
- 将
next_action/next_actions从“旧 UI 缓存列”转成真正的 Action Context 派生。 - 如果保留人工提示,迁移到新事实字段或覆盖结构。
建议动作:
- 将合同编辑页的
next_action输入改名/改语义,避免继续把它当展示缓存。 - 将文件上传、审查任务、条款修复等写路径改成写结构化事实,而不是写
next_action文案。 - 扩展
ContractActionContext,覆盖合同版本/草稿编辑状态。 - 搜索文档继续保留派生后的展示文案,但来源来自 domain/serializer。
验收:
- 写路径不再直接设置
contracts.next_action/next_actions,或仅通过兼容 helper 写入派生结果。 - 旧列一致性检查仍为
ok。 - 前端主要动作按钮展示不退化。
Slice 3.5.4:cleanup migration 设计评审
只有满足以下条件后才进入删列 migration:
- API 响应字段完全由 mapper/serializer 派生。
- 后端业务写路径不再依赖四个旧列。
- 前端不再提交旧 UI 缓存字段作为业务输入。
- 搜索索引重建可以完全从事实字段和派生规则生成展示文案。
- smoke 环境连续多轮
display_cache_consistency为ok。 - 已保留可回滚 snapshot,且 rollback 演练流程可执行。
删列顺序建议:
- 第一片只删除或弃用
status_label/status_tone,因为它们最纯粹。 - 第二片再处理
next_action/next_actions,前提是人工提示语义已经迁出。 - 每片 migration 后都复跑 Docker smoke、schema-check、合同列表、详情、搜索、workspace 抽样。
不建议的做法
- 不建议一次性删除四个旧列。
- 不建议为了“数据库更干净”破坏 OpenAPI/前端兼容字段。
- 不建议在
next_action人工提示归属未决时删除next_action/next_actions。 - 不建议把
contract_search_documents.status_label和contracts.status_label放在同一个 migration 里治理。
当前建议
2026-07-01 已完成完整上下文口径的 display cache prestate 重新固化:
- 旧列
status_label/status_tone/next_action/next_actions仍保留。 display_cache_governance已按完整ContractActionContext判断一致性。- smoke 环境已执行可回滚 backfill,
display_cache_consistency.mismatch_count=0。 display_cache_cleanup_prestate.status=ok。- 详细记录见
project-docs/V2-phase3-display-cache-context-prestate-2026-07-01.md。
下一步最稳仍不是直接写删列 migration,而是继续停写旧 contracts.next_action/next_actions 的生产写路径:
- 让文件上传、审查任务、条款修复、审批流、归档等路径只写结构化事实。
- API 响应继续由
derive_contract_display(...)和完整ContractActionContext派生next_action/next_actions。 - 旧列只作为兼容缓存和回滚面保留,继续由 schema-check 保护。
- 连续多轮 smoke 保持
display_cache_consistency=ok后,再进入 Slice 3.5.4 cleanup migration 设计评审。
这个顺序能避免把“UI 文案缓存治理”和“合同下一步动作业务模型”混成一次高风险 schema 大改,同时保留对旧前端/API 调用方的兼容面。
2026-07-01 停写旧动作缓存列进展
已完成第一片停写:
- 生产 SQLAlchemy 写路径不再刷新
contracts.next_action / contracts.next_actions。 - 旧动作列仍保留,用于兼容、回滚和后续 cleanup migration 前保护。
display_cache_consistency调整为只检查status_label/status_tone与派生状态展示一致。next_action/next_actions的用户可见值继续由derive_contract_display(...)和完整ContractActionContext派生。- 详细记录见
project-docs/V2-phase3-stop-legacy-next-action-writes-2026-07-01.md。
下一步应先部署 smoke 验证,而不是直接删列。