Files
zkxw/docs/superpowers/specs/2026-06-29-dual-sample-reception-design.md
T
2026-06-30 09:19:06 +08:00

314 lines
13 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.
# 双部门样本接收 — 质检域与生产域拆分
> 日期:2026-06-29
> 状态:待审核
> 关联:`2026-05-19-unified-sample-reception-design.md`、`2026-05-19-production-work-order-design.md`、`2026-06-03-sample-reception-lab-storage-code-design.md`
> 关联模块:样本接收、入库生产工单、协议/捐赠查看弹窗、质检工单查看
---
## 1. 背景与目标
### 1.1 现状问题
当前 **质检管理 → 样本接收** 与 **生产管理 → 生产工单 → 样本接收信息填写** 共用同一张表 `biz_sample_reception`、同一套 submit/update API,两侧均可修改全部字段(含体积、净重、检查项、接收结论等)。与客户「质检登记」与「生产登记」分属不同部门、字段不同的诉求不符。
### 1.2 目标
1. **质检域**:仅在【质检管理 → 样本接收】填写;去掉 **体积(ml)**、**净重(g)**。
2. **生产域**:仅在【生产工单 → 样本接收信息填写】填写;字段按 **来源 × 样本类型** 动态展示。
3. **互读互斥写**:质检弹窗可见生产信息(只读);生产弹窗可见质检信息(只读)。
4. **展示统一**:所有展示样本接收信息的查看弹窗/详情,同时展示 **质检接收** 与 **生产接收** 两块。
5. **历史数据(已确认)**:`volume_ml`、`net_weight_g` **仅从界面与读写 API 去掉**;库中保留旧值,**不再展示、不迁移**。
---
## 2. 业务规则
### 2.1 填写入口与权限
| 入口 | 可写域 | 只读域 | 写权限 |
|------|--------|--------|--------|
| 质检 → 样本接收(待接收/已接收/批量) | 质检接收 | 生产接收 | 现网:`采集接受人`(经手人)、`质量主管`(编辑已接收) |
| 生产工单 → 样本接收信息填写 | 生产接收 | 质检接收 | **实验专员**(处理人候选与操作角色一致;可复用 `PRODUCTION_STEP_ASSIGNEE_ROLE`) |
后端 **必须** 在 API 层拒绝跨域写入,不依赖前端 `disabled`。
### 2.2 流程依赖
- 入库生产工单在 **质检接收结论 = 接收(js)** 后自动创建(现网逻辑不变)。
- **生产接收填写** 入口仅在入库类生产工单卡片展示(`inbound_cell_storage` / `inbound_research_donation`)。
- **质检拒收(bj)**:生产弹窗不展示填写能力;只读展示质检结论即可。
- **填写顺序**:须已存在质检接收记录后,生产方可保存生产接收(生产 API 校验 `reception_id` 存在)。
### 2.3 样本类型匹配
与 `web-admin/src/utils/sample-reception-lab-storage-code.ts` 一致,按 **样本类型名称** 匹配:
| 名称 | 说明 |
|------|------|
| `围产期造血干细胞` | 对应需求文档编码 `wcqzxgxb` |
| `脐带间充质干细胞` | 对应需求文档编码 `qdjczgxb` |
**未在矩阵中的类型**(如 `胎盘亚全能干细胞`):生产弹窗仅展示公共字段(处理人、处理时间),无类型专属字段;专属字段不做必填校验。
### 2.4 历史字段 `volume_ml` / `net_weight_g`
- **决策:选项 A** — 停止读写与展示;`biz_sample_reception` 列保留,不执行数据迁移脚本。
- `_reception_to_dict` 及前端类型定义 **不再输出** 这两字段(或输出但全链路忽略)。
- 新生产域字段与历史体积/净重 **无自动映射**。
---
## 3. 字段矩阵
### 3.1 质检域(`biz_sample_reception`,现有表)
| 字段 | 变更 |
|------|------|
| reception_time, handler_user_id, handler_name | 保留 |
| receive_temperature | 保留 |
| check_results(子表) | 保留 |
| accept_conclusion, lab_storage_code | 保留 |
| receive_attachments, remark | 保留 |
| **volume_ml, net_weight_g** | **停止 API 入参/出参及 UI 展示** |
### 3.2 生产域(新表 `biz_sample_production_reception`)
**公共字段(所有来源×类型)**
| 字段 | 类型 | 规则 |
|------|------|------|
| handler_user_id | varchar(64) | 处理人;默认当前登录用户 |
| handler_name | varchar(100) | 处理人姓名快照 |
| process_time | datetime | 处理时间;默认当前时间 |
**细胞存储 + 围产期造血干细胞**
| 字段 | 类型 | 标签 |
|------|------|------|
| original_blood_weight_g | decimal(18,4) | 原血重量/g |
| hes_volume_ml | decimal(18,4) | HES用量/ml |
| blood_type | varchar(32) | 血型(文本输入,非下拉) |
| infectious_four_test_result | varchar(16) | 传染四项检测(检测卡):`zc` 正常 / `bzc` 不正常 |
| enriched_blood_weight_g | decimal(18,4) | 富集后血量/g |
| cryopreservation_volume_ml | decimal(18,4) | 冻存液用量/ml |
**细胞存储 + 脐带间充质干细胞**
| 字段 | 类型 | 标签 |
|------|------|------|
| inoculation_amount | decimal(18,4) | 接种量 |
| cryopreservation_time | datetime | 冻存时间 |
| storage_total_tubes | int | 储存总量(管) |
| available_cell_count_1e7 | decimal(18,4) | 可提供细胞数量/*10E7 |
**科研捐赠 + 脐带间充质干细胞**
| 字段 | 类型 | 标签 |
|------|------|------|
| inoculation_amount | decimal(18,4) | 接种量 |
| cryopreservation_time | datetime | 冻存时间 |
**科研捐赠 + 围产期造血干细胞**
| 字段 | 类型 | 标签 |
|------|------|------|
| original_blood_volume_ml | decimal(18,4) | 原血体积/ml |
| nucleated_cell_count_1e8 | decimal(18,4) | 有核细胞数/10E8 |
**必填规则**:对当前样本命中的类型专属字段 + 公共字段做前后端必填校验;未命中类型的专属列保持 NULL。
---
## 4. 数据库设计
### 4.1 新表
```sql
-- docs/sql/migrate_sample_production_reception.sql
CREATE TABLE `biz_sample_production_reception` (
`id` varchar(64) NOT NULL COMMENT '主键ID',
`reception_id` varchar(64) NOT NULL COMMENT '关联 biz_sample_reception.id',
`sample_code` varchar(32) DEFAULT NULL COMMENT '样本编码快照',
`reception_source` varchar(20) NOT NULL DEFAULT 'cell_storage' COMMENT '来源 cell_storage/research_donation',
`sample_type_name` varchar(100) DEFAULT NULL COMMENT '样本类型名称快照',
`handler_user_id` varchar(64) DEFAULT NULL COMMENT '处理人用户ID',
`handler_name` varchar(100) DEFAULT NULL COMMENT '处理人姓名',
`process_time` datetime NOT NULL COMMENT '处理时间',
`original_blood_weight_g` decimal(18,4) DEFAULT NULL COMMENT '原血重量/g',
`hes_volume_ml` decimal(18,4) DEFAULT NULL COMMENT 'HES用量/ml',
`blood_type` varchar(32) DEFAULT NULL COMMENT '血型',
`infectious_four_test_result` varchar(16) DEFAULT NULL COMMENT '传染四项 zc正常 bzc不正常',
`enriched_blood_weight_g` decimal(18,4) DEFAULT NULL COMMENT '富集后血量/g',
`cryopreservation_volume_ml` decimal(18,4) DEFAULT NULL COMMENT '冻存液用量/ml',
`inoculation_amount` decimal(18,4) DEFAULT NULL COMMENT '接种量',
`cryopreservation_time` datetime DEFAULT NULL COMMENT '冻存时间',
`storage_total_tubes` int DEFAULT NULL COMMENT '储存总量(管)',
`available_cell_count_1e7` decimal(18,4) DEFAULT NULL COMMENT '可提供细胞数量/*10E7',
`original_blood_volume_ml` decimal(18,4) DEFAULT NULL COMMENT '原血体积/ml',
`nucleated_cell_count_1e8` decimal(18,4) DEFAULT NULL COMMENT '有核细胞数/10E8',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw',
`order_num` int DEFAULT NULL,
`remark` varchar(512) DEFAULT NULL,
`create_by` varchar(64) DEFAULT NULL,
`create_dept_id` varchar(64) NOT NULL DEFAULT '',
`create_time` datetime NOT NULL,
`update_by` varchar(64) DEFAULT NULL,
`update_time` datetime NOT NULL,
`del_flag` smallint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_reception_id` (`reception_id`,`del_flag`),
KEY `idx_sample_code` (`tenant_no`,`sample_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生产部门样本接收登记';
```
**关系**:与 `biz_sample_reception` **1:1**(`reception_id` 唯一);软删除遵循 `del_flag`。
### 4.2 现有表
- `biz_sample_reception.volume_ml`、`net_weight_g`:**不删列**;应用层停用。
---
## 5. API 设计
### 5.1 质检 API(调整现有 `/web/admin/sample-receptions`)
- `POST` / `PUT`:请求体 **移除** `volumeMl`、`netWeightG`。
- `GET` 详情 / 列表 / `by-sample-code`:响应增加 `productionReception`(对象或 `null`);响应 **移除** `volumeMl`、`netWeightG`。
- 写接口 **拒绝** 任何生产域字段(若误传则忽略或 400)。
### 5.2 生产 API(新增)
前缀建议:`/web/admin/production-sample-receptions`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/by-sample-code?sampleCode=&sourceType=` | 返回 `{ receptionId, qcReception, productionReception, sampleInfo }` |
| GET | `/{id}` | 生产接收详情 |
| POST | `/` | 创建(body 含 `receptionId` 或 `sampleCode`+`sourceType`) |
| PUT | `/{id}` | 更新生产域字段 |
- 校验:关联质检记录存在;`accept_conclusion !== 'bj'`;当前用户具备实验专员角色或等于处理人(编辑策略与质检对称,首版:**实验专员可写**)。
- 写接口 **禁止** 修改 `biz_sample_reception` 任意字段。
### 5.3 聚合读
以下详情接口的 `reception` 或等价字段扩展为:
```json
{
"qcReception": { "...质检字段..." },
"productionReception": { "...生产字段..." }
}
```
或保持扁平 `reception` 为质检数据,另增 `productionReception`。实现时 `_row_to_dict` 为单一真相源。
**影响面**:生产工单详情、质检工单入库查看、协议/捐赠样本卡片接口(若内嵌 `sampleReception`)。
---
## 6. 前端设计
### 6.1 可写表单
| 组件 | 改造 |
|------|------|
| `quality-inspection/sample-reception/index.vue` | 去掉体积/净重;表单下增加 **生产接收信息(只读)** 区块;拉取聚合详情 |
| `production-work-order/SampleReceptionDialog.vue` | **重写**:上为质检只读,下为生产可编辑(按类型字段模板);**不再** 调用质检 submit/update |
| `production/sample-reception-completion/index.vue` | 废弃或改为跳转生产工单;菜单保持隐藏 |
### 6.2 公共只读组件(新建)
建议:`web-admin/src/components/business/sample-reception-dual-readonly-panel/`
- Props:`qcReception`、`productionReception`、`sourceType`、`sampleTypeName`
- 内置 4 套类型字段展示模板 + 空态(「生产尚未登记」/「暂未接收」)
- 供协议查看、捐赠查看、工单查看、质检任务查看等复用
### 6.3 只读展示改造清单
| 文件 | 改造 |
|------|------|
| `ProtocolViewDialog.vue` | 样本卡片拆「质检接收」「生产接收」 |
| `DonationViewDialog.vue` | 同上 |
| `production-view-sections.ts` | 拆两段 section |
| `inbound-view-sections.ts` | 同上 |
| `QcTaskViewDialog` | 同上 |
| `QcWorkOrderTaskSubList` | 同上 |
| `StorageReleaseViewDialog` | 同上 |
| 质检样本接收「接收详情」弹窗 | 增加生产只读块 |
### 6.4 生产处理人下拉
复用 `fetchProductionStepAssigneeUsers`(角色 `实验专员`);打开弹窗时默认选中当前登录用户(若在列表中)。
### 6.5 UI 规范
- 弹窗:`dialog-form-layout`、`label-position="top"`、只读块用 `readonly-grid` / `data-grid` 与现网样本接收弹窗一致。
- 质检块标题:**质检接收信息**;生产块:**生产接收信息**。
- 传染四项:`el-select`,选项「正常」「不正常」。
---
## 7. 后端模块
| 路径 | 职责 |
|------|------|
| `backend/app/models/models.py` | `BizSampleProductionReception` |
| `backend/app/crud/sample_production_reception.py` | CRUD、类型校验、`_row_to_dict` |
| `backend/app/api/sample_production_reception.py` | 路由 |
| `backend/app/crud/sample_reception.py` | 去掉 volume/netWeight 出参;详情挂载 production |
| `backend/app/api/__init__.py` + `main.py` | 注册新路由 |
字段一致性:运行 `python scripts/check_field_consistency.py`(若纳入检查范围)。
---
## 8. 权限与菜单
- 新增按钮权限(建议):`production:sample-reception:add`、`production:sample-reception:edit`,挂在生产工单或生产管理菜单下。
- 质检权限不变:`biz:sampleReception:*`。
---
## 9. 测试要点
1. 质检接收(js) → 生产工单出现 → 生产填写 4 种类型组合字段 → 质检页只读可见。
2. 质检编辑后 → 生产页只读更新;生产编辑后 → 质检页只读更新;双方 API 互不能改对方域。
3. 质检拒收 → 生产无填写入口。
4. 历史记录:`volume_ml`/`net_weight_g` 有值的旧数据,界面与 API 均不出现。
5. 未覆盖样本类型:仅公共字段可保存。
6. 实验专员/非实验专员写权限边界。
---
## 10. 实施顺序
1. SQL + Model + 生产 CRUD/API
2. 质检 API 瘦身 + 聚合读
3. `SampleReceptionDialog` 重写 + 质检页调整
4. `sample-reception-dual-readonly-panel` + 各查看弹窗接入
5. 权限 SQL + 回归
---
## 11. 不在本次范围
- 隐藏菜单「样本接收信息完善」路由删除(可后续清理)。
- 小程序端样本接收(若有)不同步,除非另行立项。
- `volume_ml`/`net_weight_g` 列的数据库物理删除。
---
## 12. 已确认决策
| 议题 | 决策 |
|------|------|
| 历史体积/净重 | **A** — 库保留,界面与 API 去掉,不展示不迁移 |
| 数据模型 | 新表 `biz_sample_production_reception`,1:1 关联质检接收 |
| 未覆盖样本类型 | 仅公共字段,无专属必填 |