From e9942af37f199523cd61084022eb5e7de0fe06b4 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Wed, 19 Aug 2026 11:15:54 +0800 Subject: [PATCH] docs: record dual-gate storage release review design. Capture production and QC dual approval, three menu entry points, auto store and rollback, and historical data rules before implementation. Co-authored-by: Cursor --- ...-08-19-storage-release-dual-gate-design.md | 323 ++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-19-storage-release-dual-gate-design.md diff --git a/docs/superpowers/specs/2026-08-19-storage-release-dual-gate-design.md b/docs/superpowers/specs/2026-08-19-storage-release-dual-gate-design.md new file mode 100644 index 00000000..8074e821 --- /dev/null +++ b/docs/superpowers/specs/2026-08-19-storage-release-dual-gate-design.md @@ -0,0 +1,323 @@ +# 入库放行审核 · 生产/质检双闸门 + +> 日期:2026-08-19 +> 状态:待审阅 +> 模块:细胞存储 / 生产管理 / 质检管理 → 入库放行审核 +> 关联: +> - `docs/superpowers/specs/2026-05-23-storage-release-review-design.md` +> - `docs/superpowers/specs/2026-07-16-storage-release-review-tabs-design.md` +> - `docs/superpowers/specs/2026-07-29-storage-release-pending-ready-filter-design.md` +> 界面模式:P22(审核列表) +> 参考页:`web-admin/src/views/quality-inspection/storage-release-review/index.vue` + +## 1. 背景与目标 + +### 现状 + +入库放行审核挂在 **市场 → 细胞存储**。一条记录只有一组审核字段;任意有权限的人提交一次「通过」即审核通过,再点「样本入库」才把样本标成已入库。不是会签,也不是多级审批。 + +### 需求 + +客户要求 **生产主管与质检主管都审批通过后,才可以入库**。 + +### 目标 + +1. 同一条 `biz_storage_release_review` 上拆成 **生产侧 / 质检侧** 两套独立结论。 +2. 三个菜单入口共用同一页面:细胞存储只读总览;生产只审生产侧;质检只审质检侧。 +3. 两侧都「通过」后 **自动入库**;任一侧改成「不通过」且当前已入库时 **直接回退出库**(不弹窗)。 +4. 生产、质检页保留「批量入库」,仅补「两侧已通过且尚未入库」。 +5. 历史单人审核结论迁到质检侧;已入库记录保持已入库。 + +### 范围外 + +- 不上工作流引擎、不加第三人审批节点。 +- 不新建第二张审核表、不复制两套列表数据。 +- 不改细胞产品库位占用;回退出库 **不** 动库位里的细胞产品。 +- 不改变出库放行审核。 +- 不在本需求中自动给角色授权新菜单(上线后由管理员在角色管理中勾选)。 + +## 2. 需求确认记录 + +| 维度 | 决定 | +|------|------| +| 数据模型 | 一条审核记录、两套结论,不拆表 | +| 细胞存储菜单 | 只读总览:看两边结论 + 入库状态;不能审、不能入库 | +| 生产 / 质检菜单 | 各审各侧;两边都能看到对侧结论 | +| 入库主路径 | 两侧都 `tg` 后自动入库 | +| 批量入库 | 仅生产、质检页保留;仅「双通过且待入库」 | +| 单条「样本入库」 | 去掉(自动入库为主,补点走批量) | +| 改结论 | 两侧可随时改自己的结论 | +| 已入库后改不通过 | 不弹窗,直接回退出库 | +| 回退范围 | 放行记录改回待入库;采集样本 `yrk` → `jcz`;细胞产品库位不动 | +| 再次双通过 | 再自动入库一次 | +| 历史未入库「通过」 | 只记质检侧;生产侧待审 | +| 历史已入库 | 质检记通过、生产待审、**入库状态保持已入库** | +| 谁先审 | 无顺序要求 | +| 同一人两边都审 | 系统不禁;靠角色菜单隔离。超管两边都能进 | + +## 3. 入口与权限 + +三个入口共用 `web-admin/src/views/quality-inspection/storage-release-review/index.vue`,用路由 `props.reviewRole` 区分能力。 + +| 入口 | 路由 path | menuCode | `reviewRole` | 审核 | 批量入库 | +|------|-----------|----------|--------------|------|----------| +| 市场 → 细胞存储 | `/market/cell-storage/storage-release-review` | `biz:storageReleaseReview:list`(沿用) | `overview` | 否 | 否 | +| 生产 → 生产管理 | `/production-group/production/storage-release-review` | `production:storageReleaseReview:list` | `production` | 仅生产侧 | 是 | +| 质检 → 质检管理 | `/quality-group/quality-manage/storage-release-review` | `quality:storageReleaseReview:list` | `qc` | 仅质检侧 | 是 | + +现有质检下隐藏 redirect(`QualityInspectionStorageReleaseReviewRedirect` → 细胞存储)改为真实菜单项,不再跳转。 + +`sqxp.ts` 与 `backend/app/api/menu.py` 的 `STATIC_MENU_TREE` 必须同步。 + +### 接口鉴权 + +| 操作 | 允许的菜单 | +|------|------------| +| 列表 / 详情 | 上述三个 menuCode 任一 | +| 提交审核 / 批量审核 | 仅 `production:storageReleaseReview:list` 或 `quality:storageReleaseReview:list` | +| 批量入库 | 同上(细胞存储不可调) | + +请求体必须带 `reviewSide`:`production` 或 `qc`。 + +- `reviewSide=production`:用户必须有生产菜单(或超管)。 +- `reviewSide=qc`:用户必须有质检菜单(或超管)。 +- 细胞存储菜单不能提交审核或入库。 +- **不信任**前端用 `reviewRole` 代替 `reviewSide`;后端以 `reviewSide` + 菜单校验为准。 + +## 4. 状态机 + +``` +两侧未齐(汇总为空) + │ + │ 两侧都 tg + ▼ + 自动入库(storage_status=yrk) + │ + │ 任一侧改为 btg(不弹窗) + ▼ + 回退出库(storage_status=drk) + │ + │ 两侧再次都 tg + ▼ + 再自动入库一次 +``` + +汇总字段 `review_conclusion`(后端维护,兼容短信、旧查询): + +| 生产结论 | 质检结论 | 汇总 | +|----------|----------|------| +| `tg` | `tg` | `tg` | +| 任一侧 `btg` | (另一侧任意) | `btg` | +| 其余(含一侧空) | | 空(待齐) | + +## 5. 数据模型 + +仍用表 `biz_storage_release_review`。原 `review_*` 改为 **汇总快照**(随两侧提交重算),不再表示「某一个人审过」。 + +新增列(紧挨原审核字段之后、入库字段之前): + +```sql +ALTER TABLE `biz_storage_release_review` + ADD COLUMN `qc_review_conclusion` VARCHAR(16) DEFAULT NULL COMMENT '质检侧结论 tg/btg/NULL待审' AFTER `review_time`, + ADD COLUMN `qc_review_remark` VARCHAR(512) DEFAULT NULL COMMENT '质检侧审核备注' AFTER `qc_review_conclusion`, + ADD COLUMN `qc_review_attachments` VARCHAR(4000) DEFAULT NULL COMMENT '质检侧审核附件(逗号分隔URL)' AFTER `qc_review_remark`, + ADD COLUMN `qc_review_by` VARCHAR(64) DEFAULT NULL COMMENT '质检侧审核人ID' AFTER `qc_review_attachments`, + ADD COLUMN `qc_review_by_name` VARCHAR(64) DEFAULT NULL COMMENT '质检侧审核人姓名' AFTER `qc_review_by`, + ADD COLUMN `qc_review_time` DATETIME DEFAULT NULL COMMENT '质检侧审核时间' AFTER `qc_review_by_name`, + ADD COLUMN `prod_review_conclusion` VARCHAR(16) DEFAULT NULL COMMENT '生产侧结论 tg/btg/NULL待审' AFTER `qc_review_time`, + ADD COLUMN `prod_review_remark` VARCHAR(512) DEFAULT NULL COMMENT '生产侧审核备注' AFTER `prod_review_conclusion`, + ADD COLUMN `prod_review_attachments` VARCHAR(4000) DEFAULT NULL COMMENT '生产侧审核附件(逗号分隔URL)' AFTER `prod_review_remark`, + ADD COLUMN `prod_review_by` VARCHAR(64) DEFAULT NULL COMMENT '生产侧审核人ID' AFTER `prod_review_attachments`, + ADD COLUMN `prod_review_by_name` VARCHAR(64) DEFAULT NULL COMMENT '生产侧审核人姓名' AFTER `prod_review_by`, + ADD COLUMN `prod_review_time` DATETIME DEFAULT NULL COMMENT '生产侧审核时间' AFTER `prod_review_by_name`; +``` + +索引:增加 `(tenant_no, qc_review_conclusion, prod_review_conclusion, del_flag)`,供 Tab / 筛选使用。原 `idx_srr_status` 保留(汇总结论仍用于短信等)。 + +DDL 与数据迁移脚本放在 `docs/sql/`(独立变更 SQL,并同步 `docs/sql/zkxw.sql` 表结构)。 + +ORM:`BizStorageReleaseReview` 增加对应字段。`_row_to_dict` 为可读字段唯一来源,须输出两侧字段 + 汇总字段。Create/Update schema 与前端 `formData` 按 `docs/dev-field-consistency.md` 对齐。 + +原 `review_by` / `review_by_name` / `review_time` / `review_remark` / `review_attachments`:提交后按 **后一次导致汇总变化的那一侧** 回填汇总人时信息(取该侧审核人/时间/备注/附件)。列表不再展示这组汇总人字段;查看弹窗以两侧字段为准。短信、协议卡等旧读取方仍可读汇总 `review_conclusion`。 + +## 6. 入库与回退 + +抽公共函数,供自动入库与批量入库共用。整次提交(写结论 + 可能的入库/回退)在同一事务;失败整单回滚。 + +### 6.1 自动入库(两侧均为 `tg` 且 `storage_status=drk`) + +与现网 `store_sample` 一致: + +- `storage_status=yrk`,写入 `storage_by` / `storage_by_name` / `storage_time`(当前提交人;若由批量入库触发则为操作人)。 +- `reception_source=cell_storage`:按 `tenant_no + sample_code` 更新 `biz_protocol_sample.sample_status='yrk'`、`storage_in_date`。 +- 科研捐赠样本:现网入库不改捐赠样本状态,本需求保持不变。 +- 不写库位、不改细胞产品。 + +### 6.2 回退出库(任一侧变为 `btg` 且 `storage_status=yrk`) + +不弹窗,直接执行: + +- `storage_status=drk`;`storage_by` / `storage_by_name` / `storage_time` 置空。 +- 采集协议样本:仅当 `sample_status='yrk'` 时改为 `jcz`(检测中);`yzf` 等其它状态不改。`storage_in_date` 清空。 +- 科研捐赠样本状态不改(与入库对称)。 +- 细胞产品、库位占用一律不动。 + +### 6.3 批量入库 + +- 仅处理:`prod_review_conclusion='tg'` **且** `qc_review_conclusion='tg'` **且** `storage_status='drk'`。 +- 不满足条件的 id 计入 fail,不中断其它 id。 +- 细胞存储入口不展示、不调用该接口。 + +## 7. API + +前缀保持 `/web/admin/storage-release-reviews`。 + +### 7.1 列表 `GET /storage-release-reviews` + +新增/调整查询参数: + +| 参数 | 说明 | +|------|------| +| `reviewSide` | `overview` / `production` / `qc`。决定 `reviewPhase` 语义。缺省按 `overview` | +| `reviewPhase` | `pending` / `reviewed` | +| `prodReviewConclusion` | 生产侧 `tg` / `btg` | +| `qcReviewConclusion` | 质检侧 `tg` / `btg` | +| `reviewConclusion` | 仍过滤 **汇总** 结论(兼容);本页筛选改为两侧下拉后,列表页可不再传此参数 | +| `storageStatus` / `keyword` / `receptionSource` / `readyForReviewOnly` | 保持现网 | + +`reviewPhase` 按入口: + +| reviewSide | pending | reviewed | +|------------|---------|----------| +| `production` | 生产侧结论为空 | 生产侧已有结论 | +| `qc` | 质检侧结论为空 | 质检侧已有结论 | +| `overview` | 至少一侧结论为空 | 两侧都已有结论 | + +待审核默认「仅展示双工单侧就绪」(`readyForReviewOnly`)规则 **不变**;「展示全部待审核」复选框保留。 + +列表项增加:`prodReviewConclusion`、`qcReviewConclusion` 及对应 Label、两侧审核人/时间(详情/查看用;列表列可不展示人)。保留汇总 `reviewConclusion`。 + +### 7.2 详情 `GET /storage-release-reviews/{id}` + +`review` 对象带两侧完整字段 + 汇总 + 入库字段。证据域(协议/运输/接收/质检/生产)不变。 + +### 7.3 提交 `PUT /storage-release-reviews/{id}/review` + +Body: + +```json +{ + "reviewSide": "production", + "reviewConclusion": "tg", + "reviewRemark": "", + "reviewAttachments": "" +} +``` + +只更新对应侧字段,然后重算汇总并触发自动入库或回退。 + +### 7.4 批量审核 `PUT /storage-release-reviews/batch-review` + +在现有 `ids` + 结论/备注/附件上增加必填 `reviewSide`。逐条走与单条相同的提交逻辑。 + +### 7.5 单条入库 `PUT /.../store` + +接口可保留供内部/自动入库复用,但 **前端不再提供单条「样本入库」按钮**。若保留对外接口,门槛改为两侧都 `tg` 且待入库;无生产/质检菜单则 403。 + +### 7.6 批量入库 `PUT /.../batch-store` + +门槛改为两侧都 `tg` 且待入库。无生产/质检菜单则 403。 + +## 8. 前端 UI + +页面仍对照现网列表骨架(`filter-block` → 工具栏 → `list-body` → 分页)。标题保持「审核列表」(`App.vue` `panelTitleBarTextRules` 若已登记则无需改文案)。 + +### 8.1 按 `reviewRole` 的按钮 + +| 能力 | overview | production / qc | +|------|----------|-----------------| +| 批量审核 | 隐藏 | 显示;本侧待审或已审均可改 | +| 批量入库 | 隐藏 | 所选行均为双 `tg` 且 `drk` 时可点 | +| 行:查看 | 有 | 有 | +| 行:审核 | 无 | 有(提交本侧) | +| 行:样本入库 | 无 | 无 | + +### 8.2 列表列 + +- 原「审核结论」拆成两列 Tag:`生产结论`、`质检结论`。空 → `info`「待审核」;`tg` → `success`「通过」;`btg` → `danger`「不通过」。 +- 去掉列表「审核人」「审核时间」列(改到查看弹窗)。 +- 「入库状态」列保留。 + +筛选:原「审核结论」改为两个下拉「生产结论」「质检结论」(通过 / 不通过 / 清空=全部)。入库状态、关键词、协议类别不变。 + +### 8.3 审核弹窗 + +证据区(接收 / 质检 / 生产)不变。表单只编辑本侧结论、备注、附件。对侧结论用只读 `el-tag` 展示(人、时间可一并只读)。 + +标题区分:「生产入库放行审核」/「质检入库放行审核」;批量时仍为「批量审核(N 条)」。 + +### 8.4 查看弹窗 + +原「放行审核」一节拆为: + +1. **质检放行**:结论 Tag、人、时间、备注、附件 +2. **生产放行**:同上 +3. **入库**:入库状态 Tag、入库人、入库时间 + +### 8.5 协议/样本卡复用 + +`sample-storage-release-info` 与 `get_storage_release_map_for_samples` 改为展示两侧结论 + 入库状态,避免协议查看仍像「单人已通过」。 + +## 9. 历史数据迁移 + +一次性 SQL(在 ALTER 之后执行): + +1. 旧 `review_conclusion` 非空:复制到质检侧(`qc_review_*` ← `review_*`)。 +2. 生产侧全部保持空。 +3. **不**改 `storage_status`、入库人、入库时间。 +4. 按第 4 节规则重算汇总 `review_conclusion`: + - 两侧都 `tg` → `tg` + - 任一侧 `btg` → `btg` + - 否则 `NULL` + +因此: + +- 旧「通过」未入库 → 质检通过、生产待审、待入库、汇总空。生产审过之后才自动入库。 +- 旧「通过」已入库 → 质检通过、生产待审、**仍已入库**、汇总空。总览允许「已入库 + 生产待审」。生产之后若改 `btg`,按新规则直接回退。 +- 旧「不通过」 → 质检不通过、生产待审、汇总 `btg`。 + +脚本须可重复执行:仅当 `qc_review_conclusion` 为空且旧 `review_conclusion` 非空时拷贝,避免二次覆盖新审核。 + +## 10. 其它读取方 + +| 读取方 | 处理 | +|--------|------| +| 样本入库短信(`review_conclusion='btg'` 或已入库) | 继续用汇总 + `storage_status`;语义仍成立 | +| 储存证等依赖样本 `sample_status='yrk'` | 随自动入库/回退变化;回退后证书资格按现网「已入库」规则自然失效 | +| 出库放行审核 | 不改 | + +## 11. 测试要点 + +1. 质检先过、生产后过 → 自动入库;采集样本变为 `yrk`。 +2. 生产先过、质检后过 → 同样自动入库。 +3. 已入库后任一侧改 `btg` → 放行回 `drk`,采集样本 `yrk`→`jcz`,库位产品仍在。 +4. 两侧再都改回 `tg` → 再次自动入库。 +5. 细胞存储入口无审核/入库按钮,调审核 API 返回 403。 +6. 生产入口不能改质检侧字段(即使 body 乱传)。 +7. 批量入库跳过未双通过或已入库行。 +8. 迁移后:已入库旧数据保持 `yrk` 且生产待审;未入库旧通过仅质检有结论。 +9. 待审核就绪过滤、作废隐藏规则仍生效。 + +## 12. 文件清单(实现时) + +| 区域 | 文件 | +|------|------| +| SQL | `docs/sql/` 变更脚本 + `docs/sql/zkxw.sql` 表结构 | +| 模型 / schema / CRUD / API | `models.py`、`schemas/storage_release_review.py`、`crud/storage_release_review.py`、`api/storage_release_review.py` | +| 菜单 | `backend/app/api/menu.py`、`web-admin/src/router/modules/sqxp.ts` | +| 前端页 | `storage-release-review/index.vue`、`StorageReleaseReviewDialog.vue`、`StorageReleaseViewDialog.vue` | +| API 类型 | `web-admin/src/api/storage-release-review.ts` | +| 协议卡 | `sample-storage-release-info`、CRUD 摘要 map | +| 单测 | `backend/tests/`:汇总重算、自动入库、回退、Tab 过滤、迁移拷贝条件 | + +实现后按 `AGENTS.md` 增量更新对应 graphify 图谱。