日期:2026-05-24 模块:中试服务管理 > 中试服务申请单 阶段:Phase 1(核心骨架) 需求来源:
docs/中科兴皖需求V0.3.md§2.3.9
实现中试服务申请单的完整 CRUD、患者信息管理、输注方案管理、输注计划管理。采用独立后端模块方案(方案A),与现有 pilot_service.py 分离。
| 阶段 | 范围 | 状态 |
|---|---|---|
| Phase 1 | 申请单+患者+输注方案+输注计划 | 本文档 |
| Phase 2 | 体检管理+交付交接+费用扣减 | 后续 |
| Phase 3 | 出库生产/质检/放行审核联动 | 后续 |
现有表仅含 channel_service_order_id + 公共字段,需增强:
-- 迁移文件: docs/sql/pilot-application-migration.sql
ALTER TABLE biz_pilot_application
ADD COLUMN application_no VARCHAR(64) DEFAULT NULL
COMMENT '申请单号(自动生成,前缀ZSSQ)' AFTER id,
ADD COLUMN application_status VARCHAR(16) NOT NULL DEFAULT 'dks'
COMMENT 'dks待开始 jxz进行中 ywc已完成 yzf已作废' AFTER application_no,
ADD COLUMN channel_id VARCHAR(64) DEFAULT NULL
COMMENT '渠道ID(缓存自服务订单)' AFTER channel_service_order_id,
ADD COLUMN channel_name VARCHAR(200) DEFAULT NULL
COMMENT '渠道名称(缓存)' AFTER channel_id;
ALTER TABLE biz_pilot_application
ADD UNIQUE KEY uk_application_no (application_no, del_flag);
以下 3 张表结构已满足 Phase 1 需求,无需变更:
| 表 | 说明 |
|---|---|
biz_pilot_patient |
患者信息,含 customer_id 关联 + 完整个人字段 |
biz_pilot_infusion_plan |
输注方案,含 cell_type/spec/total_course_count 等 |
biz_pilot_infusion_schedule |
输注计划,含 schedule_status/infusion_time 等 |
biz_pilot_infusion_schedule.schedule_status 已有值域:dsc/ysc/yjf/yqx。
Phase 1 简化使用:
schedule_status 留空(前端显示"-")ywc(已完成)在 backend/app/models/models.py 中新增 4 个 SQLAlchemy model:
| Model | 表 | 关键字段 |
|---|---|---|
BizPilotApplication |
biz_pilot_application | application_no, application_status, channel_service_order_id, channel_id, channel_name |
BizPilotPatient |
biz_pilot_patient | pilot_application_id, customer_id, patient_name, sex, age, id_card_no, phone_no, emergency_contact, emergency_phone, clinical_diagnosis, diagnosis_attachments, need_checkup |
BizPilotInfusionPlan |
biz_pilot_infusion_plan | pilot_application_id, cell_type, spec_text, total_course_count, course_interval, infusion_route, infusion_start_date |
BizPilotInfusionSchedule |
biz_pilot_infusion_schedule | infusion_plan_id, cell_type, spec_text, infusion_time, schedule_status, attachments |
每个实体一个 _row_to_dict_* 函数,作为可读字段的单一事实来源:
_row_to_dict_application(row) — 含 channel_name、patient_name(子查询)、infusion_plan_count(子查询)_row_to_dict_patient(row)_row_to_dict_infusion_plan(row) — 含 schedule_count(子查询)_row_to_dict_infusion_schedule(row)Application:
| 函数 | 说明 |
|---|---|
list_applications(db, tenant_no, filters) |
分页列表,支持按渠道/状态/服务订单筛选 |
get_application(db, app_id, tenant_no) |
获取单个申请单详情 |
create_application(db, data, tenant_no, user_id) |
创建申请单,校验服务订单存在且生效,缓存渠道信息,生成申请单号 |
update_application(db, app_id, data, tenant_no) |
更新申请单(仅 dks 状态可编辑) |
soft_delete_application(db, app_id, tenant_no) |
软删除(仅 dks 状态) |
start_application(db, app_id, tenant_no, user_id) |
状态:dks→jxz |
complete_application(db, app_id, tenant_no, user_id) |
状态:jxz→ywc |
void_application(db, app_id, tenant_no, user_id) |
状态:任意→yzf |
Patient:
| 函数 | 说明 |
|---|---|
get_patient_by_application(db, app_id, tenant_no) |
获取某申请单的患者信息 |
save_patient(db, app_id, data, tenant_no, user_id) |
保存/替换患者信息,同步更新 biz_customer 表 |
InfusionPlan:
| 函数 | 说明 |
|---|---|
list_infusion_plans(db, app_id, tenant_no) |
获取某申请单的输注方案列表 |
create_infusion_plan(db, app_id, data, tenant_no, user_id) |
创建输注方案 |
update_infusion_plan(db, plan_id, data, tenant_no) |
更新输注方案 |
soft_delete_infusion_plan(db, plan_id, tenant_no) |
软删除输注方案(级联删除其下所有计划) |
InfusionSchedule:
| 函数 | 说明 |
|---|---|
list_schedules_by_plan(db, plan_id, tenant_no) |
获取某方案下的输注计划列表 |
auto_generate_schedules(db, plan_id, tenant_no, user_id) |
按方案的 total_course_count 批量生成计划记录 |
create_schedule(db, plan_id, data, tenant_no, user_id) |
手动添加单条计划 |
update_schedule(db, schedule_id, data, tenant_no) |
更新计划(时间/备注等) |
soft_delete_schedule(db, schedule_id, tenant_no) |
软删除单条计划 |
mark_schedule_complete(db, schedule_id, tenant_no, user_id) |
标记计划为已完成(schedule_status='ywc') |
create_application:
channel_service_order_id 对应的服务订单,校验存在且状态为生效中(status='sxz')channel_id / channel_name 到申请单application_no:前缀 ZSSQ + YYYYMMDD + 4位序列号application_status = 'dks'save_patient(客户关联逻辑):
customer_id,从 biz_customer 表读取客户信息填充到患者字段customer_id 存在则更新 biz_customer 对应记录;如果不存在则新建 biz_customer 记录并回填 customer_idauto_generate_schedules:
total_course_countbiz_pilot_infusion_schedule 记录cell_type / spec_textschedule_status 留空,infusion_time 留空(用户后续手动填写)order_num 按序号 1, 2, 3... 排列路由前缀: /web/admin
Tag: 中试服务申请单管理
ApplicationCreateBody:
channel_service_order_id: str
remark: Optional[str]
ApplicationUpdateBody:
remark: Optional[str]
PatientSaveBody:
customer_id: Optional[str]
patient_name: str
sex: Optional[str]
age: Optional[int]
id_card_no: Optional[str]
phone_no: Optional[str]
emergency_contact: Optional[str]
emergency_phone: Optional[str]
clinical_diagnosis: Optional[str]
diagnosis_attachments: Optional[str]
need_checkup: int = 1
InfusionPlanCreateBody:
pilot_application_id: str
cell_type: str
spec_text: Optional[str]
total_course_count: int
course_interval: Optional[str]
infusion_route: Optional[str]
infusion_start_date: Optional[str]
InfusionPlanUpdateBody:
cell_type: Optional[str]
spec_text: Optional[str]
total_course_count: Optional[int]
course_interval: Optional[str]
infusion_route: Optional[str]
infusion_start_date: Optional[str]
InfusionScheduleUpdateBody:
infusion_time: Optional[str]
remark: Optional[str]
attachments: Optional[str]
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /pilot-application/list |
申请单分页列表 |
| GET | /pilot-application/detail |
申请单详情(含患者+方案+计划) |
| POST | /pilot-application/create |
创建申请单 |
| PUT | /pilot-application/update |
更新申请单 |
| DELETE | /pilot-application/delete |
删除申请单 |
| POST | /pilot-application/start |
开始(dks→jxz) |
| POST | /pilot-application/complete |
完成(jxz→ywc) |
| POST | /pilot-application/void |
作废(→yzf) |
| GET | /pilot-patient/detail |
获取患者信息 |
| POST | /pilot-patient/save |
保存患者信息 |
| GET | /pilot-infusion-plan/list |
输注方案列表 |
| POST | /pilot-infusion-plan/create |
创建输注方案 |
| PUT | /pilot-infusion-plan/update |
更新输注方案 |
| DELETE | /pilot-infusion-plan/delete |
删除输注方案 |
| POST | /pilot-infusion-schedule/auto-generate |
自动生成计划 |
| POST | /pilot-infusion-schedule/create |
手动添加计划 |
| PUT | /pilot-infusion-schedule/update |
更新计划 |
| DELETE | /pilot-infusion-schedule/delete |
删除计划 |
| POST | /pilot-infusion-schedule/mark-complete |
标记计划完成 |
路由注册: 在 app/api/__init__.py 和 app/main.py 中追加 pilot_application router。
权限: 使用 require_admin_or_super_admin 装饰器(与现有 pilot_service 一致)。
{
path: 'pilot-application',
name: 'PilotApplication',
component: () => import('@views/pilot-service/pilot-application/index.vue'),
meta: { title: '中试服务申请单', icon: '...' }
},
{
path: 'pilot-application/detail',
name: 'PilotApplicationDetail',
component: () => import('@views/pilot-service/pilot-application/ApplicationDetail.vue'),
meta: { title: '申请单详情', hidden: true }
}
web-admin/src/views/pilot-service/pilot-application/
├── index.vue ← 申请单列表页
└── ApplicationDetail.vue ← 详情页(Tab布局)
参考模板页: src/views/system/order/index.vue(复杂列表页)
筛选条件:
表格列:
| 列名 | 字段 | 宽度 | 说明 |
|---|---|---|---|
| 申请单号 | application_no | 180 | |
| 渠道名称 | channel_name | 150 | |
| 关联服务订单 | service_order_no | 180 | 显示订单号 + 类型标签 |
| 患者姓名 | patient_name | 100 | |
| 输注方案数 | infusion_plan_count | 100 | |
| 状态 | application_status | 100 | el-tag:dks(info) / jxz(primary) / ywc(success) / yzf(danger) |
| 创建时间 | create_time | 160 | |
| 操作 | — | 260+ | 查看/编辑/删除 + 状态按钮 |
操作按钮逻辑:
参考模板页: src/views/system/product/index.vue(含 ViewDialog 的详情展示)
Tab 布局:
| Tab | 内容 | 阶段 |
|---|---|---|
| 基本信息 | 申请单信息 + 患者信息 | Phase 1 ✅ |
| 输注方案 | 方案列表 + 计划管理 | Phase 1 ✅ |
| 体检信息 | 占位(Phase 2 实现) | Phase 2 🔒 |
| 交付信息 | 占位(Phase 2 实现) | Phase 2 🔒 |
上半区 — 申请单信息(只读):
下半区 — 患者信息面板(可编辑):
方案列表(表格或卡片):
每行显示:
展开行 — 输注计划列表:
每个方案可展开显示其下的输注计划:
| 列 | 说明 |
|---|---|
| 序号 | order_num |
| 细胞类型 | cell_type |
| 规格 | spec_text |
| 输注时间 | infusion_time(可编辑) |
| 状态 | "-" 或 "已完成"(el-tag) |
| 操作 | 编辑 / 删除 / 标记完成 |
操作流程:
在 web-admin/src/api/admin.ts 末尾追加以下函数:
// 中试申请单
fetchPilotApplicationList(params)
fetchPilotApplicationDetail(params)
createPilotApplication(data)
updatePilotApplication(data)
deletePilotApplication(data)
startPilotApplication(data)
completePilotApplication(data)
voidPilotApplication(data)
// 患者信息
fetchPilotPatient(params)
savePilotPatient(data)
// 输注方案
fetchPilotInfusionPlanList(params)
createPilotInfusionPlan(data)
updatePilotInfusionPlan(data)
deletePilotInfusionPlan(data)
// 输注计划
fetchPilotInfusionScheduleList(params)
autoGenerateSchedules(data)
updatePilotInfusionSchedule(data)
deletePilotInfusionSchedule(data)
markScheduleComplete(data)
在 web-admin/src/utils/status-tag-registry.ts 追加:
// 申请单状态
applicationStatusLabel(status) → '待开始'/'进行中'/'已完成'/'已作废'
applicationStatusTagType(status) → 'info'/'primary'/'success'/'danger'
列表页 → 点击"新增"
→ 弹窗:选择渠道(下拉)→ 联动加载该渠道的服务订单(下拉)
→ 选择服务订单 → 填写备注
→ 提交 → 后端校验订单状态 → 生成申请单号 → 创建成功
→ 跳转详情页
详情页基本信息Tab → 点击"选择客户"
→ 弹窗展示客户列表(姓名/手机搜索)
→ 选中客户 → 自动填充患者字段
→ 可手动修改字段
→ 填写临床诊断 + 上传诊断附件
→ 点击"保存" → 后端同步更新 biz_customer
详情页输注方案Tab → 点击"新增输注方案"
→ 弹窗填写:细胞类型、规格、总疗程数(N)、疗程间隔、输注途径、开始日期
→ 保存 → ElMessageBox.confirm("是否立即生成 N 条输注计划?")
→ 确认 → 调用 auto_generate → 生成 N 条计划
→ 取消 → 仅创建方案,后续手动添加
→ 展开方案 → 查看/编辑每条计划
→ 手动新增/删除/编辑计划
→ 标记计划完成
dks(待开始) ──开始──→ jxz(进行中) ──完成──→ ywc(已完成)
│ │
└────作废──→ yzf(已作废) ←──作废──┘
约束:
- 编辑/删除:仅 dks 状态可操作
- 开始:仅 dks → jxz
- 完成:仅 jxz → ywc
- 作废:dks 或 jxz → yzf
| # | 文件路径 | 说明 |
|---|---|---|
| 1 | docs/sql/pilot-application-migration.sql |
数据库迁移 SQL |
| 2 | backend/app/crud/pilot_application.py |
后端 CRUD |
| 3 | backend/app/api/pilot_application.py |
后端 API 路由 |
| 4 | web-admin/src/views/pilot-service/pilot-application/index.vue |
列表页 |
| 5 | web-admin/src/views/pilot-service/pilot-application/ApplicationDetail.vue |
详情页 |
| # | 文件路径 | 变更 |
|---|---|---|
| 6 | backend/app/models/models.py |
新增 4 个 Model 类 |
| 7 | backend/app/api/__init__.py |
注册新 router |
| 8 | backend/app/main.py |
引入新 router |
| 9 | web-admin/src/api/admin.ts |
追加 19 个 API 函数 |
| 10 | web-admin/src/utils/status-tag-registry.ts |
追加申请单状态标签函数 |
| 11 | web-admin/src/router/modules/sqxp.ts |
追加 2 个路由 |
以下内容不在 Phase 1 中: