# 细胞存储三模块现状分析(改码参考) **日期**:2026-10-03 **状态**:现状梳理(非改造设计) **用途**:后续按新思路改代码前的权威对照基线;描述「当前代码实际如何工作」,而非「应当如何」。 **覆盖模块**: | 菜单 | Hash 路由 | menuCode | |------|-----------|----------| | 采集协议单 | `/#/cell-storage/protocol-audit` | `biz:cellStorageProtocolAudit:list` | | 样本信息管理 | `/#/cell-storage/sample-info` | `cell-storage:sample-info:list` | | 缴退费财务确认及开票 | `/#/cell-storage/payment-confirm` | `biz:cellStoragePaymentConfirm:list` | **需求权威**:`docs/中科兴皖需求V0.3.md` §2.2.6~2.2.9 **相关设计文档**(增量能力): - `docs/superpowers/specs/2026-05-13-cell-storage-protocol-modules-design.md`(早期模块设计) - `docs/superpowers/specs/2026-05-19-protocol-donation-convert-design.md`(转科研) - `docs/superpowers/specs/2026-06-04-protocol-pay-mode-switch-design.md`(缴费模式切换,若存在) - `docs/superpowers/specs/2026-06-17-protocol-invoice-request-design.md`(开票) - `docs/superpowers/specs/2026-06-01-sample-qc-sms-notification-design.md` - `docs/superpowers/specs/2025-05-23-storage-entry-sms-notification-design.md` - OpenSpec:`openspec/changes/zkxw-engineering-standards/specs/api-conventions/spec.md`(细胞存储样本跨表关联键) --- ## 1. 总览:双并行业务架构 采集协议单是源头。创建后并行两条线: ``` ┌──────────────────── 细胞存储 ────────────────────┐ │ 【采集协议单】 合同 + 客户 + 样本源头 + 缴费配置 │ │ │ │ │ ├──────────────┬─────────────────────────┤ │ ▼ ▼ │ │ 【样本信息管理】 【缴退费财务确认及开票】 │ │ 只读运营台+短信 财务终审+开票完成 │ │ │ ▲ │ │ ▼ │ 业务侧发起 │ │ 接收→质检→生产→入库 缴费信息 Tab / 满月待办 │ └──────────────────────────────────────────────────┘ ``` | 模块 | 角色 | 本质 | |------|------|------| | 采集协议单 | 市场/采集专员 | 主单据 CRUD、状态变更、缴费发起、开票申请入口 | | 样本信息管理 | 运营 | **只读查询台** + 线下短信提取/标记(不登记接收、不做质检入库操作) | | 缴退费财务确认及开票 | 财务 | 收款/退费确认 + 开票完成 | **菜单演进**:样本相关 Tab、缴费确认、开票已从早期「单一协议管理页」拆出;隐藏路由仍保留 `protocol`(录入表格版)、`payment-info` 等兼容入口。 --- ## 2. 数据模型(以协议为中心) ``` biz_collection_protocol (1) ├── biz_protocol_party_customer (1) 当事人/客户 ├── biz_protocol_collection_info (1) 采集信息 ├── biz_protocol_sample (N) │ └── biz_protocol_sample_transport ├── biz_protocol_payment (1) │ └── biz_protocol_payment_detail (N) ├── biz_protocol_audit (N) 审核记录(不改协议状态) ├── biz_protocol_storage_cert ├── biz_protocol_fullmoon_todo ├── biz_protocol_invoice_request (N) │ └── biz_protocol_invoice_request_item (N) └── biz_protocol_donation_convert (0..1) 与科研捐赠互转 ``` ### 2.1 跨表关联硬规则(OpenSpec) ``` 关联键 = tenant_no + sample_code 禁止用 collection_protocol_id / protocol_sample_id 做业务匹配 ``` 原因:协议编辑可能重建样本行 `id`;协议可转科研;样本编码不变。接收防重、详情回显、质检/入库 JOIN 均按此规则。 ### 2.2 协议主表关键字段 | 字段 | 含义 | |------|------| | `protocol_no` / `protocol_no_id` | 协议号(可自动分配号池) | | `protocol_status` | `dsx` 待生效 / `sxz` 生效中 / `ygq` 已过期 / `yzf` 已作废 / `sdfq` 属地废弃 / `zky` 转科研 | | `protocol_attachments` / `consent_form_urls` / `health_survey_form_urls` | 附件 URL 逗号分隔 | | `void_type` | 作废类型(字典);作废时必填 | | `collector_user_id` | 采集专员 | | `effective_*` / `expire_*` / `void_*` / `to_research_*` | 各状态变更时间与描述 | 无效态 `yzf` / `sdfq`:下游接收、生产列表会过滤。 --- ## 3. 【采集协议单】模块 ### 3.1 前端结构 ``` web-admin/src/views/cell-storage/ ├── protocol/ # 隐藏录入列表 │ ├── index.vue │ ├── ProtocolFormDialog.vue # 新增/编辑大表单(权威录入) │ ├── ProtocolViewDialog.vue │ ├── ProtocolAuditDialog.vue │ └── ProtocolStatusChangeDialog.vue └── protocol-audit/ # 主管理页 ├── index.vue # Pill Tab ├── ProtocolAuditTab.vue ├── PaymentInfoTab.vue # 业务侧缴费看板 ├── StorageCertTab.vue ├── FullmoonTodoTab.vue # 满月保险 + 开票申请入口 └── Sample*InfoTab.vue # 被 sample-info 复用 ``` API:`web-admin/src/api/collection-protocol.ts` ### 3.2 协议状态机 ``` 创建 → [dsx] 待生效 │ 协议生效 ▼ [sxz] 生效中 ──┬── 过期 → [ygq] ├── 作废 → [yzf](须 voidType) ├── 属地废弃 → [sdfq] └── 转科研 → [zky](convert API,建捐赠单) ``` - **审核独立**:只写 `biz_protocol_audit`(`shtg`/`shth`),不驱动 `protocol_status`。 - **协议号**:空则 `allocate_next_biz_no`;已存在不可改。 - **删除**:软删主单 + 样本 + 运输,释放样本编码占用。 ### 3.3 表单保存顺序(API 硬约束) `_persist_protocol_sub_resources`(`backend/app/api/collection_protocol.py`): ``` 1. party_customer(可 upsert biz_customer) 2. collection_info 3. payment + payment_details ← 必须在样本之前 4. samples(含运输) 5. storage_cert ``` 原因:缴费校验失败时不得已占用样本编码。创建失败补偿 `soft_delete_collection_protocol`。 ### 3.4 ProtocolFormDialog 字段分区 | Tab | 主要字段 | payload 路径 | |-----|----------|--------------| | 基本信息 | 协议状态*、登记日、采集专员、备注、三类附件 | 主表字段 + attachments | | 采集信息 | 地区、医院、医护、销售、样本/母血采集时间 | `collectionInfo` | | 样本及运输 | 类型、编码、价格、年限、状态、特殊标记、运输 | `samples[]` + `transport` | | 客户信息 | 关联客户、母亲/父亲/新生儿全套 | `partyCustomer` | | 缴费信息 | 模式、合同额、分期参数、明细行 | `payment` + `paymentDetails` | - `formRules` **仅强制** `protocolStatus`。 - 组件:`sample-transport-form-section`(样本+运输权威 UI)。 - 附件:卡片化立即上传(OpenSpec UI 附件约定以本弹窗为样例)。 **提交前前端校验顺序**: 1. el-form validate 2. 缴费模式 ↔ 有效样本数(`djfq` 仅 2 样本) 3. 新建时样本编码 `usedInProtocol` 占用拦截 4. 趸交应缴合计 ≠ 合同额 → **仅 warning,不阻断** 5. `syncFqPaymentDetailRowCount` → POST/PUT ### 3.5 缴费默认定价(样本数 × 模式) `web-admin/src/utils/protocol-payment-presets.ts`: | 有效样本数(≠ yzf) | 可选模式 | 合同金额示例 | |---------------------|----------|-------------| | 1 | `dj` / `fq` | 趸交 22880;分期 25880(6 期) | | 2 | `dj` / `djfq` / `fq` | 趸交 37880;趸交+分期 41000;分期 44000 | ### 3.6 缴费模式切换矩阵 前端:`protocol-pay-mode-switch.ts` 后端:`protocol_pay_mode_switch.py`(`LOCKED_STATUSES` / `BLOCKING_STATUSES`) | 从 → 到 | 条件 | 结果 | |---------|------|------| | 任意 | 存在进行中确认(前端还拦 `ytf`) | 阻断 | | `dj` → `fq`/`djfq` | 已有锁定明细 | 阻断 | | `dj` → `fq`/`djfq` | 全待缴 | 确认后按期数重建 | | `fq`/`djfq` → `dj` | 已全部缴清 | 阻断 | | `fq`/`djfq` → `dj` | 有已缴+未缴 | 保留 yjf,作废 dfj,新增剩余趸交 | | `fq` ↔ `djfq` | 已有锁定 | 阻断;否则重建 | 后端保存 P0:`jfqrz`/`tfqrz` 禁止保存;分期条数=期数;比例合计 100%;期数不少于已缴最大期次。 **注意**:前端 `BLOCKING` 含 `ytf`,后端不含(已退费允许随协议保存只读明细)。 ### 3.7 后端核心文件 | 路径 | 角色 | |------|------| | `backend/app/api/collection_protocol.py` | 协议/缴费/储存证/满月/开票 API | | `backend/app/crud/collection_protocol.py` | 核心 CRUD(约 3900+ 行) | | `backend/app/services/protocol_pay_mode_switch.py` | 缴费校验 | | `backend/app/crud/collection_protocol_no.py` | 协议号池 | | `backend/app/models/models.py` | ORM | --- ## 4. 【样本信息管理】模块 ### 4.1 定位 **不是**样本录入页,**不是**接收/质检/入库操作台。 是协议样本的 **全链路查询 + 客户短信触达台**(组件复用自 `protocol-audit/`): | Tab | 职责 | 短信 | |-----|------|------| | 样本采集信息 | 采集进度 | 无 | | 样本接收信息 | 接收结论 | 有 | | 样本质检信息 | 入库质检结论 | 有 | | 样本入库信息 | 入库/拒入库 | 有 | 深链:`?tab=sample-collection|sample-reception|sample-qc|storage-entry` 旧路由 `sample-reception-info` / `storage-entry-info` 重定向到本页。 ### 4.2 样本状态 `sample_status` ``` (空) 未采集 → ycj 已采集 → jcz 检测中 → yrk 已入库 任意 → yzf 已作废 ``` - 协议表单录入默认常为 `ycj`。 - **接收登记不改** `sample_status`。 - **入库**:`store_sample` 按 `sample_code` 回写 `yrk` + `storage_in_date`。 - **取消入库**:若原为 `yrk` → 回退 `jcz`,清空 `storage_in_date`。 ### 4.3 真实写库链路(本页只读展示) 接收结论 = `js` 时(`create_sample_reception`): ``` 写 biz_sample_reception + 检查项 → create_work_order_from_reception # 入库质检工单 dpz → create_production_work_order_from_reception → try_auto_dispatch_builtin_tasks… # 内置任务派发 → create_review_from_reception # 入库放行审核 drk ``` 结论 = `bj`:只写接收,不创建工单/审核。 防重:`tenant_no + reception_source + sample_code`。 实验室入库编码:结论为 `js` 时必填且租户唯一。 ### 4.4 质检结论派生 `backend/app/services/qc_work_order_conclusion.py`: ``` 任一任务 conclusion == btg → 工单 btg 全部任务 conclusion == tg → 工单 tg 否则 → ""(尚无结论) ``` 工单状态:`dpz` → `ypz` →(全部任务 ywc)`ywc` 并落库 `wo.conclusion`。 `try_reopen_work_order`:回退 `ypz`,清空 conclusion。 质检 Tab 列表派生(`_qc_status_label`): | 条件 | 码 | |------|-----| | 无工单等 | `dzj` 待质检 | | `ypz` | `zjz` 质检中 | | `ywc` + tg/btg | `tg` / `btg` | | `ywc` 结论未齐 | `zjz` | | 工单 `yzf` | `yzf` | 协议详情回显:`get_qc_info_map_for_samples` 细胞存储侧必须按 **sample_code** 关联(`trigger_source=reception`)。 ### 4.5 入库双审 `submit_review`(production / qc 两侧): - 任一方 `btg` → 聚合 `btg` - 双方 `tg` → 聚合 `tg` → `storage_transition` = `store` → `store_sample` - 已入库后结论变 `btg` → `unstore` 前置:生产与质检均通过且 `storage_status=drk` 才能入库。 ### 4.6 短信规则 系统**不自动发送**。运营:提取文案 → 复制 → 线下发 → 标记已通知。 | Tab | 模板要点 | |-----|----------| | 接收 | 已于{日期}完成接收,结论:{接收/拒收} | | 质检 | 合格 / 未通过;未完成返回「暂无短信内容…」 | | 入库 | 已入库日期 / 审核未通过;待确认暂无内容 | ### 4.7 与缴退费关系 - **无直接 FK**;缴费挂协议不挂样本行。 - 间接:有效样本数 → 默认定价 / 可选缴费模式。 - 本页无缴费操作;查看协议弹窗内可见缴费。 ### 4.8 代码锚点 | 路径 | 角色 | |------|------| | `web-admin/.../sample-info/index.vue` | 壳页 | | `web-admin/.../protocol-audit/Sample*InfoTab.vue` | 四 Tab | | `backend/app/api/protocol_sample_collection.py` | 采集列表 | | `backend/app/api/sample_sms_notification.py` 等 | 三短信 API | | `backend/app/crud/sample_reception.py` | 接收写库 | | `backend/app/crud/qc_work_order.py` | 质检工单 | | `backend/app/crud/storage_release_review.py` | 入库放行 | --- ## 5. 【缴退费财务确认及开票】模块 ### 5.1 三角色协作 ``` 业务(缴费信息 / 满月待办) 财务(本模块) 提交缴费确认 dfj→jfqrz → 确认收款 → yjf(或退回→dfj) 提交退费确认 yjf→tfqrz → 确认退费 → ytf(或退回→yjf) 发起开票申请 → dkp → 完成开票 → ykp(上传发票) ``` 前端: - `payment-confirm/index.vue`(缴费确认) - `RefundConfirmTab.vue` - `InvoiceRequestTab.vue` - 上游:`PaymentInfoTab.vue`、`FullmoonTodoTab.vue` ### 5.2 缴费明细状态机(`pay_status`) ``` dfj → jfqrz → yjf → tfqrz → ytf ↑退回 ↑退回 ``` | 状态 | 业务可做 | 财务可做 | 关键写入 | |------|----------|----------|----------| | `dfj` | 编辑草稿、提交确认 | — | 转账方式/金额/时间/凭证 | | `jfqrz` | — | 确认/退回 | finance_* → yjf 或 → dfj | | `yjf` | 发起退费 | — | refund_* → tfqrz | | `tfqrz` | — | 确认/退回 | refund_finance_* → ytf 或 → yjf | | `ytf` | — | — | 终态 | 提交收款强制:转账方式非空 + `transfer_amount > 0`。 提交退费强制:仅 `yjf` + `refund_amount > 0`。 **注意**:批量提交收款不跑单条草稿完整性校验(假定已是合格 `dfj`)。 ### 5.3 开票(独立状态机,不改 `pay_status`) ``` 可开票 = pay_status=yjf AND 明细未在有效申请子表(dkp|ykp) 金额 = Σ transfer_amount(快照 line_amount / invoice_amount) 完成 = 仅 dkp + 发票附件必填 → ykp 不做 = 驳回、冲红、改缴费状态、结构化发票号/日期 ``` - 多期可合并一张发票(1 主表 + N 子表)。 - 已占用期不可再选;剩余期可另起申请。 - 抬头六字段申请时 **选填**。 函数:`create_invoice_request` / `complete_invoice_request` / `list_invoice_eligible_payment_details`。 ### 5.4 平行模块(勿混淆) | 模块 | 差异 | |------|------| | 临床缴退费财务确认 | `biz_pilot_payment`;无开票 Tab | | 渠道服务订单财务确认 | 另一套流水状态机 | --- ## 6. 协议转科研(`zky`)边界 入口:`research_donation.convert_from_storage`。 ### 6.1 前置 - 源协议 `sxz` - 无有效 `biz_protocol_donation_convert` - 协议号非空 ### 6.2 事务内拷贝 | 拷贝 | 不拷贝 | |------|--------| | 捐赠主单(同号 `donation_no`) | **缴费主表/明细** | | party / collection | 审核 / 开票 / 储存证 / 满月 | | samples(同 sample_code)+ transports | 接收/质检/入库审核记录 | 源协议 → `zky` + `to_research_*`;写关联表 `convert_type=storage_to_research`。 ### 6.3 同码共存 配对后两边可暂共用 `sample_code`;保存唯一性对配对 ID **豁免**(`get_partner_sample_codes`)。 之后两边可各自改号,**不自动同步**;关联表 `biz_no` 只留转移快照。 产品含义:转科研是合同形态切换,**不是财务结算迁移**;退费/开票仍对原协议缴费明细。 --- ## 7. 端到端写库总图 ``` 协议保存 客户 → 采集 →【缴费校验+明细】→ 样本编码占用 → 储存证 │ │ ▼ ▼ 缴费明细状态机 sample_code dfj→jfqrz→yjf→… │ │ ▼ │ 接收(js) → QC + 生产 + 入库审核(drk) │ │ ▼ ▼ 双审 tg 财务确认 / 开票(dkp→ykp) store → sample_status=yrk 转科研(sxz→zky):新捐赠同号同码;缴费不跟走 ``` --- ## 8. 实现时注意点(改码易踩坑) 1. **跨表只认 `tenant_no + sample_code`**,勿用样本行 id 做业务匹配。 2. **缴费先于样本**保存;创建失败要补偿软删。 3. **审核不改协议状态**;财务确认不改协议状态;开票不改 `pay_status`。 4. **前端/后端缴费阻断集合不完全一致**(`ytf`)。 5. **批量提交收款**校验弱于单条。 6. **转科研不迁缴费**;样本下游历史仍按编码可查。 7. **管理端色值**用 Fresh Mint(`#10b981`),勿混小程序三墙绿。 8. 平行「缴退费财务确认」菜单(临床/渠道)表模型不同。 --- ## 9. 关键代码地图(速查) ### 前端 | 模块 | 路径 | |------|------| | 协议主页 | `web-admin/src/views/cell-storage/protocol-audit/` | | 协议表单 | `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue` | | 样本信息 | `web-admin/src/views/cell-storage/sample-info/index.vue` | | 财务确认 | `web-admin/src/views/cell-storage/payment-confirm/` | | API | `web-admin/src/api/collection-protocol.ts` 等 | | 缴费工具 | `web-admin/src/utils/protocol-payment-*.ts`、`protocol-pay-mode-switch.ts` | | 路由 | `web-admin/src/router/modules/sqxp.ts` | ### 后端 | 模块 | 路径 | |------|------| | 协议 API/CRUD | `backend/app/api/collection_protocol.py`、`crud/collection_protocol.py` | | 样本采集列表 | `api/protocol_sample_collection.py`、`crud/protocol_sample_collection.py` | | 接收 | `crud/sample_reception.py` | | 质检 | `crud/qc_work_order.py`、`services/qc_work_order_conclusion.py` | | 入库 | `crud/storage_release_review.py` | | 短信 | `crud/sample_sms_notification.py`、`qc_sms_notification.py`、`storage_entry_sms_notification.py` | | 转科研 | `crud/research_donation.py`(`convert_from_storage`)、`crud/protocol_donation_convert.py` | | 缴费模式 | `services/protocol_pay_mode_switch.py` | --- ## 10. 后续改码建议用法 1. 先在本文确认「现状行为」与「要改的边界」。 2. 若行为变更跨状态机/表结构,另开 OpenSpec change 或 `docs/superpowers/specs/YYYY-MM-DD-*-design.md`。 3. 改样本下游关联时,对照 §2.1 与 OpenSpec api-conventions。 4. 改缴费/开票时,对照 §5 状态矩阵与「明确不做」列表,避免无意引入驳回/冲红等本期未实现能力。 5. 改协议表单字段时,同步检查:Create/Update Body、`_row_to_dict`、前端 `formData`(见 `docs/dev-field-consistency.md`)。 --- **文档维护**:若实现偏离本文描述,请同步更新本节或标注「已失效段落」,避免后续改码基于过时基线。