文件
hyb-oa/.trellis/tasks/08-05-contract-detail-workspace/prd.md
T

118 行
10 KiB
Markdown

# 合同详情页与高风险动作归位
## Goal
独立建设符合“列表查找、详情处理”的合同工作区,明确合同详情的信息顺序、权限边界、证据表达、终止状态和高风险动作位置;不把从零新增详情能力混入采购/资产的路由搬迁。
## Background
- 当前 `ContractsRegistryPage` 是“列表 + 内联新建/编辑器”,只有 `showEditor/editorId`,没有 selected ID、`?id=` 详情或独立详情态。
- 当前客户端只有合同列表查询、创建、PATCH、续约和台账导出;服务端没有单条详情 GET。
- 当前持久状态只有 `draft/active/archived`,`expired` 是按到期日推导的展示状态;Prisma `status` 为 `VarChar(32)`,新增 `terminated` 本身无需改列类型或回填历史状态。
- 当前合同审计为 best-effort:写入失败只记录服务日志而不阻断业务。终止属于高风险状态变更,原因和审计不得沿用该失败策略。
- 合同可关联项目;`ConfidentialDocumentRef` 可关联合同,但它是涉密资料索引,不是正文文件库。
- `docs/需求-合同附件归档规约.md` 已规划非涉密附件受控入库,并把它列为详情页之后的独立开发步骤。本轮不做附件能力是范围裁剪,不是永久产品边界。
## Scope
- 合同列表改为纯查找入口,详情承担状态、摘要、证据、历史和动作面板。
- 证据区本轮只承载归档位置、离线档案号和涉密资料索引;非涉密附件能力按 `docs/需求-合同附件归档规约.md` 第 13 节第 4 项另建 task。
- 增加 `terminated` 及 `terminated_at / terminated_reason / terminated_by` 三列,终止原因至少 10 个字,状态/终止字段/审计事务一致提交。
- 迁移、状态机、权限和审计变更实施后必须完成只读 reviewer;生产 migration 和部署另行获批。
## Requirements
### R1 页面与信息结构
- 合同列表只负责查找、筛选、状态摘要、到期/续签提醒、台账导出和进入详情;现有内联新建/编辑器随详情工作区落地移除。
- 合同详情信息顺序固定为:`状态与下一步 → 业务摘要 → 证据区 → 变更/操作历史 → 动作面板`。
- PC 动作面板位于详情右侧;移动端如本轮提供详情表达,只保留查看和底部安全区轻动作,不复制 PC 管理能力。
- 建立稳定合同详情路由、单条 GET 合同和客户端 detail query;服务端沿用当前合同范围权限,不扩大普通用户可见范围。
### R2 证据区本轮范围裁剪
- 本轮证据区只展示:归档位置 `archive_location`、离线档案号 `offline_archive_no`、涉密资料索引 `ConfidentialDocumentRef`。
- 必须显示业务引导:“涉密资料请登记线下归档位置”,解释 `archive_location` 非空必填的原因。
- 版式为后续附件类型分区预留扩展位置,但本轮不渲染空附件区,也不在列表显示“附件完整性”列。
- 本轮不新增合同正文或非涉密附件的上传、预览、下载与附件审计;该能力按 `docs/需求-合同附件归档规约.md` 第 13 节第 4 项另建 Trellis task。
- 不得把“不在本轮上传合同文件”写成永久产品边界。
### R3 状态模型
- 本轮状态值域为 `draft / active / terminated / archived / expired`,其中 `expired` 继续由日期推导,`terminated` 为新增持久状态,用户文案为“已终止”。
- “作废”不新增另一状态;已编号或已生效合同不再执行时统一进入“已终止”,合同编号不得释放或复用。
- `terminated` 采用专用字段 `terminated_at / terminated_reason / terminated_by`;终止原因 trim 后至少 10 个字,三项在详情状态区可直接读取。
- 状态不变量固定为:`status = terminated` 当且仅当三个终止字段均非空;其他状态三个字段均为空。服务层在事务内强制该不变量。
- `terminated_by` 关联员工并使用 `onDelete: Restrict`,终止人不得因员工记录变化丢失。
- 已签署、履行中、已完成等状态后置;真实使用证明现有字段不足时另建 task。
- 归档后普通字段锁定;授权修正只能追加变更记录,不覆盖历史事实。
### R4 动作矩阵
- 编辑:常规动作,按当前管理权限和状态开放。
- 续约:常规动作,只在既有允许状态开放,沿用新草稿 + 原合同归档语义。
- 归档/恢复:状态动作;归档后锁定普通字段,恢复及修正必须留痕。
- 终止:高风险动作;原因至少 10 个字,输入区持续显示要求,按钮禁用时明确显示“请填写终止原因,至少 10 个字”;二次确认展示合同编号、相对方、金额和后果,状态写入、三个终止字段与审计必须同一事务成功。
- 本轮不在合同详情放审批通过、驳回、退回或转审;如后续启用合同审批,只展示状态和进入统一审批中心的入口。
- 本轮不在合同详情放付款写动作;只展示授权付款摘要和“查看付款”入口。
- 台账导出继续属于列表筛选范围动作,不变成单条合同“下载”。
### R5 关联、历史与权限
- 跨模块只做“摘要 + 跳转”:项目、采购和付款只展示当前 API/关联模型能证明且当前用户有权查看的摘要;无稳定关联字段时不虚构数据。
- 详情展示合同创建、编辑、状态变化、归档/恢复、续约、终止和归档后修正历史;高风险写操作不得因审计失败而静默成功。
- 普通用户无权访问时不泄露合同是否存在;管理员和授权角色仍受服务端范围约束。
- 深链、刷新、返回、无权限、不存在和 dirty-form 离开均有可解释结果。
### R6 数据迁移、审查与发布
- 新增三个可空字段和 `terminated_by` Restrict 外键的 Prisma migration;历史合同不回填终止事实,新增列允许空值,仅终止接口可原子写入完整三项。
- migration 必须经过 `prisma:validate`、聚焦路由/服务测试、迁移状态检查及既有数据兼容验证;禁止用数据库重置代替迁移。
- 实施完成后启动只读 reviewer,重点检查状态机、权限、事务审计、迁移兼容和隐私;reviewer 不修改代码。
- 生产部署需另行明确批准;部署后必须显式验证 migration 状态、`/ready`、合同列表/详情和权限负向路径,不能只依赖 runbook 或 init 成功日志;失败时停止并按设计回滚。
- migration 为纯增量可空列;代码回滚前如已产生终止记录,先保留审计追溯并评估字段信息损失,再决定是否 drop 三列,不自动执行破坏性 schema 回滚。
## Acceptance Criteria
- [ ] 合同列表不再承载记录编辑器,只保留查找、筛选、提醒、导出和进入详情。
- [ ] 合同详情具有稳定深链,并按“状态与下一步、业务摘要、证据区、历史、动作面板”展示。
- [ ] 证据区展示归档位置、离线档案号和涉密资料索引,并出现“涉密资料请登记线下归档位置”;不渲染空附件区或列表附件完整性列。
- [ ] 本轮没有新增合同正文/非涉密附件上传、预览或下载;PRD 明确其为后续独立能力而非永久禁止。
- [ ] 状态值域支持 `terminated`;终止原因至少 10 个字,操作者和时间可在详情直接读取,合同编号不释放。
- [ ] 服务层与聚焦测试证明 `terminated` 与三个终止字段满足全有或全空不变量,`terminated_by` 使用 Restrict 外键。
- [ ] 终止原因不足 10 字时界面邻近显示禁用原因;满足要求后,确认展示合同编号、相对方、金额和后果,状态、三字段与审计同事务成功或共同失败。
- [ ] 归档后普通字段锁定;授权修正追加记录,不覆盖原历史。
- [ ] 编辑、续约、归档/恢复按权限和状态显示;审批类与付款类写动作不进入合同详情。
- [ ] 项目/采购/付款只做有权限且有真实数据来源的摘要与跳转,不复制对方流程。
- [ ] 无权限详情请求不泄露合同事实;不存在、深链刷新、返回和 dirty-form 行为可解释。
- [ ] 如包含 migration,Prisma 校验、迁移兼容、聚焦后端测试、前端 Playwright、typecheck/build 和 Trellis 门禁通过。
- [ ] 只读 reviewer 完成状态机、权限、事务审计、迁移与隐私审查,且未修改代码。
## Out of Scope
- 本轮不做非涉密附件上传、预览、下载或附件审计;按合同附件归档规约第 13 节第 4 项另建 task。
- 不接入合同审批;如需要按规约第 7 节另建 task 接入统一审批中心。
- 不做电子签章、法务审查、条款库、版本协同或完整 CLM。
- 不新增已签署、履行中、已完成等尚缺事实字段支撑的状态。
- 不复制采购、资产、付款或项目完整流程。
- 不写入生产合同正文、真实附件、完整归档路径或真实业务数据。
## Accepted Risk
- “已终止”是不可逆终态,本轮不提供恢复、续约或再归档路径。必填至少 10 个字的原因、合同编号/相对方/金额/后果确认和事务审计用于降低误操作风险;若后续出现真实误终止案例,另建 task 评估受控管理员修正路径。
## Open Questions
- 无;需求范围与终止持久化方案均已确认。
## Test Plan
- `cd server && npm run prisma:format && npm run prisma:validate && npm run prisma:generate`
- `cd server && node --check src/routes/contractsRegistry.js`
- `cd server && npm test -- tests/unit/routes/contractsRegistry.test.js tests/unit/routes/exports.contractsRegistry.test.js`
- `cd server && npx prisma migrate status`(只读;不得用 reset 或未经批准 deploy)。
- `cd client && npm run typecheck && npm run build`
- 聚焦 Playwright 覆盖合同列表/详情、证据引导、终止原因禁用提示、确认摘要、状态成功/重复终止、归档锁定、权限负向和窄屏动作。
- 实施后启动只读 reviewer,检查状态不变量、专用终止接口、事务审计失败回滚、`terminated` 不参与过期/续约、Restrict 外键、迁移兼容和敏感字段裁剪。
- 运行 task validate、process contract、docs registry、Trellis governance、敏感内容检查和 `git diff --check`。