Files
shanxia2.0/backend/app/crud/pilot_application.py
T
2026-05-25 15:14:06 +08:00

825 lines
27 KiB
Python

"""中试申请单 CRUD(申请单、患者信息、输注方案、输注计划)"""
from __future__ import annotations
import uuid
from datetime import datetime
from typing import Any, Dict, List, Optional
from sqlalchemy import and_, func
from sqlalchemy.orm import Session
from app.models.models import (
BizChannelServiceOrder,
BizCustomer,
BizPilotApplication,
BizPilotInfusionPlan,
BizPilotInfusionSchedule,
BizPilotPatient,
)
# ────────────────────────────────────────────
# Helpers: _row_to_dict
# ────────────────────────────────────────────
def _generate_application_no(db: Session, tenant_no: str) -> str:
today_str = datetime.now().strftime("%Y%m%d")
prefix = f"ZSSQ{today_str}"
existing = (
db.query(BizPilotApplication.application_no)
.filter(
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.application_no.like(f"{prefix}%"),
BizPilotApplication.del_flag == 0,
)
.order_by(BizPilotApplication.application_no.desc())
.first()
)
seq = 1
if existing and existing[0]:
try:
seq = int(existing[0][-4:]) + 1
except ValueError:
seq = 1
return f"{prefix}{seq:04d}"
def _row_to_dict_application(row, db=None, tenant_no=None) -> Dict[str, Any]:
d: Dict[str, Any] = {
"id": row.id,
"applicationNo": row.application_no or "",
"applicationStatus": row.application_status or "",
"channelServiceOrderId": row.channel_service_order_id or "",
"channelId": row.channel_id or "",
"channelName": row.channel_name or "",
"tenantNo": row.tenant_no or "",
"remark": row.remark or "",
"createBy": row.create_by or "",
"createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
"updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
}
if db and tenant_no:
# patientName
patient = (
db.query(BizPilotPatient.patient_name)
.filter(
BizPilotPatient.pilot_application_id == row.id,
BizPilotPatient.tenant_no == tenant_no,
BizPilotPatient.del_flag == 0,
)
.first()
)
d["patientName"] = patient.patient_name if patient else ""
# infusionPlanCount
plan_count = (
db.query(func.count(BizPilotInfusionPlan.id))
.filter(
BizPilotInfusionPlan.pilot_application_id == row.id,
BizPilotInfusionPlan.tenant_no == tenant_no,
BizPilotInfusionPlan.del_flag == 0,
)
.scalar()
) or 0
d["infusionPlanCount"] = plan_count
# serviceOrderNo + orderType
svc_order = (
db.query(BizChannelServiceOrder)
.filter(BizChannelServiceOrder.id == row.channel_service_order_id)
.first()
)
if svc_order:
d["serviceOrderNo"] = svc_order.channel_name or ""
d["orderType"] = svc_order.order_type or ""
else:
d["serviceOrderNo"] = ""
d["orderType"] = ""
return d
def _row_to_dict_patient(row) -> Dict[str, Any]:
return {
"id": row.id,
"pilotApplicationId": row.pilot_application_id or "",
"customerId": row.customer_id or "",
"patientName": row.patient_name or "",
"sex": row.sex or "",
"age": row.age if row.age is not None else None,
"idCardNo": row.id_card_no or "",
"phoneNo": row.phone_no or "",
"emergencyContact": row.emergency_contact or "",
"emergencyPhone": row.emergency_phone or "",
"clinicalDiagnosis": row.clinical_diagnosis or "",
"diagnosisAttachments": row.diagnosis_attachments or "",
"needCheckup": row.need_checkup if row.need_checkup is not None else 1,
"tenantNo": row.tenant_no or "",
"remark": row.remark or "",
"createBy": row.create_by or "",
"createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
"updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
}
def _row_to_dict_infusion_plan(row, db=None, tenant_no=None) -> Dict[str, Any]:
d: Dict[str, Any] = {
"id": row.id,
"pilotApplicationId": row.pilot_application_id or "",
"cellType": row.cell_type or "",
"specText": row.spec_text or "",
"totalCourseCount": row.total_course_count if row.total_course_count is not None else 0,
"courseInterval": row.course_interval or "",
"infusionRoute": row.infusion_route or "",
"infusionStartDate": row.infusion_start_date or "",
"tenantNo": row.tenant_no or "",
"remark": row.remark or "",
"createBy": row.create_by or "",
"createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
"updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
}
if db and tenant_no:
# scheduleCount (all schedules)
schedule_count = (
db.query(func.count(BizPilotInfusionSchedule.id))
.filter(
BizPilotInfusionSchedule.infusion_plan_id == row.id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
)
.scalar()
) or 0
d["scheduleCount"] = schedule_count
# completedCount (schedule_status='ywc')
completed_count = (
db.query(func.count(BizPilotInfusionSchedule.id))
.filter(
BizPilotInfusionSchedule.infusion_plan_id == row.id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
BizPilotInfusionSchedule.schedule_status == "ywc",
)
.scalar()
) or 0
d["completedCount"] = completed_count
return d
def _row_to_dict_infusion_schedule(row) -> Dict[str, Any]:
return {
"id": row.id,
"infusionPlanId": row.infusion_plan_id or "",
"cellType": row.cell_type or "",
"specText": row.spec_text or "",
"infusionTime": row.infusion_time.strftime("%Y-%m-%d %H:%M:%S") if row.infusion_time else "",
"scheduleStatus": row.schedule_status or "",
"attachments": row.attachments or "",
"tenantNo": row.tenant_no or "",
"remark": row.remark or "",
"createBy": row.create_by or "",
"createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
"updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
}
# ────────────────────────────────────────────
# Application CRUD
# ────────────────────────────────────────────
def list_applications(
db: Session,
tenant_no: str,
page: int = 1,
page_size: int = 10,
channel_id: Optional[str] = None,
application_status: Optional[str] = None,
channel_service_order_id: Optional[str] = None,
):
q = db.query(BizPilotApplication).filter(
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
if channel_id:
q = q.filter(BizPilotApplication.channel_id == channel_id)
if application_status:
statuses = [s.strip() for s in application_status.split(",") if s.strip()]
if statuses:
q = q.filter(BizPilotApplication.application_status.in_(statuses))
if channel_service_order_id:
q = q.filter(BizPilotApplication.channel_service_order_id == channel_service_order_id)
total = q.order_by(None).with_entities(func.count(BizPilotApplication.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
q.order_by(BizPilotApplication.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
items = [_row_to_dict_application(r, db, tenant_no) for r in rows]
return {
"list": items,
"pagination": {"page": page, "pageSize": page_size, "total": total},
}
def get_application(db: Session, app_id: str, tenant_no: str) -> Optional[Dict[str, Any]]:
row = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == app_id,
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
.first()
)
if not row:
return None
return _row_to_dict_application(row, db, tenant_no)
def create_application(
db: Session,
data: dict,
tenant_no: str,
user_id: str,
dept_id: str,
) -> Dict[str, Any]:
channel_service_order_id = data.get("channelServiceOrderId") or ""
if not channel_service_order_id:
raise ValueError("请选择渠道服务订单")
# Validate service order exists and is active
svc_order = (
db.query(BizChannelServiceOrder)
.filter(
BizChannelServiceOrder.id == channel_service_order_id,
BizChannelServiceOrder.del_flag == 0,
)
.first()
)
if not svc_order:
raise ValueError("渠道服务订单不存在")
if svc_order.order_status != "sxz":
raise ValueError("渠道服务订单未生效,无法创建中试申请")
now = datetime.now()
application_no = _generate_application_no(db, tenant_no)
row = BizPilotApplication(
id=str(uuid.uuid4()),
application_no=application_no,
application_status="dks",
channel_service_order_id=channel_service_order_id,
channel_id=svc_order.channel_id or "",
channel_name=svc_order.channel_name or "",
tenant_no=tenant_no,
order_num=0,
remark=data.get("remark") or "",
create_by=user_id,
create_dept_id=dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(row)
db.commit()
db.refresh(row)
return _row_to_dict_application(row, db, tenant_no)
def update_application(db: Session, app_id: str, data: dict, tenant_no: str) -> Dict[str, Any]:
row = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == app_id,
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("申请单不存在")
if row.application_status != "dks":
raise ValueError("当前状态不允许编辑")
if "remark" in data and data["remark"] is not None:
row.remark = data["remark"]
db.commit()
db.refresh(row)
return _row_to_dict_application(row, db, tenant_no)
def soft_delete_application(db: Session, app_id: str, tenant_no: str) -> None:
row = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == app_id,
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("申请单不存在")
if row.application_status != "dks":
raise ValueError("当前状态不允许删除")
row.del_flag = 1
row.update_time = datetime.now()
db.commit()
def start_application(db: Session, app_id: str, tenant_no: str, user_id: str) -> Dict[str, Any]:
row = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == app_id,
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("申请单不存在")
if row.application_status != "dks":
raise ValueError("当前状态不允许开始")
row.application_status = "jxz"
row.update_by = user_id
row.update_time = datetime.now()
db.commit()
db.refresh(row)
return _row_to_dict_application(row, db, tenant_no)
def complete_application(db: Session, app_id: str, tenant_no: str, user_id: str) -> Dict[str, Any]:
row = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == app_id,
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("申请单不存在")
if row.application_status != "jxz":
raise ValueError("当前状态不允许完成")
row.application_status = "ywc"
row.update_by = user_id
row.update_time = datetime.now()
db.commit()
db.refresh(row)
return _row_to_dict_application(row, db, tenant_no)
def void_application(db: Session, app_id: str, tenant_no: str, user_id: str) -> Dict[str, Any]:
row = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == app_id,
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("申请单不存在")
if row.application_status not in ("dks", "jxz"):
raise ValueError("当前状态不允许作废")
row.application_status = "yzf"
row.update_by = user_id
row.update_time = datetime.now()
db.commit()
db.refresh(row)
return _row_to_dict_application(row, db, tenant_no)
# ────────────────────────────────────────────
# Patient CRUD
# ────────────────────────────────────────────
def get_patient_by_application(
db: Session, app_id: str, tenant_no: str
) -> Optional[Dict[str, Any]]:
row = (
db.query(BizPilotPatient)
.filter(
BizPilotPatient.pilot_application_id == app_id,
BizPilotPatient.tenant_no == tenant_no,
BizPilotPatient.del_flag == 0,
)
.first()
)
if not row:
return None
return _row_to_dict_patient(row)
def save_patient(
db: Session,
app_id: str,
data: dict,
tenant_no: str,
user_id: str,
dept_id: str,
) -> Dict[str, Any]:
now = datetime.now()
customer_id = data.get("customerId") or ""
if customer_id:
customer = (
db.query(BizCustomer)
.filter(BizCustomer.id == customer_id, BizCustomer.del_flag == 0)
.first()
)
if customer:
if data.get("patientName"):
customer.customer_name = data["patientName"]
if data.get("sex"):
customer.sex = data["sex"]
if data.get("age") is not None:
customer.age = data["age"]
if data.get("idCardNo"):
customer.id_card_no = data["idCardNo"]
if data.get("phoneNo"):
customer.phone_no = data["phoneNo"]
customer.update_by = user_id
customer.update_time = now
else:
customer_id = str(uuid.uuid4())
customer = BizCustomer(
id=customer_id,
customer_name=data.get("patientName") or "",
sex=data.get("sex") or "2",
id_card_no=data.get("idCardNo") or "",
age=data.get("age"),
phone_no=data.get("phoneNo") or "",
tenant_no=tenant_no,
order_num=0,
remark="",
create_by=user_id,
create_dept_id=dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(customer)
db.flush()
existing = (
db.query(BizPilotPatient)
.filter(
BizPilotPatient.pilot_application_id == app_id,
BizPilotPatient.tenant_no == tenant_no,
BizPilotPatient.del_flag == 0,
)
.first()
)
if existing:
existing.customer_id = customer_id
existing.patient_name = data.get("patientName") or ""
existing.sex = data.get("sex") or ""
existing.age = data.get("age")
existing.id_card_no = data.get("idCardNo") or ""
existing.phone_no = data.get("phoneNo") or ""
existing.emergency_contact = data.get("emergencyContact") or ""
existing.emergency_phone = data.get("emergencyPhone") or ""
existing.clinical_diagnosis = data.get("clinicalDiagnosis") or ""
existing.diagnosis_attachments = data.get("diagnosisAttachments") or ""
existing.need_checkup = data.get("needCheckup") if data.get("needCheckup") is not None else 1
existing.remark = data.get("remark") or ""
existing.update_by = user_id
existing.update_time = now
else:
existing = BizPilotPatient(
id=str(uuid.uuid4()),
pilot_application_id=app_id,
customer_id=customer_id,
patient_name=data.get("patientName") or "",
sex=data.get("sex") or "",
age=data.get("age"),
id_card_no=data.get("idCardNo") or "",
phone_no=data.get("phoneNo") or "",
emergency_contact=data.get("emergencyContact") or "",
emergency_phone=data.get("emergencyPhone") or "",
clinical_diagnosis=data.get("clinicalDiagnosis") or "",
diagnosis_attachments=data.get("diagnosisAttachments") or "",
need_checkup=data.get("needCheckup") if data.get("needCheckup") is not None else 1,
tenant_no=tenant_no,
order_num=0,
remark=data.get("remark") or "",
create_by=user_id,
create_dept_id=dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(existing)
db.commit()
db.refresh(existing)
return _row_to_dict_patient(existing)
# ────────────────────────────────────────────
# InfusionPlan CRUD
# ────────────────────────────────────────────
def list_infusion_plans(
db: Session, app_id: str, tenant_no: str
) -> List[Dict[str, Any]]:
rows = (
db.query(BizPilotInfusionPlan)
.filter(
BizPilotInfusionPlan.pilot_application_id == app_id,
BizPilotInfusionPlan.tenant_no == tenant_no,
BizPilotInfusionPlan.del_flag == 0,
)
.order_by(BizPilotInfusionPlan.order_num.asc(), BizPilotInfusionPlan.create_time.asc())
.all()
)
return [_row_to_dict_infusion_plan(r, db, tenant_no) for r in rows]
def create_infusion_plan(
db: Session,
app_id: str,
data: dict,
tenant_no: str,
user_id: str,
dept_id: str,
) -> Dict[str, Any]:
now = datetime.now()
row = BizPilotInfusionPlan(
id=str(uuid.uuid4()),
pilot_application_id=app_id,
cell_type=data.get("cellType") or "",
spec_text=data.get("specText") or "",
total_course_count=data.get("totalCourseCount") or 0,
course_interval=data.get("courseInterval") or "",
infusion_route=data.get("infusionRoute") or "",
infusion_start_date=data.get("infusionStartDate") or "",
tenant_no=tenant_no,
order_num=data.get("orderNum") or 0,
remark=data.get("remark") or "",
create_by=user_id,
create_dept_id=dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(row)
db.commit()
db.refresh(row)
return _row_to_dict_infusion_plan(row, db, tenant_no)
def update_infusion_plan(
db: Session, plan_id: str, data: dict, tenant_no: str
) -> Dict[str, Any]:
row = (
db.query(BizPilotInfusionPlan)
.filter(
BizPilotInfusionPlan.id == plan_id,
BizPilotInfusionPlan.tenant_no == tenant_no,
BizPilotInfusionPlan.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("输注方案不存在")
fields = {
"cellType": "cell_type",
"specText": "spec_text",
"totalCourseCount": "total_course_count",
"courseInterval": "course_interval",
"infusionRoute": "infusion_route",
"infusionStartDate": "infusion_start_date",
"remark": "remark",
}
for camel, snake in fields.items():
if camel in data and data[camel] is not None:
setattr(row, snake, data[camel])
row.update_time = datetime.now()
db.commit()
db.refresh(row)
return _row_to_dict_infusion_plan(row, db, tenant_no)
def soft_delete_infusion_plan(db: Session, plan_id: str, tenant_no: str) -> None:
now = datetime.now()
row = (
db.query(BizPilotInfusionPlan)
.filter(
BizPilotInfusionPlan.id == plan_id,
BizPilotInfusionPlan.tenant_no == tenant_no,
BizPilotInfusionPlan.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("输注方案不存在")
row.del_flag = 1
row.update_time = now
# Also soft-delete all schedules under this plan
db.query(BizPilotInfusionSchedule).filter(
BizPilotInfusionSchedule.infusion_plan_id == plan_id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
db.commit()
# ────────────────────────────────────────────
# InfusionSchedule CRUD
# ────────────────────────────────────────────
def list_schedules_by_plan(
db: Session, plan_id: str, tenant_no: str
) -> List[Dict[str, Any]]:
rows = (
db.query(BizPilotInfusionSchedule)
.filter(
BizPilotInfusionSchedule.infusion_plan_id == plan_id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
)
.order_by(BizPilotInfusionSchedule.order_num.asc())
.all()
)
return [_row_to_dict_infusion_schedule(r) for r in rows]
def auto_generate_schedules(
db: Session,
plan_id: str,
tenant_no: str,
user_id: str,
dept_id: str,
) -> Dict[str, Any]:
plan = (
db.query(BizPilotInfusionPlan)
.filter(
BizPilotInfusionPlan.id == plan_id,
BizPilotInfusionPlan.tenant_no == tenant_no,
BizPilotInfusionPlan.del_flag == 0,
)
.first()
)
if not plan:
raise ValueError("输注方案不存在")
total = plan.total_course_count or 0
if total <= 0:
raise ValueError("总疗程数必须大于0")
now = datetime.now()
for i in range(1, total + 1):
sched = BizPilotInfusionSchedule(
id=str(uuid.uuid4()),
infusion_plan_id=plan_id,
cell_type=plan.cell_type or "",
spec_text=plan.spec_text or "",
schedule_status="",
tenant_no=tenant_no,
order_num=i,
remark="",
create_by=user_id,
create_dept_id=dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(sched)
db.commit()
return {"generatedCount": total}
def create_schedule(
db: Session,
plan_id: str,
data: dict,
tenant_no: str,
user_id: str,
dept_id: str,
) -> Dict[str, Any]:
now = datetime.now()
row = BizPilotInfusionSchedule(
id=str(uuid.uuid4()),
infusion_plan_id=plan_id,
cell_type=data.get("cellType") or "",
spec_text=data.get("specText") or "",
schedule_status=data.get("scheduleStatus") or "",
attachments=data.get("attachments") or "",
tenant_no=tenant_no,
order_num=data.get("orderNum") or 0,
remark=data.get("remark") or "",
create_by=user_id,
create_dept_id=dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(row)
db.commit()
db.refresh(row)
return _row_to_dict_infusion_schedule(row)
def update_schedule(
db: Session, schedule_id: str, data: dict, tenant_no: str
) -> Dict[str, Any]:
row = (
db.query(BizPilotInfusionSchedule)
.filter(
BizPilotInfusionSchedule.id == schedule_id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("输注计划不存在")
if "infusionTime" in data and data["infusionTime"] is not None:
row.infusion_time = data["infusionTime"]
if "remark" in data and data["remark"] is not None:
row.remark = data["remark"]
if "attachments" in data and data["attachments"] is not None:
row.attachments = data["attachments"]
row.update_time = datetime.now()
db.commit()
db.refresh(row)
return _row_to_dict_infusion_schedule(row)
def soft_delete_schedule(db: Session, schedule_id: str, tenant_no: str) -> None:
row = (
db.query(BizPilotInfusionSchedule)
.filter(
BizPilotInfusionSchedule.id == schedule_id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("输注计划不存在")
row.del_flag = 1
row.update_time = datetime.now()
db.commit()
def mark_schedule_complete(
db: Session, schedule_id: str, tenant_no: str, user_id: str
) -> Dict[str, Any]:
row = (
db.query(BizPilotInfusionSchedule)
.filter(
BizPilotInfusionSchedule.id == schedule_id,
BizPilotInfusionSchedule.tenant_no == tenant_no,
BizPilotInfusionSchedule.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("输注计划不存在")
row.schedule_status = "ysc"
row.update_by = user_id
row.update_time = datetime.now()
db.commit()
db.refresh(row)
return _row_to_dict_infusion_schedule(row)