日期:2026-05-19 状态:待审核 关联:AGENTS.md(database-conventions、api-conventions、ui-conventions)
当前【样本接收】模块仅支持来自细胞存储(采集协议单)的样本。业务需要科研捐赠(捐赠协议单)的样本也走统一的样本接收流程,接收后自动生成质检工单。
核心诉求:
在 biz_sample_reception 表新增 3 个字段:
-- docs/sql/sample_reception_add_donation_source.sql
ALTER TABLE biz_sample_reception
ADD COLUMN reception_source VARCHAR(20) NOT NULL DEFAULT 'cell_storage'
COMMENT '来源类型 cell_storage细胞存储 research_donation科研捐赠'
AFTER receive_attachments,
ADD COLUMN donation_sample_id VARCHAR(64) NULL
COMMENT '捐赠样本ID(biz_research_donation_sample.id, 仅科研捐赠来源)'
AFTER reception_source,
ADD COLUMN research_donation_id VARCHAR(64) NULL
COMMENT '科研捐赠单ID(biz_research_donation.id, 仅科研捐赠来源)'
AFTER donation_sample_id;
约束规则:
reception_source = 'cell_storage':protocol_sample_id + collection_protocol_id 必填,donation_sample_id = NULLreception_source = 'research_donation':donation_sample_id + research_donation_id 必填,protocol_sample_id = NULLreception_source 默认 'cell_storage',无迁移风险,无需数据回填文件:backend/app/models/models.py — BizSampleReception 类
新增字段:
reception_source = Column(String(20), nullable=False, default="cell_storage", comment="来源类型 cell_storage/research_donation")
donation_sample_id = Column(String(64), nullable=True, comment="捐赠样本ID(仅科研捐赠)")
research_donation_id = Column(String(64), nullable=True, comment="科研捐赠单ID(仅科研捐赠)")
文件:backend/app/crud/sample_reception.py
新增 _donation_sample_detail_rows_query(db) — 对应捐赠样本的四表 JOIN:
BizResearchDonationSample + BizResearchDonation + BizResearchDonationParty + BizResearchDonationSampleTransport新增 _donation_sample_pending_query(db, tenant_no, keyword) — 查询未被接收的捐赠样本:
BizSampleReception.donation_sample_id IN (已接收的捐赠样本ID)donation_no、sample_code、mother_name、newborn_name新增 _donation_rows_to_sample_list_items(db, rows, tenant_no, ...) — 将捐赠样本行转为统一列表项字典,字段映射:
protocolSampleId → donation_sample.id(注意:字段名保持 protocolSampleId 不变,前端统一使用)
protocolNo → donation.donation_no(协议号/捐赠编号)
sourceType → 'research_donation'
sourceTypeLabel → '科研捐赠'
修改 list_pending_protocol_samples():
修改 list_received_protocol_samples():
BizSampleReception 查询时,按 reception_source 分两组cell_storage 组走现有 _sample_detail_rows_query 逻辑(不变)research_donation 组走新增的捐赠查询逻辑reception_time DESC 排序修改 create_sample_reception():
reception_source: str = "cell_storage"、donation_sample_id: Optional[str] = None、research_donation_id: Optional[str] = Nonereception_source = 'research_donation' 时:
donation_sample_id 对应的 BizResearchDonationSample 存在BizSampleReception.donation_sample_id)protocol_sample_id 置为空字符串或 NULLcollection_protocol_id 置为空字符串或 NULLjs 时,仍触发 qc_wo_svc.create_work_order_from_reception()两种来源的列表项保持统一字段名,前端无需区分:
| 字段名 | 细胞存储来源 | 科研捐赠来源 |
|---|---|---|
protocolSampleId |
biz_protocol_sample.id |
biz_research_donation_sample.id |
protocolNo |
采集协议号 | 捐赠编号 |
sampleCode |
样本编码 | 样本编码 |
sampleTypeName |
样本类型名 | 样本类型名 |
motherName |
母亲姓名 | 母亲姓名 |
newbornName |
新生儿姓名 | 新生儿姓名 |
sourceType |
cell_storage |
research_donation |
sourceTypeLabel |
细胞存储 | 科研捐赠 |
etaTime |
预计到达时间 | 预计到达时间 |
transportType |
运输方式 | 运输方式 |
waybillNo |
快递单号 | 快递单号 |
expressCompanyName |
快递公司名 | 快递公司名 |
carrierName |
携带人 | 携带人 |
carrierPhone |
携带人电话 | 携带人电话 |
文件:backend/app/api/sample_reception.py
GET /web/admin/sample-receptions/pending 新增可选参数:
sourceType:cell_storage / research_donation / 不传(全部)SampleReceptionCreateBody 新增字段:
source_type: Optional[str] = Field("cell_storage", alias="sourceType") # cell_storage / research_donation
donation_sample_id: Optional[str] = Field(None, alias="donationSampleId") # 科研捐赠时必传
当 source_type = 'research_donation' 时,protocolSampleId 可不传,改为传 donationSampleId。
文件:web-admin/src/views/quality-inspection/sample-reception/index.vue
在 filter-grid 中增加:
<el-form-item label="来源类型">
<el-select v-model="searchForm.sourceType" placeholder="全部" clearable>
<el-option label="细胞存储" value="cell_storage" />
<el-option label="科研捐赠" value="research_donation" />
</el-select>
</el-form-item>
在序号列后增加:
<el-table-column prop="sourceTypeLabel" label="来源类型" width="100" align="center" header-align="center">
<template #default="{ row }">
<el-tag :type="row.sourceType === 'research_donation' ? 'warning' : 'success'" size="small">
{{ row.sourceTypeLabel }}
</el-tag>
</template>
</el-table-column>
新增列(在快递单号之后):
transportType:运输方式(快递运输 / 人员携带)expressCompanyName:快递公司carrierName:携带人carrierPhone:携带人电话单个接收弹窗(receiveDialogVisible)和查看详情弹窗(viewDialogVisible)中,在"样本信息"区块后增加"运输信息"区块:
运输信息
运输方式:快递运输
快递公司:顺丰速运
快递单号:SF1234567890
预计到达:2026-05-20 14:00:00
携带人:张三 / 13800138000
使用 readonly-grid form-grid-2 布局,运输方式为"快递运输"时显示快递公司和快递单号,为"人员携带"时显示携带人和电话。
batch-receive-card__meta 区域增加来源类型标签和运输信息展示。
fetchSampleReceptionSubmit 调用时,增加 sourceType 和 donationSampleId 参数:
sourceType: 'cell_storage',传 protocolSampleIdsourceType: 'research_donation',传 donationSampleId文件:web-admin/src/api/sample-reception.ts
fetchSampleReceptionSubmit 参数类型增加:
sourceType?: 'cell_storage' | 'research_donation'
donationSampleId?: string // sourceType=research_donation 时必传
fetchSampleReceptionPendingList 参数增加 sourceType?: string。
当前 row-key="protocolSampleId",捐赠样本也复用此字段名(后端统一输出),无需修改。
| 层 | 文件 | 变更类型 |
|---|---|---|
| SQL | docs/sql/sample_reception_add_donation_source.sql(新建) |
ALTER TABLE |
| Model | backend/app/models/models.py(BizSampleReception) |
新增 3 字段 |
| CRUD | backend/app/crud/sample_reception.py |
新增捐赠查询函数 + 修改 pending/received/create |
| API | backend/app/api/sample_reception.py |
修改 schema + 路由参数 |
| 前端 API | web-admin/src/api/sample-reception.ts |
参数扩展 |
| 前端页面 | web-admin/src/views/quality-inspection/sample-reception/index.vue |
UI 变更 |
不涉及的文件(零影响):
create_work_order_from_reception 已基于 reception 对象创建,无需改动)create_work_order_from_reception() 接收 BizSampleReception 对象。新增的捐赠接收记录走同一函数,内部通过 reception.collection_protocol_id 查询协议信息。对于捐赠来源,需确认该函数是否依赖 collection_protocol_id 非空。
如依赖,则需在质检工单创建逻辑中增加 reception_source 判断,对捐赠来源改为通过 research_donation_id 查询捐赠单信息作为工单的关联协议。实施时需读取 qc_work_order.py 的 create_work_order_from_reception 函数确认并适配。
| 决策 | 选择 | 理由 |
|---|---|---|
| 统一表 vs 独立表 | 统一表加字段 | 用户要求统一入口,且现有细胞存储流程零改动 |
| 新增字段 vs 改用通用FK | 新增字段 | 不破坏现有数据引用,风险最低 |
| 质检工单触发 | 捐赠也触发 | 用户确认需求一致 |
| 前端字段名 | 保持 protocolSampleId 不变 | 后端统一输出,前端改动最小化 |