Files
ai-contract-platform/project-docs/V2-phase3-contract-versions-implementation-2026-06-30.md
T
yuqei a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
release: freeze V2 release candidate
2026-07-12 15:32:24 +08:00

141 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 合同平台 V2 Phase 3 Slice 3.1 合同版本表实施记录
日期:2026-06-30
## 本次范围
本次按 Phase 3 schema 硬化设计评审的 Slice 3.1 实施 `contract_versions`。
已完成:
- 新增 Contract API Alembic migration:`20260630_0019_add_contract_versions.py`。
- 新增 SQLAlchemy model:`ContractVersionModel`。
- Runtime schema check 核心表清单加入 `contract_versions`。
- 合同创建时写入初始版本。
- 合同正文更新时写入新版本。
- 条款推荐/一键修订应用后写入新版本。
- `/api/v1/contracts/{contract_id}/versions` 改为读取真实版本表。
- `/api/v1/contracts/{contract_id}/versions/version-current-{contract_id}` 保留当前版本别名兼容。
- `contracts.body_content` 继续保留为兼容缓存,没有删除旧字段。
- 补充合同版本历史和租户隔离测试。
未做:
- 未删除 `contracts.body_content`。
- 未进入审批模型、风险项结构化、资产发布快照等后续 Phase 3 slice。
## 数据模型
新增表:`contract_versions`
核心字段:
- `id`
- `tenant_id`
- `contract_id`
- `version_no`
- `version_label`
- `body_content`
- `body_hash`
- `source_type`
- `source_id`
- `created_by`
- `change_summary`
- `metadata`
- `created_at`
- `published_at`
核心约束:
- `tenant_id + contract_id + version_no` 唯一。
- `tenant_id + contract_id + created_at` 索引。
- `contract_id + version_no` 索引。
- `source_type + source_id` 索引。
## 兼容策略
本次仍保持 `contracts.body_content` 作为当前正文缓存:
- 详情接口继续返回 `ContractDetail.body_content`。
- 前端合同详情、编辑、版本对比无需改 API path。
- 版本列表返回真实历史版本。
- 旧的 `version-current-{contract_id}` 仍作为当前正文别名保留。
## 验证结果
已通过:
```powershell
python -m compileall -q yuqei-contract-platform/services/contract-api/src/yuqei_contract_api
python -m compileall -q yuqei-contract-platform/services/contract-api/migrations
python -m pytest yuqei-contract-platform/services/contract-api/tests/test_contract_workflow.py::test_contract_versions_endpoint_returns_current_persisted_body yuqei-contract-platform/services/contract-api/tests/test_tenant_isolation.py::test_tenant_isolation_contract_versions -q
python -m pytest yuqei-contract-platform/services/contract-api/tests -q
```
Alembic head 检查:
```text
20260630_0019
```
本地临时 SQLite 空库迁移到 head 已通过,并确认 `contract_versions` 表存在。
权威 V2 smoke PostgreSQL / Docker 环境验收已通过:
```powershell
$env:SSHPASS='<remote-root-password>'
powershell.exe -NoProfile -ExecutionPolicy Bypass -File yuqei-infra\scripts\deploy-v2-smoke-docker.ps1 -UseSshpass -AllowDirtyWorktree -KeepRemoteBackup -SkipSmoke
```
部署结果:
- 远端环境:`192.168.3.90:/opt/apps/ai-contract-platform-v2-smoke`
- Compose project:`yuqei-contract-v2-smoke`
- 部署产物:`project-docs/qa-artifacts/v2-smoke-docker-deploy/2026-06-30T12-26-13`
- 脚本结果:`V2_SMOKE_DOCKER_DEPLOY_PASS`
- Contract API:`http://192.168.3.90:18100` healthy
- Contract Web:`http://192.168.3.90:13000` healthy
- AI Platform API:`http://192.168.3.90:18101` healthy
Runtime schema check 抽样:
```text
status=ok
database=yuqei_contract
backend=PostgreSQL
actual=20260630_0019
expected_head=20260630_0019
core_tables checked_count=17
```
远端 PostgreSQL 抽样:
```text
contract_api_alembic_version = 20260630_0019
contract_versions exists = true
contract_versions total = 12007
smoke contract versions:
smoke-phase3 | contract-d4854c4bd1ec | 1 | v1 | contract_create
smoke-phase3 | contract-d4854c4bd1ec | 2 | v2 | contract_update
```
版本 API 抽样:
```text
POST /api/v1/contracts
PATCH /api/v1/contracts/{contract_id}
GET /api/v1/contracts/{contract_id}/versions
GET /api/v1/contracts/{contract_id}/versions/version-current-{contract_id}
contract_id=contract-d4854c4bd1ec
version_total=2
latest version=v2
initial version=v1
latest body matches patched body=true
current alias body matches patched body=true
```
## 下一步
Slice 3.1 已完成本地和权威 V2 smoke PostgreSQL 验收。建议下一步进入 Phase 3 Slice 3.2:正式审批模型 `approval_instances` / `approval_steps` / `approval_actions` 的设计确认与第一片迁移实现。