2026-05-24-pilot-application-phase1-design.md 18 KB

中试服务申请单 Phase 1 设计文档

日期:2026-05-24 模块:中试服务管理 > 中试服务申请单 阶段:Phase 1(核心骨架) 需求来源:docs/中科兴皖需求V0.3.md §2.3.9

1. 概述

实现中试服务申请单的完整 CRUD、患者信息管理、输注方案管理、输注计划管理。采用独立后端模块方案(方案A),与现有 pilot_service.py 分离。

分期规划

阶段 范围 状态
Phase 1 申请单+患者+输注方案+输注计划 本文档
Phase 2 体检管理+交付交接+费用扣减 后续
Phase 3 出库生产/质检/放行审核联动 后续

Phase 1 范围

  • ✅ 中试申请单 CRUD + 状态流转(开始/完成/作废)
  • ✅ 患者信息(关联客户管理表 biz_customer,选填+新建模式)
  • ✅ 输注方案 CRUD
  • ✅ 输注计划(按疗程数自动生成 + 手动增删调整 + 标记完成)
  • ✅ 申请单列表页 + 详情 Tab 页
  • ❌ 体检管理(Phase 2)
  • ❌ 交付/交接模块(Phase 2)
  • ❌ 出库生产/质检/放行联动(Phase 3)
  • ❌ 费用扣减逻辑(Phase 2 交付模块中实现)

2. 数据库变更

2.1 ALTER biz_pilot_application

现有表仅含 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);

2.2 现有表不变

以下 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 等

2.3 输注计划状态约定(Phase 1)

biz_pilot_infusion_schedule.schedule_status 已有值域:dsc/ysc/yjf/yqx。

Phase 1 简化使用:

  • 创建时 schedule_status 留空(前端显示"-")
  • 用户点击"完成"按钮后更新为 ywc(已完成)
  • Phase 3 出库联动时再启用 dsc/ysc/yjf/yqx 等详细状态

3. 后端架构

3.1 Model(models.py 新增)

在 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

3.2 CRUD(新文件:backend/app/crud/pilot_application.py)

_row_to_dict 函数

每个实体一个 _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)

CRUD 函数清单

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:

  1. 查询 channel_service_order_id 对应的服务订单,校验存在且状态为生效中(status='sxz')
  2. 从服务订单关联的渠道信息缓存 channel_id / channel_name 到申请单
  3. 生成 application_no:前缀 ZSSQ + YYYYMMDD + 4位序列号
  4. 默认 application_status = 'dks'

save_patient(客户关联逻辑):

  1. 如果传入 customer_id,从 biz_customer 表读取客户信息填充到患者字段
  2. 用户可修改已填充的字段
  3. 保存时:如果 customer_id 存在则更新 biz_customer 对应记录;如果不存在则新建 biz_customer 记录并回填 customer_id
  4. 与采集协议单的母亲信息模式一致

auto_generate_schedules:

  1. 读取方案的 total_course_count
  2. 批量插入 N 条 biz_pilot_infusion_schedule 记录
  3. 每条记录继承方案的 cell_type / spec_text
  4. schedule_status 留空,infusion_time 留空(用户后续手动填写)
  5. order_num 按序号 1, 2, 3... 排列
  6. 返回生成数量

3.3 API(新文件:backend/app/api/pilot_application.py)

路由前缀: /web/admin Tag: 中试服务申请单管理

Pydantic Schemas(内联定义)

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]

API 路由清单

方法 路径 说明
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 一致)。


4. 前端架构

4.1 路由(sqxp.ts 追加)

{
  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 }
}

4.2 页面文件

web-admin/src/views/pilot-service/pilot-application/
├── index.vue                 ← 申请单列表页
└── ApplicationDetail.vue     ← 详情页(Tab布局)

4.3 列表页 index.vue

参考模板页: src/views/system/order/index.vue(复杂列表页)

筛选条件:

  • 申请单号(文本输入)
  • 渠道(下拉选择,数据源:已有渠道 API)
  • 服务订单(下拉选择,联动渠道筛选)
  • 状态(Tag 筛选按钮组)

表格列:

列名 字段 宽度 说明
申请单号 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+ 查看/编辑/删除 + 状态按钮

操作按钮逻辑:

  • 新增:弹窗选择渠道 + 服务订单 → 创建成功后跳转详情页
  • 状态按钮动态显示:
    • dks(待开始):显示"开始"按钮
    • jxz(进行中):显示"完成"和"作废"按钮
    • ywc(已完成) / yzf(已作废):无状态按钮
  • 点击行 → router.push 跳转详情页(携带 application_id 参数)

4.4 详情页 ApplicationDetail.vue

参考模板页: src/views/system/product/index.vue(含 ViewDialog 的详情展示)

Tab 布局:

Tab 内容 阶段
基本信息 申请单信息 + 患者信息 Phase 1 ✅
输注方案 方案列表 + 计划管理 Phase 1 ✅
体检信息 占位(Phase 2 实现) Phase 2 🔒
交付信息 占位(Phase 2 实现) Phase 2 🔒

基本信息 Tab

上半区 — 申请单信息(只读):

  • 申请单号、渠道名称、关联服务订单、状态、备注
  • 状态以 el-tag 展示

下半区 — 患者信息面板(可编辑):

  • "选择客户"按钮 → 弹窗从客户管理表(biz_customer)选取
    • 弹窗展示客户列表,支持搜索(姓名/手机号)
    • 选中后自动填充:姓名、性别、年龄、身份证号、联系电话、紧急联系人、紧急联系人电话
  • 已填充字段可手动修改
  • "是否需要体检"开关(need_checkup)
  • 临床诊断文本框 + 诊断附件上传
  • "保存患者信息"按钮
  • 保存时同步更新 biz_customer 表(与采集协议单母亲信息模式一致)

输注方案 Tab

方案列表(表格或卡片):

每行显示:

  • 细胞类型(cell_type)
  • 规格(spec_text)
  • 总疗程数(total_course_count)
  • 疗程间隔(course_interval)
  • 输注途径(infusion_route)
  • 输注开始日期(infusion_start_date)
  • 计划数 / 已完成数(如 3/5)
  • 操作:编辑 / 删除

展开行 — 输注计划列表:

每个方案可展开显示其下的输注计划:

列 说明
序号 order_num
细胞类型 cell_type
规格 spec_text
输注时间 infusion_time(可编辑)
状态 "-" 或 "已完成"(el-tag)
操作 编辑 / 删除 / 标记完成

操作流程:

  1. 点击"新增输注方案" → 弹窗填写方案信息 → 保存
  2. 保存方案后自动弹出提示:"是否立即生成 X 条输注计划?"
  3. 确认后调用 auto_generate 接口批量生成
  4. 生成后可手动编辑每条计划的时间/备注
  5. 可手动新增/删除计划
  6. 每条计划有"标记完成"按钮

4.5 API 函数(admin.ts 追加)

在 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)

4.6 状态标签注册

在 web-admin/src/utils/status-tag-registry.ts 追加:

// 申请单状态
applicationStatusLabel(status) → '待开始'/'进行中'/'已完成'/'已作废'
applicationStatusTagType(status) → 'info'/'primary'/'success'/'danger'

5. 关键交互流程

5.1 创建申请单流程

列表页 → 点击"新增"
  → 弹窗:选择渠道(下拉)→ 联动加载该渠道的服务订单(下拉)
  → 选择服务订单 → 填写备注
  → 提交 → 后端校验订单状态 → 生成申请单号 → 创建成功
  → 跳转详情页

5.2 患者信息录入流程

详情页基本信息Tab → 点击"选择客户"
  → 弹窗展示客户列表(姓名/手机搜索)
  → 选中客户 → 自动填充患者字段
  → 可手动修改字段
  → 填写临床诊断 + 上传诊断附件
  → 点击"保存" → 后端同步更新 biz_customer

5.3 输注方案+计划管理流程

详情页输注方案Tab → 点击"新增输注方案"
  → 弹窗填写:细胞类型、规格、总疗程数(N)、疗程间隔、输注途径、开始日期
  → 保存 → ElMessageBox.confirm("是否立即生成 N 条输注计划?")
    → 确认 → 调用 auto_generate → 生成 N 条计划
    → 取消 → 仅创建方案,后续手动添加
  → 展开方案 → 查看/编辑每条计划
  → 手动新增/删除/编辑计划
  → 标记计划完成

5.4 申请单状态流转

dks(待开始) ──开始──→ jxz(进行中) ──完成──→ ywc(已完成)
    │                    │
    └────作废──→ yzf(已作废) ←──作废──┘

约束:
- 编辑/删除:仅 dks 状态可操作
- 开始:仅 dks → jxz
- 完成:仅 jxz → ywc
- 作废:dks 或 jxz → yzf

6. 文件清单

新增文件

# 文件路径 说明
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 个路由

7. 不在范围内

以下内容不在 Phase 1 中:

  • 体检预约/报告/费用管理(biz_pilot_checkup + reserve_item + actual_item)
  • 交付信息/物料清单/交付方式(biz_pilot_delivery + delivery_material + delivery_method)
  • 应用确认(biz_pilot_infusion_confirm)
  • 输注计划→出库生产工单自动联动
  • 出库质检工单
  • 出库放行审核
  • 费用/份数扣减逻辑
  • 工作流集成
  • 数据权限(使用 require_admin_or_super_admin)