Capture the agreed approach: freight batch entity linked at sample level from protocol forms, dictionary-backed transport modes, internal cost only. Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
样本运费批次 — 实现设计(方案 1)
日期:2026-10-07
状态:已确认(2026-10-07)
关联:细胞存储 / 科研捐赠「采集协议单」样本及运输信息;内部成本登记(不向客户收取)
1. 背景
1.1 现状
细胞存储与科研捐赠协议单共用 SampleTransportFormSection:
- 运输挂在每个样本上(
biz_protocol_sample_transport/biz_research_donation_sample_transport)。 - 运输类型为固定枚举:
kdys(快递运输)/ryxd(人员携带)。 - 快递场景下人工填写快递公司、单号、目的站点、样本级快递费用、预计到达等。
- 批量「配置运输信息」只能把同一套信息复制到当前协议单内勾选样本,无法表达「多协议单共用一笔运费、只收一次」。
1.2 客户诉求
- 运输方式:默认「顺丰快递」「专线车」;支持填写其他方式;若字典中不存在则自动加入字典,后续可选。
- 运费:单位元;可对应多个样本(含跨多个协议单),例如同一专线车只登记一次费用。
- 本阶段只登记内部成本,不向客户收取,不进合同金额 / 缴费。
- 旧物流细节(单号、站点等)本阶段不要;后续再扩展。
1.3 已确认决策
| 项 | 结论 |
|---|---|
| 录入时机 | 录采集协议单时填写(已知会与其他单共用) |
| 关联粒度 | 样本级(可跨协议;单内也可仅部分样本) |
| 与旧运输 | 替换样本快递费用与旧运输录入;单号/站点等本阶段不做 |
| 费用性质 | 仅内部成本登记 |
| 架构 | 运费批次实体 + 协议单内新建/关联(方案 1);暂不做独立运费中心 |
2. 目标与非目标
2.1 目标
- 新建「运费批次」:运输方式(字典)+ 运费金额;多样本可关联同一批次。
- 支持跨细胞存储 / 科研捐赠协议单的样本共用一笔运费。
- 协议单 Tab「样本及运输信息」提供「配置运费」:新建 / 关联已有 / 修改方式与金额 / 取消关联。
- 运输方式字典预置「顺丰快递」「专线车」;手输不存在项时自动写入字典。
- 列表与查看弹窗展示运输方式 + 运费(及共用信息);合同/缴费逻辑不读运费。
2.2 非目标
- 独立运费菜单/业务中心(后续可加轻量查询列表)。
- 运费计入合同金额、缴费、向客户分摊或开票。
- 快递单号、目的站点、预计到达、携带人等物流字段。
- 旧运输记录自动迁移为运费批次。
- 小程序端改造。
- 物理删除旧运输表(保留兼容历史只读数据)。
3. 数据模型
3.1 运费批次主表 biz_sample_freight
| 字段 | 说明 |
|---|---|
id |
UUID PK |
transport_mode |
运输方式字典值(sample_transport_mode) |
freight_amount |
运费(元),Numeric(18,2),≥ 0 |
| 公共字段 | tenant_no、order_num、remark、create_by、create_dept_id、create_time、update_by、update_time、del_flag |
字段顺序遵循 database-conventions:id → 业务字段 → 公共字段。
3.2 样本关联表 biz_sample_freight_item
| 字段 | 说明 |
|---|---|
id |
UUID PK |
freight_id |
运费批次 ID |
sample_source |
cell_storage | research_donation |
sample_id |
样本主键 |
sample_code |
样本编码快照(可选,便于展示) |
protocol_id |
所属协议单 ID(便于反查) |
| 公共字段 | 同上 |
约束(业务层强制,建议唯一索引):
同一租户下,(sample_source, sample_id) 且 del_flag=0 至多对应一条关联(一个样本最多挂一笔运费)。
3.3 字典 sample_transport_mode
-
预置两项:
dict_value dict_label sfkd顺丰快递 zxc专线车 -
创建/更新运费时:前端可传
transportMode(已有 value)或手输文案。后端解析顺序:- 按
dict_value命中 → 使用该项; - 否则按
dict_labeltrim 后精确匹配 → 使用已有项; - 否则自动新增:
dict_label= trim 后文案,dict_value= 同文案(若 value 冲突则加短后缀保证唯一),再保存运费。
- 按
-
仅当前租户自动新增;不做跨租户共享。
3.4 与旧运输表
biz_protocol_sample_transport、biz_research_donation_sample_transport:保留不删。- 本阶段协议单保存路径不再写入旧运输明细。
- 历史数据不自动迁移;需补录时人工「配置运费」。
- 下游若仍读旧运输字段:无数据则展示空,不阻断主流程;物流扩展时再对齐。
4. 协议单交互
细胞存储 ProtocolFormDialog / ProtocolViewDialog 与科研捐赠 DonationFormDialog / DonationViewDialog 行为一致;公共组件由「配置运输信息」改为「配置运费」(可演进现有 SampleTransportFormSection)。
4.1 列表
- 移除列:运输类型、目的站点、预计到达、快递/携带摘要。
- 新增:运输方式、运费(元);共用时显示同一金额,并标注共用(如关联样本数或「共用」)。
- 未关联运费显示「—」。
4.2 工具栏
- 添加样本(不变)。
- 配置运费:须先勾选样本,否则禁用。
- 取消运费关联:对勾选且已关联的样本解除关联;批次下若无剩余样本则软删批次。
4.3 「配置运费」弹窗
模式 A · 新建运费
- 运输方式:可搜索下拉,允许手输;提交时字典无则自动新增。
- 运费(元):必填,≥ 0。
- 确定:创建批次并挂上当前勾选样本。
模式 B · 关联已有运费
- 分页检索近期批次:运输方式、金额、已关联样本数、创建时间。
- 确定:将勾选样本挂到该批次;若样本已有运费,提示将替换原关联后执行。
默认「新建」;可切换「关联已有」。
4.4 编辑与跨单
- 勾选样本若同属一批次,可在弹窗中改运输方式/金额;保存前提示「将同步 N 个关联样本」。
- 协议单 B 可通过「关联已有」挂到协议单 A 创建的批次。
- 查看态仅展示运输方式 + 运费(及共用信息)。
4.5 样本价格(细胞存储)
- 样本「原价格 / 优惠价格」与合同金额联动逻辑保持不变。
- 运费与合同金额、缴费无联动。
5. 接口与保存规则
5.1 运费 API(前缀 /api/admin)
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /sample-freights |
分页检索(关联已有) |
| GET | /sample-freights/{id} |
详情(含关联样本:来源、协议、编码) |
| POST | /sample-freights |
新建批次并挂样本 |
| PUT | /sample-freights/{id} |
更新方式/金额 |
| POST | /sample-freights/{id}/link |
追加关联 |
| POST | /sample-freights/{id}/unlink |
取消关联;无样本则软删批次 |
请求/响应字段使用 camelCase(transportMode、freightAmount、sampleSource、sampleId、protocolId、sampleCode、linkedSampleCount 等)。
创建/更新时执行运输方式字典自动入库。
5.2 与协议单保存
已落库样本(编辑已有协议,或协议已保存过):
- 「配置运费」确定后立即调用运费 API。
- 协议单保存不再嵌套旧
transport整包写入。
未落库草稿样本(新建协议未保存):
- 前端用
freightDraft.clientKey让多个草稿样本共用同一笔草稿运费。 - 协议单创建/更新 body 中样本可带:
freightId:关联已有批次;或freightDraft: { clientKey, transportMode, freightAmount }:同一clientKey只创建一笔批次再挂样本。
- 保存成功后详情返回真实
freightId;之后改价走独立 API。
5.3 校验
transportMode、freightAmount必填;金额 ≥ 0。- 同一租户样本不可同时挂两笔运费;重新配置 = 解旧链再挂新链(须提示)。
- 全部查询/写入按当前用户
tenant_no隔离,并套数据权限(与协议样本一致)。
5.4 协议详情 enrichment
样本返回例如:
"freight": {
"id": "...",
"transportMode": "zxc",
"transportModeLabel": "专线车",
"freightAmount": 800,
"linkedSampleCount": 3
}
5.5 历史数据
- 旧运输只读保留,不迁移。
- 新编辑以运费批次为准。
6. 前端改造要点
| 位置 | 改动 |
|---|---|
sample-transport-form-section |
改为运费配置(或拆出 SampleFreightFormSection);列表列与弹窗按 §4 |
ProtocolFormDialog / DonationFormDialog |
接入运费草稿与保存 payload;去掉对旧 transport 必填依赖 |
ProtocolViewDialog / DonationViewDialog |
样本运输区改为方式 + 运费 |
| API 模块 | 新增 sample-freight.ts(或等价) |
| 字典 | 初始化 sample_transport_mode;下拉允许创建 |
UI 遵循 Fresh Mint / 现有协议弹窗约定:label-position="top"、dialog-form-layout、无 backdrop blur、按钮带图标。
7. 风险与对策
| 风险 | 对策 |
|---|---|
| 新建协议样本无服务端 ID | freightDraft.clientKey,协议保存时建批次 |
| 跨单改价影响他人样本 | 改价前提示同步关联样本数 |
| 字典手输脏数据/重复 | 租户内按 trim 后的 label 去重;实现阶段可再加强规范化 |
| 下游仍读旧运输 | 空值展示,不阻断;后续扩展再对齐 |
| 历史仅有旧运输 | 不迁移;人工补录运费 |
8. 验收标准
- 预置「顺丰快递」「专线车」可选;手输新方式保存后,再次打开下拉可选到。
- 同一协议单内多样本可共用一笔运费,金额只录一次。
- 不同协议单(含细胞存储与科研捐赠)的样本可关联同一笔运费。
- 修改批次金额/方式后,所有关联样本展示一致。
- 取消关联后样本运费为空;批次无样本后软删。
- 合同金额、缴费明细不因运费变化。
- 新保存协议单不再依赖旧运输明细写入;查看弹窗不再展示单号/站点等旧字段。
9. 实现顺序(建议)
- SQL:字典类型/数据 +
biz_sample_freight+biz_sample_freight_item(及唯一索引)。 - 后端:model / crud / api;字典自动入库;协议详情 enrichment;协议保存处理
freightId/freightDraft,停止写旧运输。 - 前端:API + 协议单公共运费组件 + 查看态。
- 测试:共用、跨来源关联、草稿 clientKey、字典自动新增、合同金额不受影响。
- 按仓库约定增量更新 graphify(backend / web-admin)。