294 lines
12 KiB
Markdown
294 lines
12 KiB
Markdown
# 科研捐赠协议单 · 一键导入增强(单样本 / 多样本)
|
||
|
||
> 日期:2026-07-02
|
||
> 状态:已确认 / 已实现
|
||
> 关联模块:`web-admin` 捐赠协议单新增弹窗(`DonationFormDialog.vue`)
|
||
> 关联常量:`SAMPLE_TYPE_PERINATAL_HSC`、`SAMPLE_TYPE_UC_MSC`(`sample-production-reception-fields.ts`)
|
||
|
||
---
|
||
|
||
## 1. 背景与目标
|
||
|
||
### 1.1 业务诉求
|
||
|
||
科研捐赠协议单新增时,左侧「一键导入」支持粘贴客户提供的标准文本,解析后自动填充右侧表单。客户文本存在两种典型场景:
|
||
|
||
1. **单样本**:如围产期造血干细胞捐献,仅一种样本类型。
|
||
2. **多样本**:如同一产妇同时采集「围产期造血干细胞」与「脐带间充质干细胞」,文本中含多条不同样本的采集时间字段。
|
||
|
||
当前实现仅支持**首行标题推断一个样本类型**、**固定快递运输**、**胎次仅识别阿拉伯数字**、**时间格式不支持「点」**,无法正确导入多样本数据。
|
||
|
||
### 1.2 目标
|
||
|
||
1. 根据**采集时间字段名**推断应创建的样本类型与行数(1 行或 N 行)。
|
||
2. 正确映射运输方式:快递 → `kdys`,顺风车等 → `ryxd`(人员携带)。
|
||
3. 胎次支持中文数字(如「二胎」→ `2`)。
|
||
4. 中文时间支持 `18点05分` 格式(与 `12时17分` 等价)。
|
||
5. 保持「仅填充空白字段、不覆盖已有值」策略。
|
||
6. **不改后端表结构**;协议级 `sampleCollectTime` 取各样本采集时间中的**最早值**(已确认:选项 A)。
|
||
|
||
### 1.3 非目标(本期不做)
|
||
|
||
- 样本表增加独立 `sample_collect_time` 字段(方案 B,后续若产品要求再立项)。
|
||
- 配置化规则引擎(JSON/YAML 运营可改规则)。
|
||
- 细胞存储协议单一键导入(本模块无此功能)。
|
||
- 一键导入 UI 布局变更(仍为新增弹窗左侧粘贴区 + 右侧表单)。
|
||
|
||
---
|
||
|
||
## 2. 客户文本样例
|
||
|
||
### 2.1 单样本(捐献)
|
||
|
||
```
|
||
围产期干细胞(捐献)
|
||
产妇姓名:李会
|
||
新生儿性别:男
|
||
新生儿体重:3900g
|
||
新生儿评分: 9
|
||
分娩时间:2026年6月22日12时17分
|
||
造血干细胞采集时间:2026年6月22日12时18分
|
||
母血采集时间:未采集
|
||
分娩方式:顺产
|
||
胎次:3
|
||
采集医护:王天琪
|
||
采集医院:利辛县人民医院
|
||
运输方式:顺丰快递
|
||
销售人员:赵文静
|
||
采集医院:利辛县人民医院
|
||
```
|
||
|
||
**期望结果**:1 条样本(围产期造血干细胞);运输 `kdys` + 顺丰;胎次 `3`。
|
||
|
||
### 2.2 多样本(科研脐带)
|
||
|
||
```
|
||
围产期干细胞(科研脐带)
|
||
产妇姓名:陆玲
|
||
新生儿性别:男孩
|
||
新生儿体重:3700g
|
||
分娩时间:2026年3月14日 18点05分
|
||
造血干细胞样本采集时间:2026年3月14日 18点06分
|
||
脐带采集时间:2026年3月14日 18点08分
|
||
母血采集时间:2026年3月14日 19点40分
|
||
分娩方式:顺产
|
||
胎次:二胎
|
||
运输方式:顺风车
|
||
销售人员:张慧茹
|
||
采集医院:涡阳县中医院
|
||
```
|
||
|
||
**期望结果**:2 条样本(围产期造血干细胞、脐带间充质干细胞);协议级 `sampleCollectTime` = `2026-03-14 18:06`(最早);运输 `ryxd`,不填快递公司;胎次 `2`。
|
||
|
||
---
|
||
|
||
## 3. 业务规则
|
||
|
||
### 3.1 样本类型推断(优先级)
|
||
|
||
| 优先级 | 规则 | 说明 |
|
||
|--------|------|------|
|
||
| 1 | **采集时间字段** | 存在且值可解析 → 创建对应样本行(可多条) |
|
||
| 2 | **首行标题** | 无采集时间字段时,回退现有 `matchCellSampleType(header)` 逻辑 |
|
||
|
||
**采集时间字段 → 样本类型映射**:
|
||
|
||
| 字段名(含别名) | 目标类型名 | typeKey |
|
||
|------------------|------------|---------|
|
||
| `造血干细胞样本采集时间`、`造血干细胞采集时间`、`样本采集时间` | 围产期造血干细胞 | `hsc` |
|
||
| `脐带采集时间` | 脐带间充质干细胞 | `uc_msc` |
|
||
|
||
- 同一 typeKey 在文本中多次出现:以**首次有效时间**为准。
|
||
- 字段值为空或「未采集」等占位:不创建该样本行。
|
||
- 多样本场景下,**首行标题不参与**样本类型决定(避免「围产期干细胞(科研脐带)」歧义)。
|
||
|
||
### 3.2 协议级样本采集时间(已确认)
|
||
|
||
- 存储位置:`formData.sampleCollectTime` → `biz_research_donation_collection.sample_collect_time`。
|
||
- **多样本**:取所有已识别样本采集时间中的**最早时间**(minute 精度 `YYYY-MM-DD HH:mm`)。
|
||
- **单样本**:该样本采集时间写入协议级字段(与现逻辑一致)。
|
||
- 若协议级字段已有值:不覆盖(空白才填)。
|
||
|
||
### 3.3 运输方式映射
|
||
|
||
客户文本字段仍为 `运输方式`;解析结果区分 **模式** 与 **原始标签**:
|
||
|
||
| 客户文本特征 | `transportType` | 后续处理 |
|
||
|--------------|-----------------|----------|
|
||
| 含「快递」「顺丰」「圆通」「中通」「韵达」「EMS」等 | `kdys` | 匹配 `expressCompanyId`(现有 `matchExpressCompany`) |
|
||
| 含「顺风车」「顺丰车」(后者为笔误)、「自行」「人员携带」「自带」「自送」等 | `ryxd` | **不**填 `expressCompanyId`;可选后续人工填携带人 |
|
||
| 无法识别 | — | warning,不填运输 |
|
||
|
||
- 多样本时:各样本行复制**同一套**运输配置(客户数据中通常一致)。
|
||
- 仅当样本行 `transport.transportType` 为空时写入。
|
||
|
||
### 3.4 胎次
|
||
|
||
| 输入示例 | 解析结果 |
|
||
|----------|----------|
|
||
| `3` | `3` |
|
||
| `二胎`、`三胎` | `2`、`3` |
|
||
| `第二胎` | `2` |
|
||
| 无法解析 | warning,不填 |
|
||
|
||
实现:`parseBirthOrder(text)` — 先 `extractNumericPart`;若无阿拉伯数字,再解析中文数字(一~十及「N胎」模式)。
|
||
|
||
### 3.5 其他字段(保持 / 微调)
|
||
|
||
| 字段 | 规则 |
|
||
|------|------|
|
||
| 新生儿性别 | 扩展识别 `男孩`→`1`、`女孩`→`0` |
|
||
| 母血采集时间 | 「未采集」等 → 清空(已有) |
|
||
| 采集医院重复行 | 首次有效值为准(已有) |
|
||
| 母亲 / 医院 / 销售 | 远程匹配(已有) |
|
||
| 样本编码 | 每行样本 `assignDonationSampleCodeIfMissing`(已有) |
|
||
|
||
### 3.6 填充策略(不变)
|
||
|
||
- **仅填空**:目标字段为空才写入。
|
||
- **样本行**:若 `formData.samples` 已有同类型样本且 `sampleTypeId` 非空,不重复添加;若为空行可复用。
|
||
- 导入完成后:`ElMessage.success` 列出已填充项;`ElMessage.warning` 列出未匹配项。
|
||
|
||
---
|
||
|
||
## 4. 技术设计
|
||
|
||
### 4.1 解析层结构变更
|
||
|
||
**文件**:`web-admin/src/utils/donation-standard-import-parse.ts`
|
||
|
||
扩展 `DonationStandardImportParsed`(或新增子类型):
|
||
|
||
```typescript
|
||
export type DonationSampleImportHint = {
|
||
typeKey: 'hsc' | 'uc_msc'
|
||
typeName: string
|
||
collectTime?: string // 已解析为标准 datetime-minute
|
||
}
|
||
|
||
export type DonationStandardImportParsed = {
|
||
sampleTypeHeader: string
|
||
sampleHints: DonationSampleImportHint[] // 新增:0..N
|
||
transportModeText?: string // 原始运输方式文本
|
||
transportType?: 'kdys' | 'ryxd' // 新增:解析后的系统码
|
||
// ... 现有 common 字段保持不变
|
||
}
|
||
```
|
||
|
||
**新增 / 导出函数**:
|
||
|
||
| 函数 | 职责 |
|
||
|------|------|
|
||
| `parseBirthOrder(text)` | 阿拉伯 + 中文胎次 |
|
||
| `resolveTransportType(text)` | 运输文本 → `kdys` \| `ryxd` \| null |
|
||
| `matchSampleTypeByExactName(name, types)` | 按 `typeName` 精确匹配(优先于 segment 评分) |
|
||
|
||
**采集时间字段识别**:在 `KEY_ALIASES` 之外,对 key 做 pattern 匹配(支持「造血干细胞**样本**采集时间」等变体),命中后写入 `sampleHints` 而非单一 `sampleCollectTime`。
|
||
|
||
**回退**:`sampleHints.length === 0` 时,保留现有 `sampleCollectTime` + 首行 `matchCellSampleType` 行为。
|
||
|
||
### 4.2 时间解析增强
|
||
|
||
**文件**:`web-admin/src/utils/chinese-datetime-parse.ts`
|
||
|
||
- 正则扩展:`[时::点]` 均视为小时分隔符。
|
||
- 例:`2026年3月14日 18点06分` → `2026-03-14 18:06`。
|
||
|
||
**可选新文件**:`web-admin/src/utils/chinese-numeral-parse.ts`
|
||
|
||
- `parseChineseNumeral(text: string): number | null` — 支持「二」「二胎」「三胎」等;供 `parseBirthOrder` 调用。
|
||
|
||
### 4.3 填充层变更
|
||
|
||
**文件**:`web-admin/src/views/research-donation/list/DonationFormDialog.vue` — `handleStandardImport()`
|
||
|
||
```
|
||
1. parseDonationStandardImportText(text)
|
||
2. 并行:母亲 / 医院 / 销售 / 字典预加载(现有)
|
||
3. common 字段填空(性别、体重、评分、分娩时间、母血、分娩方式、胎次、医护…)
|
||
4. 样本行:
|
||
if sampleHints.length > 0:
|
||
for each hint:
|
||
matchSampleTypeByExactName(hint.typeName)
|
||
获取或创建对应样本行
|
||
assignDonationSampleCodeIfMissing
|
||
sampleCollectTime = min(hint.collectTime) // 最早
|
||
else:
|
||
现有单样本逻辑(首行 + 一行样本)
|
||
5. 运输:
|
||
if parsed.transportType === 'kdys': 匹配快递公司
|
||
if parsed.transportType === 'ryxd': transportType=ryxd,跳过 expressCompanyId
|
||
6. hydrateSampleLookupOptions + 消息反馈
|
||
```
|
||
|
||
### 4.4 数据模型(无变更)
|
||
|
||
| 实体 | 字段 | 说明 |
|
||
|------|------|------|
|
||
| `biz_research_donation_collection` | `sample_collect_time` | 协议级;多样本取最早采集时间 |
|
||
| `biz_research_donation_sample` | — | 无 per-sample 采集时间;本期不加 |
|
||
| `biz_research_donation_sample_transport` | `transport_type` | `kdys` / `ryxd` |
|
||
|
||
---
|
||
|
||
## 5. 测试
|
||
|
||
**新建**:`web-admin/src/utils/donation-standard-import-parse.test.ts`(或项目现有 vitest 路径)
|
||
|
||
| 用例 | 断言 |
|
||
|------|------|
|
||
| 单样本完整文本(§2.1) | 1 个 hint(hsc);transportType=kdys;birthOrder=3 |
|
||
| 多样本完整文本(§2.2) | 2 个 hint(hsc+uc_msc);最早时间 18:06;transportType=ryxd;birthOrder=2 |
|
||
| 胎次「二胎」「3」 | 2、3 |
|
||
| 时间「18点06分」「12时18分」 | 等价解析 |
|
||
| 仅首行无采集时间 | sampleHints 空,回退首行匹配 |
|
||
| 运输「顺风车」「顺丰快递」 | ryxd / kdys |
|
||
|
||
**手工验收**(`/#/market/research-donation/audit` 或 list 页新增):
|
||
|
||
1. 粘贴 §2.1 → 1 样本、顺丰、胎次 3。
|
||
2. 粘贴 §2.2 → 2 样本、人员携带、协议采集时间 18:06、胎次 2。
|
||
3. 先手动填部分字段再导入 → 已填字段不被覆盖。
|
||
|
||
---
|
||
|
||
## 6. 改动文件清单
|
||
|
||
| 文件 | 改动类型 |
|
||
|------|----------|
|
||
| `web-admin/src/utils/donation-standard-import-parse.ts` | 扩展解析:多样本 hints、运输、胎次 |
|
||
| `web-admin/src/utils/chinese-datetime-parse.ts` | 支持「点」 |
|
||
| `web-admin/src/utils/chinese-numeral-parse.ts` | 新建(可选,也可内联于 parse 文件) |
|
||
| `web-admin/src/views/research-donation/list/DonationFormDialog.vue` | `handleStandardImport` 多样本 + 运输 |
|
||
| `web-admin/src/utils/donation-standard-import-parse.test.ts` | 新建单元测试 |
|
||
|
||
**不改**:`backend/`、`docs/sql/`、OpenSpec(除非评审要求补充 ui-conventions 条目)。
|
||
|
||
---
|
||
|
||
## 7. 风险与限制
|
||
|
||
1. **协议级采集时间**:多样本各时间不同,列表/采集信息页仅展示一个时间;业务接受「最早时间」代表协议(已确认)。
|
||
2. **运输携带人**:`ryxd` 模式下客户文本通常不含携带人姓名;导入后可能需人工补全。
|
||
3. **未知样本类型**:若未来出现第三种采集时间字段,需扩展 `SAMPLE_COLLECT_TIME_RULES`;本期仅 hsc / uc_msc。
|
||
|
||
---
|
||
|
||
## 8. 验收标准
|
||
|
||
- [ ] §2.1 单样本文本一键导入后,样本 Tab 有 1 行「围产期造血干细胞」,运输为快递运输 + 顺丰。
|
||
- [ ] §2.2 多样本文本一键导入后,样本 Tab 有 2 行(造血 + 脐带),运输为人员携带,协议采集时间为 18:06。
|
||
- [ ] 胎次「二胎」解析为 2;「3」解析为 3。
|
||
- [ ] `18点06分` 时间正确解析。
|
||
- [ ] 已有字段不被覆盖;单元测试通过。
|
||
|
||
---
|
||
|
||
## 9. 决策记录
|
||
|
||
| 日期 | 决策 | 理由 |
|
||
|------|------|------|
|
||
| 2026-07-02 | 方案 A:纯前端增强,不改表 | 最小改动、与现架构一致 |
|
||
| 2026-07-02 | 协议级 `sampleCollectTime` 取**最早**采集时间 | 用户确认选项 A |
|
||
| 2026-07-02 | 多样本以采集时间字段为准,首行标题仅作回退 | 避免标题歧义 |
|