10 KiB
样本特殊标记与特殊说明
日期:2026-07-01
状态:待审核
关联:2026-05-19-protocol-donation-convert-design.md(捐赠转细胞存储样本复制)
关联模块:采集协议单、捐赠协议单、样本信息、质检、生产、入库审核
1. 背景与目标
1.1 业务诉求
在采集协议单与捐赠协议单的样本信息中,增加「特殊样本」标记及「特殊说明」文本字段。例如样本为小三阳时:勾选特殊样本,特殊说明填写「小三阳」。
凡展示样本数据的界面,若该样本为特殊样本,须醒目展示:
- 红色加粗 【特殊样本】
- 红色加粗 特殊说明 内容
1.2 目标
- 两种协议单的样本录入弹窗支持维护
isSpecial+specialRemark。 - 勾选特殊样本时,特殊说明必填(已确认:选项 A)。
- 所有 web-admin 展示样本数据的页面/弹窗/卡片,特殊样本信息只读展示,样式统一。
- 捐赠转细胞存储时,特殊标记随样本复制到新
biz_protocol_sample行。 - 历史数据默认非特殊样本,兼容现有流程。
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_specialspecial_remark← 源样本special_remark
3. 数据模型
3.1 表结构变更
两张样本表对称增加字段(位于业务字段末尾、tenant_no 之前):
biz_protocol_sample
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
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→BizProtocolSamplebackend/app/models/research_donation.py→BizResearchDonationSample
3.3 API 字段(camelCase)
_sample_to_dict 统一输出:
"isSpecial": bool(s.is_special),
"specialRemark": (s.special_remark or "").strip(),
save_samples 写入时:
is_special = 1 if raw.get("isSpecial") else 0special_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
位置:添加/编辑样本子弹窗,「样本状态」表单项之后。
控件:
el-checkbox— 标签「特殊样本」,绑定sampleDraft.isSpecialel-inputtype="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:
<!-- props: isSpecial, specialRemark, layout?: 'inline' | 'block' -->
<template v-if="isSpecial">
<span class="special-sample-mark">【特殊样本】</span>
<span v-if="specialRemark" class="special-sample-remark">{{ specialRemark }}</span>
</template>
样式(scoped + 可全局复用 class):
.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 |
展示策略:
- 列表/卡片:样本编码列或标题行旁 inline 展示(最醒目)
- 详情弹窗:样本编码下方或信息区独立一行
- 统一使用
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. 风险与约束
- 双表对称:采集与捐赠样本表、CRUD、UI 须同步改动,避免只改一侧。
- 改动面大:下游展示点 15+,须用公共组件收敛样式。
- 与普通 remark 区分:文档与 UI 文案明确,避免用户把特殊说明写入备注。
- 警示色:使用 Element Plus danger 色 +
font-weight: 700,符合 Fresh Mint 规范中对强提醒的使用场景。
9. 决策记录
| 议题 | 决策 |
|---|---|
| 勾选特殊样本时特殊说明是否必填 | A. 必填(前后端校验) |
| 数据存储位置 | 样本源表唯一数据源,下游 JOIN |
| 展示组件 | 新建 SpecialSampleDisplay,红色加粗文本,非 Tag |
| 本期范围 | web-admin 全链路展示;不含筛选/导出/小程序 |