Files
zkxw/docs/superpowers/plans/2026-07-01-special-sample-mark.md
T
2026-07-01 16:37:33 +08:00

151 lines
6.2 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.
# 样本特殊标记 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在采集/捐赠协议单样本上维护 `isSpecial` + `specialRemark`,并在 web-admin 全链路只读展示红色加粗警示。
**Architecture:** 字段仅存 `biz_protocol_sample` / `biz_research_donation_sample`;下游 API JOIN 源表补字段;前端 `SpecialSampleDisplay` 统一渲染。
**Tech Stack:** FastAPI + SQLAlchemy + Vue 3 + Element Plus
**Spec:** `docs/superpowers/specs/2026-07-01-special-sample-mark-design.md`
---
## File Map
| 文件 | 职责 |
|------|------|
| `docs/sql/migrate-special-sample-mark.sql` | DDL 增量脚本 |
| `backend/app/utils/sample_special.py` | 校验 + dict 转换工具 |
| `backend/app/models/models.py` | `BizProtocolSample` 新列 |
| `backend/app/models/research_donation.py` | `BizResearchDonationSample` 新列 |
| `backend/app/crud/collection_protocol.py` | `_sample_to_dict` / `save_samples` / list sampleItems |
| `backend/app/crud/research_donation.py` | 同上 + 转存储复制 |
| `backend/app/crud/qc_work_order.py` | `_enrich_work_order_list_items` 补字段 |
| `backend/app/crud/sample_reception.py` | 待/已接收 list item |
| `backend/app/crud/protocol_sample_collection.py` | 样本采集信息 list |
| `backend/app/crud/research_donation_sample_collection.py` | 捐赠样本采集 list |
| `backend/app/crud/pre_transit_sample.py` | 转运前样本卡片 items |
| `backend/app/crud/storage_release_review.py` | 入库审核 detail |
| `backend/app/crud/cell_product.py` | 溯源链样本信息 |
| `web-admin/src/components/business/special-sample-display/index.vue` | 公共展示组件 |
| `web-admin/src/components/business/sample-transport-form-section/index.vue` | 录入 |
| `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue` | 采集查看 |
| `web-admin/src/views/research-donation/list/DonationViewDialog.vue` | 捐赠查看 |
| 下游 15+ vue 页面 | 样本编码旁 inline 展示 |
---
## Task 1: 数据库与 Model
**Files:**
- Create: `docs/sql/migrate-special-sample-mark.sql`
- Modify: `backend/app/models/models.py`, `backend/app/models/research_donation.py`
- [ ] **Step 1:** 执行 DDL(两表对称)
```sql
ALTER TABLE `biz_protocol_sample`
ADD COLUMN `is_special` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否特殊样本:0否 1是' AFTER `storage_expire_date`,
ADD COLUMN `special_remark` varchar(256) DEFAULT NULL COMMENT '特殊说明' AFTER `is_special`;
ALTER TABLE `biz_research_donation_sample`
ADD COLUMN `is_special` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否特殊样本:0否 1是' AFTER `storage_expire_date`,
ADD COLUMN `special_remark` varchar(256) DEFAULT NULL COMMENT '特殊说明' AFTER `is_special`;
```
- [ ] **Step 2:** ORM 增加 `is_special = Column(SmallInteger...)` 与 `special_remark = Column(String(256)...)`
---
## Task 2: 后端工具与核心 CRUD
**Files:**
- Create: `backend/app/utils/sample_special.py`
- Modify: `backend/app/crud/collection_protocol.py`, `backend/app/crud/research_donation.py`
- [ ] **Step 1:** 创建工具函数
```python
def sample_special_to_dict(sample) -> Dict[str, Any]: ...
def validate_sample_special_raw(raw: dict, *, index: Optional[int] = None) -> None: ...
def apply_sample_special_fields(obj, raw: dict) -> None: ...
```
- [ ] **Step 2:** `_sample_to_dict` 增加 `isSpecial` / `specialRemark`
- [ ] **Step 3:** `save_samples` / `_create_samples` 循环内调用 `validate_sample_special_raw` + `apply_sample_special_fields`
- [ ] **Step 4:** `research_donation.py` 转存储复制 `is_special`、`special_remark`
- [ ] **Step 5:** `collection_protocol.py` 列表 `sampleItems` 构建补字段
---
## Task 3: 下游 CRUD API
**Files:** `sample_reception.py`, `protocol_sample_collection.py`, `research_donation_sample_collection.py`, `pre_transit_sample.py`, `qc_work_order.py`, `storage_release_review.py`, `cell_product.py`
- [ ] **Step 1:** 凡已有 `BizProtocolSample` / `BizResearchDonationSample` 对象处,`item.update(sample_special_to_dict(sample))`
- [ ] **Step 2:** `qc_work_order._enrich_work_order_list_items` 在 `sampleTypeName` 赋值后补 special 字段(覆盖质检+生产工单)
---
## Task 4: 前端公共组件与录入
**Files:**
- Create: `web-admin/src/components/business/special-sample-display/index.vue`
- Modify: `web-admin/src/components/business/sample-transport-form-section/index.vue`
- [ ] **Step 1:** 创建 `SpecialSampleDisplay`(`v-if="isSpecial"`,danger 色加粗)
- [ ] **Step 2:** 样本子弹窗增加 checkbox + textarea;取消勾选清空说明
- [ ] **Step 3:** `confirmSampleDialog` 校验:`isSpecial && !specialRemark.trim()` → warning
- [ ] **Step 4:** 列表增加「特殊样本」列;`openAddSampleDialog` 初始化 `isSpecial: false, specialRemark: ''`
- [ ] **Step 5:** `normalizeSampleRow` 保留 `isSpecial` / `specialRemark`
---
## Task 5: 协议查看弹窗
**Files:** `ProtocolViewDialog.vue`, `DonationViewDialog.vue`
- [ ] **Step 1:** 样本卡片 header inline 展示 `SpecialSampleDisplay`
---
## Task 6: 下游展示页面(P1 全量)
在样本编码列/标题行旁接入 `SpecialSampleDisplay`:
- [ ] `ProtocolAuditTab.vue`, `PreTransitProtocolCardList.vue`
- [ ] `research-donation/audit/index.vue`
- [ ] `SampleCollectionInfoTab.vue`, `DonationSampleCollectionInfoTab.vue`
- [ ] `sample-reception/index.vue`, `SampleReceptionCardList.vue`
- [ ] `QcWorkOrderProtocolGroup.vue`, `QcWorkOrderSampleGroup.vue`, `QcWorkOrderTaskSubList.vue`
- [ ] `storage-release-review/index.vue`, `StorageReleaseReviewDialog.vue`, `StorageReleaseViewDialog.vue`
- [ ] `ProductionWorkOrderCardList.vue`, `SampleReceptionDialog.vue`, `DispatchTaskDialog.vue`
- [ ] `SampleCustomerViewDialog.vue`
---
## Task 7: 验证
- [ ] 本地执行 `docs/sql/migrate-special-sample-mark.sql`
- [ ] `python scripts/check_field_consistency.py`
- [ ] `./scripts/update-graph.sh frontend` + `backend`
- [ ] 手工:协议单录入特殊样本 → 查看弹窗 → 样本接收/质检卡片可见红色标记
---
## 部署前
1. 执行 `docs/sql/migrate-special-sample-mark.sql`
2. 重启 backend
3. 刷新 web-admin