Files
zkxw/docs/superpowers/specs/2026-06-23-pre-transit-sample-design.md
T
2026-06-24 10:39:55 +08:00

311 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 预产及在途样本 — 功能设计
**日期**: 2026-06-23
**状态**: 已实现
**界面模式**: P27 衍生列表(只读监控)+ 双分区单页;**列表样式对齐采集协议单列表**
**参考页**: `web-admin/src/views/cell-storage/protocol/index.vue`(列表壳层与列风格)、`research-donation/list/index.vue`(捐赠侧字段对齐)、`collection_protocol.py` 列表 `_row_to_dict`(样本概要字段)
## 1. 需求背景
客户在【质检管理】菜单下、【样本接收】上方新增 **【预产及在途样本】** 页面,用于质检人员集中查看接收前的两类待办:
| 分区 | 业务含义 | 列表粒度 |
|------|----------|----------|
| **上半区 · 在途样本** | 协议下存在 **已采集、未接收** 样本的协议单 | **协议单行**(样本为行内展示字段) |
| **下半区 · 预产样本** | 协议已建且 **样本未录入** 和/或 **样本未采集** | **协议单行**(样本为行内展示字段) |
**已确认展示维度(2026-06-23)**:上下半区均采用 **协议单维度** 展示;样本编码、类型、采集概况、运输/预计到达等作为 **协议行内的概要信息**,不拆成样本级多行。
**已确认列表样式**:参照 **【采集协议单】** 列表(`cell-storage/protocol/index.vue`)——`panel-top` + `filter-block` + `list-table`(`stripe`、列居中、`el-tag` 状态)+ `list-pager-bar`;**无**新增/编辑/删除工具栏与勾选列,操作列仅「查看」。
**已确认交互**:两个分区列表 **操作列仅「查看」**;按来源打开对应协议查看弹窗(采集 → `ProtocolViewDialog`,捐赠 → `DonationViewDialog`)。**不提供**接收、编辑、跳转样本接收等操作。
**已确认数据规则(上半区)**:仅展示 **`sample_status = 'ycj'`(已采集)且尚无有效接收记录** 的样本。
## 2. 与现有模块关系
- **【样本接收】**:操作入口(登记接收、批量接收、触发质检工单);本页为 **只读看板**,与之互补。
- **【采集协议单】列表**:列布局、筛选区结构、表格视觉的直接模板(见 §5.2)。
- **样本采集信息 Tab**:仅作查看弹窗 `handleView` 参考,不作列表列模板。
- **跨表关联键**:判断是否已接收 SHALL 使用 `tenant_no` + `sample_code`(OpenSpec `api-conventions` § 细胞存储样本跨表关联键),与 `sample_reception.py` 现有逻辑一致。
## 3. 数据判定规则
> **列表粒度**:先按 §3.1 / §3.2 判定 **协议是否入选**;再在行内挂载 **与本分区相关的样本概要**(`sampleItems` 为过滤后的子集,见 §3.4)。
### 3.1 上半区 · 在途(协议入选条件)
协议入选当且仅当:在其 `del_flag=0` 的样本子表中,**存在至少一条**同时满足:
```
sample_status = 'ycj'
AND NOT EXISTS 有效 biz_sample_reception(tenant_no + sample_code)
```
且协议本身:
```
del_flag = 0
AND tenant_no = 当前用户租户
AND protocol_status / donation_status NOT IN ('yzf', 'zky', 'zckc', 'ygq')
```
**行内样本展示(`sampleItems`)**:仅包含满足上述「在途」条件的样本行;附带各样本运输字段(`etaTime`、`waybillNo` 等)。
**行内汇总字段**(复用采集协议单列表逻辑):
- `sampleCodes`、`sampleTypeNames`、`labStorageCodes` — 由在途样本拼接
- `sampleCollectSummary` — 仅统计在途样本,如「2个已采集(待接收)」
- `etaTimeSummary` — 在途样本中最早的预计到达(用于排序与列展示,实施时命名可调整)
**协议级排序**:`etaTimeSummary` 升序(空值靠后)→ 协议 `create_time` 升序。
**关键词**:协议号、母亲姓名/电话/身份证、在途样本编码、实验室入库编码。
### 3.2 下半区 · 预产(协议入选条件)
协议入选当满足 **公共协议条件** 且符合 **下列 A 或 B**:
**公共协议条件**:
```
del_flag = 0
AND tenant_no = 当前用户租户
AND protocol_status / donation_status IN ('dsx', 'sxz') -- 排除 ygq
AND NOT IN ('yzf', 'zky', 'zckc')
```
| 子场景 | preDeliveryKind | 入选条件 |
|--------|-----------------|----------|
| A · 样本未录入 | `no_sample` | 无任何 `del_flag=0` 样本子表行 |
| B · 样本未采集 | `uncollected` | 存在 `sample_status` 为空的样本行 |
同一协议若同时无样本行与未采集样本不可能并存;若 **仅有未采集样本** 为 `uncollected`;**仅有零样本** 为 `no_sample`。若协议同时含未采集样本与在途样本,**可同时出现在上下两区**(各分区一行,样本概要不同)。
**行内样本展示**:
- `no_sample`:`sampleItems=[]`,`sampleCollectSummary` 展示「样本未录入」
- `uncollected`:`sampleItems` 仅含未采集样本;`sampleCollectSummary` 如「2个未采集」
**协议级排序**:预产期 `expected_delivery_date` 降序 → 协议 `create_time` 降序。
**关键词**:协议号、母亲姓名/电话/身份证、未采集样本编码。
### 3.3 数据归属总览(协议维度)
| 场景 | 上半区 · 在途 | 下半区 · 预产 |
|------|---------------|---------------|
| 协议无样本行 | — | **展示**(`no_sample`) |
| 仅有未采集样本 | — | **展示**(`uncollected`) |
| 仅有已采集未接收样本 | **展示** | — |
| 兼有未采集 + 已采集未接收 | **展示**(在途概要) | **展示**(未采集概要) |
| 全部样本已接收 | — | — |
### 3.4 行内样本概要结构(与采集协议单列表对齐)
后端复用 `collection_protocol.list` / `research_donation.list` 的组装方式,统一返回:
| 字段 | 说明 |
|------|------|
| `sampleItems[]` | `{ sampleTypeName, sampleCode, sampleStatus, labStorageCode, transport? }`,**已按分区过滤** |
| `sampleTypeNames` | 逗号拼接 |
| `sampleCodes` | 逗号拼接 |
| `labStorageCodes` | 逗号拼接 |
| `sampleCollectSummary` | 文字概要(分区语义不同) |
| `preDeliveryKind` | 仅下半区:`no_sample` / `uncollected`(协议同时仅一种预产态;若仅 `uncollected` 则标 `uncollected`) |
上半区在途样本的 `transport.etaTime` 可汇总到列 **预计到达**(多样本取最早或展示「详见概要」,实施取最早一条)。
## 4. 后端设计
### 4.1 新增 API(推荐独立模块,方案 A)
| 接口 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 在途样本列表 | GET | `/web/admin/pre-transit-samples/in-transit` | 上半区 |
| 预产协议列表 | GET | `/web/admin/pre-transit-samples/pre-delivery` | 下半区 |
**公共查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| page | int | 页码 |
| pageSize | int | 每页条数(≤100) |
| keyword | string | 关键词 |
| sourceType | string | 可选:`cell_storage` / `research_donation`,空=全部 |
**在途列表额外参数**(可选一期):
| 参数 | 类型 | 说明 |
|------|------|------|
| etaTimeStart | string | 预计到达开始 YYYY-MM-DD |
| etaTimeEnd | string | 预计到达结束 YYYY-MM-DD |
**权限**:`require_menu_code("biz:preTransitSample:list")`;`apply_data_scope` 挂在 **协议主表**(`BizCollectionProtocol` / `BizResearchDonation`),与采集协议单列表一致。
**实现要点**:
1. 先查询符合分区条件的 **协议 ID 集合**(采集 + 捐赠 UNION,分页在协议级)。
2. 批量加载当事人、采集信息、样本、运输、接收记录,组装与 `list_collection_protocols` 同构的协议行。
3. 按分区规则 **过滤 `sampleItems`** 并生成 `sampleCollectSummary` 等概要字段。
**实现文件**:
- `backend/app/crud/pre_transit_sample.py` — 协议级查询与行组装(可抽取与 `collection_protocol` 共用的样本概要 helper)
- `backend/app/api/pre_transit_sample.py` — 路由
- 注册:`app/api/__init__.py`、`app/main.py`
### 4.2 列表响应字段(上下半区均为协议行)
在 **采集协议单列表行** 基础上扩展,细胞存储与捐赠 **字段名统一**(捐赠的 `donationStatus` 映射为 `protocolStatus` 展示,`donationNo` 映射为 `docNo` 或保留双字段由前端列模板分支)。
**协议主信息**(对齐 `protocol/index.vue` 列):
| 字段 | 列名 |
|------|------|
| sourceType, sourceTypeLabel | 来源类型(新增,Tag 展示) |
| protocolStatus | 状态 |
| registerDate / donationDate | 登记日期 |
| motherName, motherPhone, motherIdCard | 母亲信息 |
| fatherName | 父亲姓名 |
| newbornSex, deliveryMode, newbornWeightGram | 新生儿信息 |
| regionName, hospitalName | 采集地区/医院 |
| collectorUserName, salesUserName | 采集专员/销售 |
| sampleCollectTime | 样本采集时间 |
| createTime | 创建时间 |
| expectedDeliveryDate | 预产期(下半区排序用,上半区可展示) |
**样本概要**(行内展示,非独立行):
| 字段 | 列名建议 |
|------|----------|
| sampleTypeNames | 样本类型 |
| sampleCodes | 样本编码 |
| labStorageCodes | 实验室入库编码 |
| sampleCollectSummary | 样本采集概况 |
| etaTimeSummary | 预计到达(**上半区**) |
| preDeliveryKindLabel | 预产类型(**下半区**:样本未录入 / 样本未采集) |
**查看用 ID**:`collectionProtocolId` 或 `researchDonationId`(二选一有值)、`sourceType`。
### 4.3 预产列表响应字段
与 §4.2 相同;额外必有 `preDeliveryKind`、`preDeliveryKindLabel`。
## 5. 前端设计
### 5.1 菜单与路由
| 项 | 值 |
|----|-----|
| 菜单名 | 预产及在途样本 |
| 父级 | 质检管理 `QCM00000000000000000000000000001` |
| 排序 | `order_num = 0`(位于样本接收 `order_num = 1` 之上) |
| 路由 path | `pre-transit-sample` |
| 组件 | `/quality-inspection/pre-transit-sample/index` |
| menuCode | `biz:preTransitSample:list` |
同步修改 `web-admin/src/router/modules/sqxp.ts`。
### 5.2 页面结构(对齐采集协议单列表)
```
page-container
└── section.panel
├── filter-block(结构同 protocol/index.vue:关键词、来源类型、创建时间等;下半区可不单独筛预产类型)
├── panel-top → h2.title-bar-heading「在途样本」
├── list-body.list-wrap
│ ├── el-table.list-table(列布局参照 protocol/index.vue + 来源/样本概要列)
│ └── pagination.list-pager-bar
├── panel-top → h2.title-bar-heading「预产样本」
└── list-body.list-wrap(同上)
```
**与采集协议单列表的差异**:
| 采集协议单列表 | 本页 |
|----------------|------|
| 有勾选列、新增/批量删除工具栏 | **无**工具栏、无勾选 |
| 操作:查看 + 更多(编辑/删除) | **仅查看**(`btn-op-item` + View,与协议列表查看按钮一致,不设 `primary` 亦可与协议页保持一致) |
| 仅细胞存储 | 增加 **来源类型** 列;捐赠行状态 Tag 用 `donationStatusTagType` |
| 无双分区 | **上下两个独立表格 + 分页** |
**表格列(建议顺序)**:序号 → 来源类型 → 状态 → 登记日期 → 母亲姓名 → 母亲电话 → 母亲身份证 → 父亲姓名 → 新生儿性别 → 分娩方式 → 新生儿体重 → 采集地区 → 采集医院 → 采集专员 → 销售 → **样本类型** → **样本编码** → **样本采集概况** → 样本采集时间 → **预计到达**(上半区)/ **预产类型**(下半区)→ 创建时间 → 操作。
- 列表:`list-table` + `stripe`;列 `align="center"`;状态/来源用 `el-tag`。
- **在途列表不对预计到达过期行标红**。
- 操作列:`fixed="right"`,`min-width ≥ 120`。
- 两个分区 **独立分页**。
**实现建议**:抽取 `ProtocolListTableColumns` 或在本页复制 `protocol/index.vue` 列定义并追加样本概要列,避免与录入页工具栏耦合。
### 5.3 查看交互
```typescript
function handleView(row) {
if (row.sourceType === 'research_donation') {
// DonationViewDialog :donation-id="row.researchDonationId"
} else {
// ProtocolViewDialog :protocol-id="row.collectionProtocolId"
}
}
```
挂载组件(默认配置,不展开审核/储存证等扩展 Tab):
- `@/views/cell-storage/protocol/ProtocolViewDialog.vue`
- `@/views/research-donation/list/DonationViewDialog.vue`
参照:`SampleCollectionInfoTab.vue`、`DonationSampleCollectionInfoTab.vue` 的 `handleView` 与 `listRow` 摘要传参。
### 5.4 API 模块
**文件**:`web-admin/src/api/pre-transit-sample.ts`
- `fetchPreTransitInTransitList(params)`
- `fetchPreTransitPreDeliveryList(params)`
## 6. 数据库与权限 SQL
**文件**:`docs/sql/alter_sys_menu_add_pre_transit_sample.sql`
- 新增菜单 C 类型:`biz:preTransitSample:list`
- 新增按钮 F 类型:`biz:preTransitSample:list`(查询,与菜单 perms 一致即可)
- 将原「样本接收」`order_num` 调整为 2(若新菜单占 1)或新菜单 0、样本接收保持 1
菜单 `id` 使用新 UUID(实施时生成,避免与现有冲突)。
## 7. 非目标(一期不做)
- 列表导出
- 从本页直接接收样本
- 双击行打开查看(可二期加,一期仅按钮)
- 修改【样本接收】待接收 Tab 的过滤逻辑(保持现网行为)
## 8. 测试要点
1. 上半区:仅含「存在 ycj 且未接收样本」的协议;行内 `sampleItems` 仅含在途样本;已接收、纯未采集协议不出现。
2. 下半区:仅 `dsx`/`sxz` 且非 `ygq`;`no_sample` / `uncollected` 协议各一行;同一协议可同时出现在上下区时样本概要不同。
3. 列表为协议维度,列风格与采集协议单列表一致;样本信息以概要列展示,非样本级多行。
3. 细胞存储行点查看打开 `ProtocolViewDialog`;捐赠行打开 `DonationViewDialog`。
4. 租户隔离、数据权限与关键词搜索正确。
5. 菜单显示在样本接收上方;无权限用户不可访问 API。
## 9. 已确认项汇总
| # | 项 | 结论 |
|---|-----|------|
| 1 | 上半区数据范围 | **仅 `ycj` + 未接收** |
| 2 | 操作列 | **仅查看** |
| 3 | 查看目标 | 采集 → 采集协议单;捐赠 → 捐赠协议单 |
| 4 | 布局 | 上下双分区、分开展示 |
| 5 | 来源 | 采集协议单 + 捐赠协议单均覆盖 |
| 6 | 下半区协议状态 | **仅 `dsx` + `sxz`,排除 `ygq` 已过期** |
| 7 | 在途过期样式 | **不标红、不特殊标记预计到达已过期行** |
| 8 | 下半区数据范围 | **样本未录入 + 样本未采集** |
| 9 | 列表粒度 | **上下半区均为协议单维度**;样本为行内概要字段 |
| 10 | 列表样式 | **参照采集协议单列表**(`protocol/index.vue`) |
## 10. 待确认(可选,不阻塞开发)
1. 筛选区是否在共用关键词之外,为在途区单独提供「预计到达日期」范围?