# Prototype Review

- 审查目标：`src/prototypes/contract-template-management`
- 用户资料/参考资料：
  - 本轮用户任务：对「合同模板管理」原型执行 Prototype Review / 需求评审
  - `src/resources/contract-template-management/PRD.md`（v1.2）
  - `src/prototypes/contract-template-management/index.tsx`、`ContractTemplate.jsx`
  - `contract-template-store.js`、`contract-template-catalog.js`、`contract-template-section-html.js`、`contract-template-section-filter.js`、`contract-template-seed.js`
  - 下游消费：`lease-contract-management/lease-contract-preview-build.js`、`LeaseContractCreate.jsx`
  - `annotation-source.json`、`.spec/prototype-comments.json`
- 生成时间：2026-07-07

## 总体点评

合同模板管理原型在**主业务链路上已基本成立**：列表筛选与嵌套章节子表、草稿/发布/启用停用、版本日志与快照查看、编辑页 9 章节 Tab、锁定/风控/条件条款工具、离开未保存确认，以及经 `contract-template-store` 向租赁合同创建贯通预览，均能在同浏览器 `localStorage` 会话内端到端演示。这与 PRD v1.2 对「Word 文档级模型 + 启用态控制 + 跨模块消费」的定位一致。

当前最影响「按真实法务作业验收」的缺口集中在 **Word 导入仍为文件名驱动的 Mock**，无法证明 PRD §9 对任意 `.doc/.docx` 的拆分策略；其次是 **标注/内嵌 PRD 与 v1.2 字段口径仍有残留不一致**，以及租赁预览侧 **条件条款存在模板 HTML 与硬编码注入双轨**。权限、存储失败、正式 API 与异常分支属原型已知边界，应在研发阶段补齐，但不阻断当前演示型原型价值。

## P0-P3 优先级问题

### P1 - Word 导入未实现真实解析与章节拆分

- 证据：`ContractTemplate.jsx` 中 `handleImportWord` 仅调用 `mockWordBlocks(file.name, categoryFilter)`，按文件名关键字返回内置 `v266LeaseBlocks` / 试用合同块或通用占位块，未读取上传文件内容，也未调用 PRD §9 所述 `sanitizeWordExportHtml` + 标记切分流程。
- 影响：法务无法在本原型中验证「上传任意 Word → 自动拆成 9 章」这一核心维护路径；验收标准 §16.2「导入 Word 后 9 章节正确拆分」仅对少数种子文件名成立，与 PRD 表述的业务成立条件存在明显差距。
- 修复方向：原型层至少接入 `.docx → HTML` 解析（或明确标注为演示 Mock 并下调 PRD 验收项）；研发按 §9.2–§9.4 实现服务端/前端拆分，并在导入失败时给出可恢复提示（重试、保留原稿、查看错误原因）。

### P2 - 标注与内嵌 PRD 仍使用「合同名称」，与 v1.2「合同类型」冲突

- 证据：`annotation-source.json` 列表筛选节点 `ct-list-filter` 的 `annotationText` 与 `markdownMap.ct-list-filter` 仍写「合同名称 / 选项来自模板名称」；而 `PRD.md` v1.2 §7.2、§7.4 与实现中筛选字段 `keyword`、列表列 `contractType`、编辑区「合同类型」均已统一为 **合同类型**。
- 影响：评审、测试与业务方按标注操作时会对筛选对象产生误解，造成需求口径分裂；与 PRD §16.4「标注与 PRD 一致」未完全满足。
- 修复方向：运行或更新 `scripts/build-annotation-source.mjs`，将筛选说明改为「合同类型（可搜索下拉，精确匹配 `contractType`）」；同步目录内嵌 PRD 片段版本号至 v1.2。

### P2 - 租赁预览条件条款存在「模板 HTML」与硬编码注入双轨

- 证据：`lease-contract-preview-build.js` 在读取 store 合并 HTML 后仍调用 `injectPrototypeVehicleClauses`；该函数在 `lease-contract-vehicle-clause.js` 中按 `PROTOTYPE_VEHICLE_CLAUSE_DEFS` 与固定 `paragraphMarker` 包裹段落，注释写明「正式环境由模板管理产出」。模板管理侧条件条款则通过编辑区「条件」写入 `data-vehicle-clause` / `data-vehicle-bindings`。
- 影响：法务在模板管理中新设或调整条件条款后，租赁预览对标准 V26.6 类合同可能仍部分依赖硬编码注入点；与 PRD「创建合同时按订单车辆过滤条件条款」的单一数据源目标不完全一致，增加联调与回归风险。
- 修复方向：租赁预览改为**仅**解析模板 HTML 中的条件条款标记；硬编码注入降级为种子数据初始化脚本或 feature flag，并在跨模块验收用例中覆盖「仅改模板、不改租赁代码」场景。

### P2 - 车型主数据未与车辆管理模块打通

- 证据：PRD §11 要求品牌·型号与车辆管理一致；实现中 `ContractTemplate.jsx` / `VehicleBrandModelPicker.jsx` 使用 `LEASE_VEHICLE_BRAND_MODEL_CATALOG`（`lease-order-vars.js` 内置枚举），未读取 `vehicle-management` 主数据或接口。
- 影响：章节「适用车型」绑定与预览模拟车型的选项集可能与正式车辆台账漂移；绑定存的是字符串，正式环境若车型更名会导致历史模板命中失败。
- 修复方向：原型可保留内置枚举但需在 PRD/标注标明；研发对接车辆主数据 API，并约定品牌型号唯一键与变更策略。

### P3 - 权限边界与存储异常未在原型中表达

- 证据：PRD §13 建议法务可编辑/发布、客服只读等角色能力；原型无角色切换或操作拦截。`contract-template-store.js` 的 `writeToStorage` 在 `localStorage` 失败时静默忽略，无用户提示或降级策略。
- 影响：无法演示「启用中不可编辑」之外的组织级权限；极端环境下用户以为已保存但实际未持久化，造成跨页面数据丢失误解。
- 修复方向：原型可增加「法务 / 客服只读」演示开关；存储失败时 `message.error` 并阻止离开编辑页；正式环境对接权限服务与后端持久化。

## 完整性与项目对齐

| 维度 | 结论 |
|---|---|
| 核心用户 | 法务为主、客服/风控间接用户 — 与 PRD §2 一致 |
| 主流程 | 列表 → 新增/编辑 → 保存/发布 → 启用/停用 → 租赁创建选用 — **可演示** |
| 入口/出口 | `index.tsx` + `ContractTemplate.jsx` 列表/编辑双视图；`navigationRef` 与标注目录可切换 — 对齐 |
| 范围边界 | 仅 `lease` 分类业务闭环；`purchase`/`service` 代码预留未暴露 — 符合非目标 |
| 跨模块 | `contract-template-catalog.js` 冷启动种子、`getContractTemplateBaseHtml` 优先 store — 与 PRD §12、§16.3 对齐 |
| 资料冲突 | **待确认**：`annotation-source.json` 筛选字段仍称「合同名称」，与 PRD v1.2 / 源码「合同类型」冲突（见 P2） |
| 项目 metadata | 路由 `/prototypes/contract-template-management`；与 `lease-contract-management` PRD 关联描述一致 |

**已覆盖且与 PRD 一致的主要能力（摘要）**：嵌套列表 9 章节子表及风控/条件条款计数；启用中锁定编辑删除、停用二次确认；发布递增版本号、已发布再发布写 `versionLogs` 且默认停用；合同主体非空与合同类型校验；未保存离开确认；版本快照只读查看；附件章节车型绑定与租赁侧 `applyAttachmentSectionVehicleFilter` 联动。

## 业务逻辑连贯性

- **状态迁移**：草稿（无版本号）→ 发布（有版本号、默认启用）→ 停用（可编辑）→ 再发布（版本递增、写快照、默认停用）— 与 PRD §6.2–§6.3 一致，源码 `handleSaveTemplate` / `toggleTemplateEnabled` 可印证。
- **启用与下游**：`getPublishedEnabledLeaseOptions` 过滤 `published && enabled`；停用后 `LeaseContractCreate` 订阅 store 更新选项 — 连贯。
- **版本号生成**：`generateNextVersionNo` 按 `category === lease` 下全部已发布模板取最大 minor 递增 — 符合 PRD §6.1「同分类下递增」字面规则；若业务期望「每份 Word 文件独立版本序列」，需产品另行确认（当前无冲突证据，记为待确认项）。
- **条件条款命中**：模板编辑预览与租赁预览均遵循「订单车辆任意命中则展示」；模板管理预览未选模拟车型时隐藏条件段落，与 PRD §8.7.3 一致。
- **数据生命周期**：`creator/updater/createTime/updateTime` 在保存时更新；删除仅草稿或已发布且停用 — 符合 §6.5。

## 状态、异常、边界与恢复

| 类别 | 审查结论 |
|---|---|
| 输入校验 | 保存/发布校验合同主体非空、合同类型必填；条件条款弹窗要求至少一个车型 — 已覆盖 |
| 空状态 | 列表无数据、筛选无结果、版本日志为空、编辑区无内容预览引导 — 已覆盖 |
| 操作确认 | 停用、删除、未保存离开 — 有 Modal 确认 |
| 权限失败 | 未实现角色权限 — 见 P3 |
| 导入失败 | 非真实 Word 解析，无格式错误/拆分失败分支 — 见 P1 |
| 存储失败 | `localStorage` 写入失败静默 — 见 P3 |
| 并发/重复点击 | 保存无 loading 锁，快速连点可能重复写入（原型风险低）— P3 |
| 跨页面恢复 | 刷新后 `localStorage` 恢复模板；清空存储后 `ensureContractTemplateStore` 重新种子 — 可恢复 |
| 下游一致性 | 停用模板后租赁侧选项实时收缩；预览读 store `sectionContents` — 已检查通过 |

## 证据与评估说明

- **用户资料优先级**：本轮无额外用户附件；以 PRD v1.2 为业务基线，与 `annotation-source.json` 冲突处已标「待确认」。
- **读取范围**：
  - 规范：`rules/prototype-review-guide.md`
  - 资料：`src/resources/contract-template-management/PRD.md`
  - 源码：`index.tsx`、`ContractTemplate.jsx`（列表/编辑/保存发布/导入/版本日志）、`contract-template-store.js`、`contract-template-catalog.js`、`contract-template-section-filter.js`
  - 下游：`lease-contract-preview-build.js`、`lease-contract-vehicle-clause.js`、`LeaseContractCreate.jsx`
  - 标注：`annotation-source.json`；未展开阅读 `canvas.excalidraw` 全量
- **独立评估**：`degraded`（未使用子代理分拆「业务基线」与「原型证据」两轮独立评估，在同一审查中顺序完成）

---

## 修复记录（2026-07-07 后续）

| 优先级 | 问题 | 状态 | 说明 |
|---|---|---|---|
| P1 | Word 导入 Mock | **已修复** | `contract-template-word-import.js` + mammoth 解析 `.docx`；失败回退演示模板并提示 |
| P2 | 标注「合同名称」口径 | **已修复** | `build-annotation-source.mjs` 同步 `annotationText` / `annotationsByElementId` / `markdownMap` |
| P2 | 租赁预览条件条款双轨 | **已修复** | `injectPrototypeVehicleClauses` 在 HTML 已有条件标记时跳过 |
| P2 | 车型主数据未打通 | **已修复（原型）** | `src/common/vehicle-brand-model-catalog.js` 读取车辆管理种子并合并演示车型 |
| P3 | 权限与存储异常 | **已修复（原型）** | `?role=cs` 只读演示；存储失败 `message.error` 且阻止离开编辑页 |
