1d85329a6a
Build and Test Quality Gate / Contract Web typecheck and build (pull_request) Successful in 9m12s
Build and Test Quality Gate / Isolated Alibaba and Tencent KMS gateway (pull_request) Failing after 4m53s
Build and Test Quality Gate / Python tests and Contract API migration (pull_request) Failing after 24m55s
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 8m42s
165 lines
8.2 KiB
Markdown
165 lines
8.2 KiB
Markdown
# V2 P1-04.1 可审阅 Playbook 兜底合同新版本实施与 UAT 报告
|
||
|
||
日期:2026-08-23
|
||
状态:已完成,隔离 UAT 通过。
|
||
|
||
## 1. 目标与验收结论
|
||
|
||
P1-04.1 的目标是把企业 Playbook 兜底从“直接修改正文”升级为“生成候选版本、审阅差异、明确采用”,确保系统不会在用户没有确认的情况下静默覆盖当前合同正文。
|
||
|
||
验收结论:通过。
|
||
|
||
- 一键生成的是独立候选版本,不会修改 `contracts.body_content`。
|
||
- 候选版本自动打开原文/新文差异审阅界面。
|
||
- “确认采用为当前正文”默认禁用,用户必须勾选明确确认。
|
||
- 采用请求必须同时携带 `confirmed=true` 和已审阅的候选正文 SHA-256。
|
||
- 当前正文、合同状态或候选摘要发生变化时,系统停止采用并转人工处理。
|
||
- 采用成功后原正文仍保留为历史版本,可继续查看、比较和取证。
|
||
|
||
## 2. 两阶段状态机
|
||
|
||
```text
|
||
审查快照命中规则
|
||
|
|
||
v
|
||
生成候选版本 candidate --------> 当前合同正文保持不变
|
||
|
|
||
| 用户打开差异、勾选确认、提交候选哈希
|
||
v
|
||
显式采用 adopted --------------> 候选正文成为当前正文
|
||
|
|
||
+------------------------> 原正文继续保留在版本历史
|
||
|
||
任一状态/正文/哈希冲突 --------> conflict + 审计证据 + 人工接管
|
||
```
|
||
|
||
兼容既有数据:历史 `succeeded` 执行记录继续按已采用处理,不要求回写迁移。
|
||
|
||
## 3. 产品与交互改造
|
||
|
||
### 3.1 生成候选
|
||
|
||
- 原“应用企业兜底文本”按钮改为“生成可审阅新版本”。
|
||
- 一次点击完成规则快照校验、候选正文生成、候选版本落库和审计记录。
|
||
- 生成成功后立即展示红绿差异,并明确提示“当前正文尚未修改”。
|
||
- 关闭差异对话框后,页面显示“候选版本待审阅 / 原正文未修改”。
|
||
|
||
### 3.2 明确采用
|
||
|
||
- 差异对话框同时显示原正文和候选版本的删除/新增内容。
|
||
- 采用按钮默认禁用;只有勾选“我已审阅原正文与候选版本差异”后才可点击。
|
||
- 用户可选择“暂不采用”,候选版本保留,当前正文不变。
|
||
- 采用后显示“兜底版本已采用”,下一步引导为“重新发起审查验证企业兜底文本”。
|
||
|
||
### 3.3 版本历史
|
||
|
||
- 候选未采用时标记“候选待审阅”。
|
||
- 采用后标记“已确认采用”。
|
||
- 版本列表保留原版本与候选版本,可分别查看正文并与当前版本比较。
|
||
- 候选生成或采用后,版本面板通过页面事件立即刷新,无需用户手动刷新页面。
|
||
|
||
## 4. 后端安全边界
|
||
|
||
### 4.1 生成阶段
|
||
|
||
- 锁定当前租户的合同记录。
|
||
- 校验审查任务成功、快照 ID、快照摘要、规则版本和来源正文哈希。
|
||
- 校验合同处于 `draft`、`reviewing` 或 `rejected`。
|
||
- 生成 `playbook_fallback_candidate` 版本与执行台账,不更新当前合同正文。
|
||
- 写入 `playbook_fallback_candidate_created` 审计事件,记录原文与候选正文哈希。
|
||
|
||
### 4.2 采用阶段
|
||
|
||
- 新接口:`POST /api/v1/contracts/{contract_id}/playbook-fallbacks/{execution_id}/adopt`。
|
||
- 仅具备合同编辑权限的账号可以调用。
|
||
- 请求必须提交 64 位候选正文 SHA-256 和字面值为 `true` 的确认字段。
|
||
- 在数据库事务中锁定合同和候选执行记录。
|
||
- 再次校验租户、合同状态、当前正文哈希、候选版本来源和候选正文哈希。
|
||
- 全部通过后才更新当前正文,并把候选版本标记为已采用。
|
||
- 写入 `playbook_fallback_adopted` 审计事件和采用人、采用时间。
|
||
|
||
### 4.3 冲突与幂等
|
||
|
||
- 候选生成使用租户级幂等键,重复请求不会重复创建版本。
|
||
- 已采用候选再次提交采用请求时安全重放,不重复修改正文。
|
||
- 候选生成后如果有人编辑了当前正文,采用操作返回 `conflict`,当前正文保持编辑后的内容。
|
||
- 冲突记录包含 `requires_manual_handoff=true`、当前正文哈希和期望正文哈希。
|
||
- 文件审查仍保持只读,不自动改写原始附件。
|
||
|
||
## 5. 隔离 UAT 证据
|
||
|
||
### 5.1 环境与账号
|
||
|
||
- 租户:`uat-playbook-20260823`(Playbook 隔离 UAT)。
|
||
- 法务/租户管理员:`uat.legal.20260823@example.test`。
|
||
- 独立审批人:`uat.approver.20260823@example.test`。
|
||
- 两个账号为独立用户,报告不记录密码。
|
||
|
||
### 5.2 验收对象
|
||
|
||
- 合同:`contract-a9323e1ded7f`。
|
||
- 标题:`UAT P1-04.1 可审阅兜底版本合同`。
|
||
- 审查任务:`review-045068122a21`。
|
||
- 快照:`playbook-snapshot-045068122a21`。
|
||
- 快照摘要:`4b66f02e671ecfbdb7fd42784aa1db2ad4020ca4654e8998530be10c0f273a4e`。
|
||
- 执行记录:`playbook-fallback-c3c35c792911`。
|
||
- 候选版本:`version-970c07ed14bd`。
|
||
- 最终执行状态:`adopted`。
|
||
|
||
### 5.3 Chrome 可视化验收
|
||
|
||
1. 法务账号创建包含“承担全部且无限责任”的合同并完成 AI 审查。
|
||
2. 页面命中租户规则 `UAT 禁止无限责任`,聚合快照显示 1 条可用兜底。
|
||
3. 点击“生成可审阅新版本”后自动打开差异对话框:
|
||
- 原无限责任表述显示为删除内容;
|
||
- 企业兜底文本显示为新增内容;
|
||
- 页面明确显示“当前正文尚未修改”;
|
||
- 确认采用按钮默认禁用。
|
||
4. 关闭差异对话框后,正文预览仍显示原无限责任表述,证明候选生成没有覆盖正文。
|
||
5. 切换独立审批账号:
|
||
- 可以查看候选待审阅状态和版本证据;
|
||
- 看不到生成、审阅、采用或编辑按钮。
|
||
6. 切回法务账号,打开候选差异并勾选确认,随后采用:
|
||
- 当前正文切换为企业兜底文本;
|
||
- 页面显示“兜底版本已采用”;
|
||
- 下一步提示为重新发起审查。
|
||
7. 版本面板显示版本 1 和版本 2:
|
||
- 版本 2 标记“已确认采用”;
|
||
- 打开版本 1 仍能完整看到原无限责任正文。
|
||
|
||
## 6. 主要实现位置
|
||
|
||
- 状态与请求模型:`yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/enterprise_playbook.py`
|
||
- 候选生成与采用事务:`yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/sqlalchemy_playbook_fallback_orchestration.py`
|
||
- API 路由:`yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/api/review.py`
|
||
- 数据模型:`yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/persistence/models.py`
|
||
- 数据库迁移:`yuqei-contract-platform/services/contract-api/migrations/versions/20260823_0052_add_playbook_candidate_adoption.py`
|
||
- 前端差异与确认:`yuqei-contract-platform/apps/contract-web/src/app/contracts/[id]/apply-playbook-fallback-button.tsx`
|
||
- 版本证据展示:`yuqei-contract-platform/apps/contract-web/src/app/contracts/[id]/contract-version-panel.tsx`
|
||
- Web 采用代理:`yuqei-contract-platform/apps/contract-web/src/app/api/contract-platform/contracts/[id]/playbook-fallbacks/[executionId]/adopt/route.ts`
|
||
- OpenAPI:`yuqei-contract-platform/packages/contract-openapi/openapi.yaml`
|
||
|
||
## 7. 自动化、迁移与构建
|
||
|
||
- 后端核心回归:98 项通过。
|
||
- 新增断言覆盖:候选生成不改正文、未确认请求拒绝、候选采用、采用幂等、租户隔离、审批人无编辑权限、生成前漂移冲突、生成后漂移冲突。
|
||
- 前端测试:全部通过。
|
||
- TypeScript 类型检查:通过。
|
||
- Python `compileall`:通过。
|
||
- OpenAPI YAML 解析:通过。
|
||
- Next.js 生产构建:通过,114 个页面路由完成构建。
|
||
- PostgreSQL 迁移:`20260823_0052 (head)`。
|
||
- 全新 SQLite 数据库从 0 到 `0052` 完整迁移:通过。
|
||
|
||
非阻断告警:测试环境仍有 Starlette `TestClient` 上游弃用提示,以及 Windows `.pytest_cache` 已存在导致的缓存写入提示,不影响测试结果和产品功能。
|
||
|
||
## 8. 当前运行状态与下一步
|
||
|
||
- Web:`http://127.0.0.1:3000`,运行中。
|
||
- Contract API:`http://127.0.0.1:8100`,启用 Web 代理鉴权,运行中。
|
||
- AI API:`http://127.0.0.1:8101`,运行中。
|
||
- Review Worker:运行中。
|
||
- Chrome 保留在合同 `contract-a9323e1ded7f` 的采用后验收页面。
|
||
|
||
建议后续进入 P1-04.2:支持一次审阅多个规则候选、逐条勾选或跳过、组合差异预览,以及候选版本主动撤销;仍应保持“未经明确确认不更新当前正文”的发布门槛。
|