2026-07-01-special-sample-mark-design.md 10 KB

样本特殊标记与特殊说明

日期: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

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 → BizProtocolSample
  • backend/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 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:

<!-- 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

展示策略:

  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 全链路展示;不含筛选/导出/小程序