# 样本特殊标记与特殊说明 > 日期:2026-07-01 > 状态:待审核 > 关联:`2026-05-19-protocol-donation-convert-design.md`(捐赠转细胞存储样本复制) > 关联模块:采集协议单、捐赠协议单、样本信息、质检、生产、入库审核 --- ## 1. 背景与目标 ### 1.1 业务诉求 在**采集协议单**与**捐赠协议单**的样本信息中,增加「特殊样本」标记及「特殊说明」文本字段。例如样本为小三阳时:勾选特殊样本,特殊说明填写「小三阳」。 凡展示样本数据的界面,若该样本为特殊样本,须**醒目展示**: - 红色加粗 **【特殊样本】** - 红色加粗 **特殊说明** 内容 ### 1.2 目标 1. 两种协议单的样本录入弹窗支持维护 `isSpecial` + `specialRemark`。 2. 勾选特殊样本时,特殊说明**必填**(已确认:选项 A)。 3. 所有 web-admin 展示样本数据的页面/弹窗/卡片,特殊样本信息只读展示,样式统一。 4. 捐赠转细胞存储时,特殊标记随样本复制到新 `biz_protocol_sample` 行。 5. 历史数据默认非特殊样本,兼容现有流程。 ### 1.3 非目标(本期不做) - 小程序端展示 - 按特殊样本筛选列表 - 导出 Excel 增加特殊说明列 - 下游业务表(接收、质检工单等)冗余存储特殊字段 --- ## 2. 业务规则 ### 2.1 字段语义 | 字段 | API 名 | 类型 | 说明 | |------|--------|------|------| | 特殊样本标记 | `isSpecial` | `boolean` | `true` 表示特殊样本 | | 特殊说明 | `specialRemark` | `string` | 自由文本,如「小三阳」 | 与现有 `remark`(普通备注)**语义分离**:`remark` 为通用备注;`specialRemark` 为结构化警示说明,仅在 `isSpecial=true` 时有效。 ### 2.2 校验规则(已确认) | 条件 | 规则 | |------|------| | `isSpecial = true` | `specialRemark` **必填**,trim 后非空,最大 256 字符 | | `isSpecial = false` | `specialRemark` 保存时**清空**为 `null` / 空字符串 | | 未勾选却填写了说明 | 前端保存前清空;后端以 `isSpecial` 为准,忽略 `specialRemark` | 前后端均须校验;后端为最终门禁。 ### 2.3 编辑与展示 - **可编辑**:采集/捐赠协议单新增、编辑样本(`SampleTransportFormSection` 子弹窗)。 - **只读展示**:协议查看弹窗、下游质检/生产/接收等所有样本展示位。 - **编辑时机**:协议单保存样本时一并持久化;下游模块通过 JOIN 源样本表读取最新值。 ### 2.4 捐赠转细胞存储 `research_donation.py` 复制 `BizResearchDonationSample` → `BizProtocolSample` 时,须同步复制: - `is_special` ← 源样本 `is_special` - `special_remark` ← 源样本 `special_remark` --- ## 3. 数据模型 ### 3.1 表结构变更 两张样本表**对称**增加字段(位于业务字段末尾、`tenant_no` 之前): **`biz_protocol_sample`** ```sql ALTER TABLE `biz_protocol_sample` ADD COLUMN `is_special` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否特殊样本:0否 1是' AFTER `storage_expire_date`, ADD COLUMN `special_remark` varchar(256) DEFAULT NULL COMMENT '特殊说明' AFTER `is_special`; ``` **`biz_research_donation_sample`** ```sql ALTER TABLE `biz_research_donation_sample` ADD COLUMN `is_special` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否特殊样本:0否 1是' AFTER `storage_expire_date`, ADD COLUMN `special_remark` varchar(256) DEFAULT NULL COMMENT '特殊说明' AFTER `is_special`; ``` 同步更新 `docs/sql/zkxw.sql` 基线。 ### 3.2 ORM - `backend/app/models/models.py` → `BizProtocolSample` - `backend/app/models/research_donation.py` → `BizResearchDonationSample` ### 3.3 API 字段(camelCase) `_sample_to_dict` 统一输出: ```python "isSpecial": bool(s.is_special), "specialRemark": (s.special_remark or "").strip(), ``` `save_samples` 写入时: - `is_special = 1 if raw.get("isSpecial") else 0` - `special_remark` 仅在 `is_special=1` 时写入 trim 后文本,否则 `None` --- ## 4. 架构方案 ### 4.1 选定方案:样本表唯一数据源 + 读取 JOIN 特殊标记仅存于 `biz_protocol_sample` / `biz_research_donation_sample`,下游模块在 list/detail API 中 JOIN 或复用已有样本查询补出 `isSpecial`、`specialRemark`。 **不**在 `biz_sample_reception`、`biz_qc_work_order` 等子表冗余。 ### 4.2 后端改造模式 凡构建样本相关 dict 的位置,按 `_sample_to_dict` 模式补字段。重点文件: | 文件 | 改动 | |------|------| | `crud/collection_protocol.py` | `_sample_to_dict`、`save_samples` | | `crud/research_donation.py` | `_sample_to_dict`、`save_samples`、转存储复制 | | `crud/protocol_sample_collection.py` | list item 构建 | | `crud/research_donation_sample_collection.py` | list item 构建 | | `crud/sample_reception.py` | 待接收/已接收/批量 list | | `crud/qc_work_order.py` | 工单 list/detail 中样本信息 | | `crud/production_work_order.py` | 同上 | | `crud/storage_release_review.py` | 入库审核 list/detail | | 转运前样本等相关 CRUD | 协议卡片内样本 list | Pydantic Create/Update body 中 samples 数组项增加可选字段 `isSpecial`、`specialRemark`;后端 save 时校验。 运行 `python scripts/check_field_consistency.py` 验证 CRUD ↔ Schema ↔ 前端字段一致。 --- ## 5. 前端设计 ### 5.1 录入 UI — `SampleTransportFormSection` **位置**:添加/编辑样本子弹窗,「样本状态」表单项之后。 **控件**: 1. `el-checkbox` — 标签「特殊样本」,绑定 `sampleDraft.isSpecial` 2. `el-input` type="textarea" — 标签「特殊说明」,`:disabled="!sampleDraft.isSpecial"`,`maxlength=256`,`show-word-limit` **交互**: - 取消勾选 → 清空 `specialRemark` - 确定时:`isSpecial=true` 且 `specialRemark` 为空 → `ElMessage.warning` + 表单校验失败 **列表表格**:新增列「特殊样本」(`min-width` ≥ 120): - 非特殊:显示 `—` - 特殊:inline 展示 `SpecialSampleDisplay` 组件内容 `createEmptySample()` / `sampleDraft` 初始化增加 `isSpecial: false`、`specialRemark: ''`。 采集协议单、捐赠协议单共用此组件,**改一处覆盖两种协议**。 ### 5.2 展示 UI — 公共组件 新建 `web-admin/src/components/business/special-sample-display/index.vue`: ```vue ``` 样式(scoped + 可全局复用 class): ```scss .special-sample-mark, .special-sample-remark { color: var(--el-color-danger); font-weight: 700; } .special-sample-remark { margin-left: 4px; } ``` - **非特殊样本**:组件不渲染任何 DOM(不占位、不显示「—」)。 - **不使用 `el-tag`**:此为警示文本,非状态枚举。 ### 5.3 查看弹窗 **`ProtocolViewDialog` / `DonationViewDialog`** 样本卡片: - 卡片 header 标题行:样本类型名旁 inline 展示 `SpecialSampleDisplay` - 样本信息 data-grid:独立一行「特殊说明」(仅 `isSpecial` 时显示,block 布局) ### 5.4 下游展示接入点(P1 全量) | 模块 | 文件 | |------|------| | 协议管理 | `ProtocolAuditTab.vue`、`PreTransitProtocolCardList.vue` | | 捐赠审核 | `research-donation/audit/index.vue` | | 样本采集信息 | `SampleCollectionInfoTab.vue`、`DonationSampleCollectionInfoTab.vue` | | 样本接收 | `sample-reception/index.vue`、`SampleReceptionCardList.vue` | | 质检工单 | `QcWorkOrderProtocolGroup.vue`、`QcWorkOrderSampleGroup.vue`、`QcWorkOrderTaskSubList.vue` | | 入库审核 | `storage-release-review/index.vue`、`StorageReleaseReviewDialog.vue`、`StorageReleaseViewDialog.vue` | | 生产工单 | `ProductionWorkOrderCardList.vue`、`SampleReceptionDialog.vue`、`DispatchTaskDialog.vue` 等 | | 溯源 | `SampleCustomerViewDialog.vue` | **展示策略**: 1. 列表/卡片:**样本编码列或标题行**旁 inline 展示(最醒目) 2. 详情弹窗:样本编码下方或信息区独立一行 3. 统一使用 `SpecialSampleDisplay`,禁止各页复制样式 ### 5.5 TypeScript 类型 `web-admin/src/types/api.d.ts` 中协议/捐赠样本相关类型补充 `isSpecial?: boolean`、`specialRemark?: string`。 --- ## 6. 实施阶段 | 阶段 | 范围 | 交付标准 | |------|------|----------| | **P0** | DB + Model + 两协议 CRUD + 录入组件 + 两协议查看弹窗 | 可在协议单录入/查看特殊样本 | | **P1** | 公共展示组件 + 下游全部展示点 + 转存储复制 | 质检/生产/接收等链路可见红色警示 | | **P2**(可选) | 筛选、导出、小程序 | 产品另行排期 | 建议 P0、P1 **同一 PR 或连续 PR** 交付,避免下游看不到标记造成业务风险。 --- ## 7. 测试要点 ### 7.1 录入校验 - [ ] 勾选特殊样本、说明为空 → 前端拦截 - [ ] 勾选特殊样本、说明为空 → 后端 API 返回 422/400 - [ ] 未勾选、说明有值 → 保存后 `specialRemark` 为空 - [ ] 说明超 256 字 → 校验失败 ### 7.2 持久化 - [ ] 采集协议单编辑保存后 detail 回显正确 - [ ] 捐赠协议单同上 - [ ] 历史样本(无新字段)→ `isSpecial=false` ### 7.3 展示 - [ ] 协议查看弹窗:红色加粗【特殊样本】+ 说明 - [ ] 非特殊样本:各页无多余占位 - [ ] 样本接收、质检工单、生产工单卡片可见特殊标记 ### 7.4 转存储 - [ ] 捐赠单特殊样本转细胞存储后,新采集协议单样本保留 `isSpecial` + `specialRemark` ### 7.5 字段一致性 - [ ] `python scripts/check_field_consistency.py` 通过 --- ## 8. 风险与约束 1. **双表对称**:采集与捐赠样本表、CRUD、UI 须同步改动,避免只改一侧。 2. **改动面大**:下游展示点 15+,须用公共组件收敛样式。 3. **与普通 remark 区分**:文档与 UI 文案明确,避免用户把特殊说明写入备注。 4. **警示色**:使用 Element Plus danger 色 + `font-weight: 700`,符合 Fresh Mint 规范中对强提醒的使用场景。 --- ## 9. 决策记录 | 议题 | 决策 | |------|------| | 勾选特殊样本时特殊说明是否必填 | **A. 必填**(前后端校验) | | 数据存储位置 | 样本源表唯一数据源,下游 JOIN | | 展示组件 | 新建 `SpecialSampleDisplay`,红色加粗文本,非 Tag | | 本期范围 | web-admin 全链路展示;不含筛选/导出/小程序 |