diff --git a/docs/superpowers/specs/2026-10-07-sample-freight-batch-design.md b/docs/superpowers/specs/2026-10-07-sample-freight-batch-design.md new file mode 100644 index 00000000..c6363a23 --- /dev/null +++ b/docs/superpowers/specs/2026-10-07-sample-freight-batch-design.md @@ -0,0 +1,259 @@ +# 样本运费批次 — 实现设计(方案 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)或手输文案。后端解析顺序: + 1. 按 `dict_value` 命中 → 使用该项; + 2. 否则按 `dict_label` trim 后精确匹配 → 使用已有项; + 3. 否则自动新增:`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 工具栏 + +1. **添加样本**(不变)。 +2. **配置运费**:须先勾选样本,否则禁用。 +3. **取消运费关联**:对勾选且已关联的样本解除关联;批次下若无剩余样本则软删批次。 + +### 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 + +样本返回例如: + +```json +"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. 验收标准 + +1. 预置「顺丰快递」「专线车」可选;手输新方式保存后,再次打开下拉可选到。 +2. 同一协议单内多样本可共用一笔运费,金额只录一次。 +3. 不同协议单(含细胞存储与科研捐赠)的样本可关联同一笔运费。 +4. 修改批次金额/方式后,所有关联样本展示一致。 +5. 取消关联后样本运费为空;批次无样本后软删。 +6. 合同金额、缴费明细不因运费变化。 +7. 新保存协议单不再依赖旧运输明细写入;查看弹窗不再展示单号/站点等旧字段。 + +--- + +## 9. 实现顺序(建议) + +1. SQL:字典类型/数据 + `biz_sample_freight` + `biz_sample_freight_item`(及唯一索引)。 +2. 后端:model / crud / api;字典自动入库;协议详情 enrichment;协议保存处理 `freightId` / `freightDraft`,停止写旧运输。 +3. 前端:API + 协议单公共运费组件 + 查看态。 +4. 测试:共用、跨来源关联、草稿 clientKey、字典自动新增、合同金额不受影响。 +5. 按仓库约定增量更新 graphify(backend / web-admin)。