Files
zkxw/backend/app/api/pilot_application.py
T
2026-05-25 15:14:06 +08:00

561 lines
18 KiB
Python

"""中试服务申请单管理 API(申请单、患者信息、输注方案、输注计划、出库联动)"""
from typing import Optional
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.menu_guard import require_admin_or_super_admin
from app.core.response import error_response, success_response
from app.crud import pilot_application
from app.models.models import SysUser
router = APIRouter(prefix="/web/admin", tags=["中试服务申请单管理"])
def _get_tenant_no(db: Session, user: dict) -> str:
user_id = user.get("user_id")
if not user_id:
raise PermissionError("用户身份信息缺失,无法确定租户")
sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
if not sys_user or not sys_user.tenant_no:
raise PermissionError("用户租户信息缺失")
return sys_user.tenant_no
# ---------------------------------------------------------------------------
# Pydantic Schemas
# ---------------------------------------------------------------------------
class IdBody(BaseModel):
id: str
class PlanIdBody(BaseModel):
plan_id: str
class ApplicationCreateBody(BaseModel):
channel_service_order_id: str
remark: Optional[str] = None
class ApplicationUpdateBody(BaseModel):
id: str
remark: Optional[str] = None
class PatientSaveBody(BaseModel):
application_id: str
customer_id: Optional[str] = None
patient_name: str
sex: Optional[str] = None
age: Optional[int] = None
id_card_no: Optional[str] = None
phone_no: Optional[str] = None
emergency_contact: Optional[str] = None
emergency_phone: Optional[str] = None
clinical_diagnosis: Optional[str] = None
diagnosis_attachments: Optional[str] = None
need_checkup: int = 1
class InfusionPlanCreateBody(BaseModel):
pilot_application_id: str
cell_type: str
spec_text: Optional[str] = None
total_course_count: int
course_interval: Optional[str] = None
infusion_route: Optional[str] = None
infusion_start_date: Optional[str] = None
class InfusionPlanUpdateBody(BaseModel):
id: str
cell_type: Optional[str] = None
spec_text: Optional[str] = None
total_course_count: Optional[int] = None
course_interval: Optional[str] = None
infusion_route: Optional[str] = None
infusion_start_date: Optional[str] = None
class InfusionScheduleUpdateBody(BaseModel):
id: str
infusion_time: Optional[str] = None
remark: Optional[str] = None
attachments: Optional[str] = None
class ScheduleCreateBody(BaseModel):
plan_id: str
infusion_time: Optional[str] = None
remark: Optional[str] = None
# ---------------------------------------------------------------------------
# 1. 申请单 CRUD + 状态变更
# ---------------------------------------------------------------------------
@router.get("/pilot-application/list")
async def api_list_applications(
page: int = Query(1, ge=1),
pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
channelId: Optional[str] = Query(None, alias="channelId"),
applicationStatus: Optional[str] = Query(None, alias="applicationStatus"),
channelServiceOrderId: Optional[str] = Query(None, alias="channelServiceOrderId"),
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _get_tenant_no(db, user)
result = pilot_application.list_applications(
db,
tenant_no=tenant_no,
page=page,
page_size=pageSize,
channel_id=channelId,
application_status=applicationStatus,
channel_service_order_id=channelServiceOrderId,
)
return success_response(result)
@router.get("/pilot-application/detail")
async def api_get_application(
id: str = Query(...),
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _get_tenant_no(db, user)
result = pilot_application.get_application(db, app_id=id, tenant_no=tenant_no)
if result is None:
return error_response(404, "申请单不存在")
return success_response(result)
@router.post("/pilot-application/create")
async def api_create_application(
body: ApplicationCreateBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {
"channelServiceOrderId": body.channel_service_order_id,
"remark": body.remark,
}
result = pilot_application.create_application(
db,
data=data,
tenant_no=tenant_no,
user_id=user.get("user_id"),
dept_id=user.get("region_id", ""),
)
return success_response(result, message="创建成功")
except ValueError as e:
return error_response(400, str(e))
@router.put("/pilot-application/update")
async def api_update_application(
body: ApplicationUpdateBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {}
if body.remark is not None:
data["remark"] = body.remark
result = pilot_application.update_application(
db, app_id=body.id, data=data, tenant_no=tenant_no
)
return success_response(result, message="更新成功")
except ValueError as e:
return error_response(400, str(e))
@router.delete("/pilot-application/delete")
async def api_delete_application(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
pilot_application.soft_delete_application(db, app_id=body.id, tenant_no=tenant_no)
return success_response(None, message="删除成功")
except ValueError as e:
return error_response(400, str(e))
@router.post("/pilot-application/start")
async def api_start_application(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
result = pilot_application.start_application(
db, app_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id")
)
return success_response(result, message="操作成功")
except ValueError as e:
return error_response(400, str(e))
@router.post("/pilot-application/complete")
async def api_complete_application(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
result = pilot_application.complete_application(
db, app_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id")
)
return success_response(result, message="操作成功")
except ValueError as e:
return error_response(400, str(e))
@router.post("/pilot-application/void")
async def api_void_application(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
result = pilot_application.void_application(
db, app_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id")
)
return success_response(result, message="操作成功")
except ValueError as e:
return error_response(400, str(e))
# ---------------------------------------------------------------------------
# 2. 患者信息
# ---------------------------------------------------------------------------
@router.get("/pilot-patient/detail")
async def api_get_patient(
application_id: str = Query(...),
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _get_tenant_no(db, user)
result = pilot_application.get_patient_by_application(
db, app_id=application_id, tenant_no=tenant_no
)
if result is None:
return success_response(None)
return success_response(result)
@router.post("/pilot-patient/save")
async def api_save_patient(
body: PatientSaveBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {
"customerId": body.customer_id,
"patientName": body.patient_name,
"sex": body.sex,
"age": body.age,
"idCardNo": body.id_card_no,
"phoneNo": body.phone_no,
"emergencyContact": body.emergency_contact,
"emergencyPhone": body.emergency_phone,
"clinicalDiagnosis": body.clinical_diagnosis,
"diagnosisAttachments": body.diagnosis_attachments,
"needCheckup": body.need_checkup,
}
result = pilot_application.save_patient(
db,
app_id=body.application_id,
data=data,
tenant_no=tenant_no,
user_id=user.get("user_id"),
dept_id=user.get("region_id", ""),
)
return success_response(result, message="保存成功")
except ValueError as e:
return error_response(400, str(e))
# ---------------------------------------------------------------------------
# 3. 输注方案
# ---------------------------------------------------------------------------
@router.get("/pilot-infusion-plan/list")
async def api_list_plans(
application_id: str = Query(...),
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _get_tenant_no(db, user)
result = pilot_application.list_infusion_plans(
db, app_id=application_id, tenant_no=tenant_no
)
return success_response(result)
@router.post("/pilot-infusion-plan/create")
async def api_create_plan(
body: InfusionPlanCreateBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {
"cellType": body.cell_type,
"specText": body.spec_text,
"totalCourseCount": body.total_course_count,
"courseInterval": body.course_interval,
"infusionRoute": body.infusion_route,
"infusionStartDate": body.infusion_start_date,
}
result = pilot_application.create_infusion_plan(
db,
app_id=body.pilot_application_id,
data=data,
tenant_no=tenant_no,
user_id=user.get("user_id"),
dept_id=user.get("region_id", ""),
)
return success_response(result, message="创建成功")
except ValueError as e:
return error_response(400, str(e))
@router.put("/pilot-infusion-plan/update")
async def api_update_plan(
body: InfusionPlanUpdateBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {}
if body.cell_type is not None:
data["cellType"] = body.cell_type
if body.spec_text is not None:
data["specText"] = body.spec_text
if body.total_course_count is not None:
data["totalCourseCount"] = body.total_course_count
if body.course_interval is not None:
data["courseInterval"] = body.course_interval
if body.infusion_route is not None:
data["infusionRoute"] = body.infusion_route
if body.infusion_start_date is not None:
data["infusionStartDate"] = body.infusion_start_date
result = pilot_application.update_infusion_plan(
db, plan_id=body.id, data=data, tenant_no=tenant_no
)
return success_response(result, message="更新成功")
except ValueError as e:
return error_response(400, str(e))
@router.delete("/pilot-infusion-plan/delete")
async def api_delete_plan(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
pilot_application.soft_delete_infusion_plan(
db, plan_id=body.id, tenant_no=tenant_no
)
return success_response(None, message="删除成功")
except ValueError as e:
return error_response(400, str(e))
# ---------------------------------------------------------------------------
# 4. 输注计划
# ---------------------------------------------------------------------------
@router.post("/pilot-infusion-schedule/auto-generate")
async def api_auto_generate(
body: PlanIdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
result = pilot_application.auto_generate_schedules(
db,
plan_id=body.plan_id,
tenant_no=tenant_no,
user_id=user.get("user_id"),
dept_id=user.get("region_id", ""),
)
return success_response(result, message="生成成功")
except ValueError as e:
return error_response(400, str(e))
@router.post("/pilot-infusion-schedule/create")
async def api_create_schedule(
body: ScheduleCreateBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {
"infusionTime": body.infusion_time,
"remark": body.remark,
}
result = pilot_application.create_schedule(
db,
plan_id=body.plan_id,
data=data,
tenant_no=tenant_no,
user_id=user.get("user_id"),
dept_id=user.get("region_id", ""),
)
return success_response(result, message="创建成功")
except ValueError as e:
return error_response(400, str(e))
@router.put("/pilot-infusion-schedule/update")
async def api_update_schedule(
body: InfusionScheduleUpdateBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
data = {}
if body.infusion_time is not None:
data["infusionTime"] = body.infusion_time
if body.remark is not None:
data["remark"] = body.remark
if body.attachments is not None:
data["attachments"] = body.attachments
result = pilot_application.update_schedule(
db, schedule_id=body.id, data=data, tenant_no=tenant_no
)
return success_response(result, message="更新成功")
except ValueError as e:
return error_response(400, str(e))
@router.delete("/pilot-infusion-schedule/delete")
async def api_delete_schedule(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
pilot_application.soft_delete_schedule(
db, schedule_id=body.id, tenant_no=tenant_no
)
return success_response(None, message="删除成功")
except ValueError as e:
return error_response(400, str(e))
@router.post("/pilot-infusion-schedule/mark-complete")
async def api_mark_complete(
body: IdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
result = pilot_application.mark_schedule_complete(
db, schedule_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id")
)
return success_response(result, message="操作成功")
except ValueError as e:
return error_response(400, str(e))
# ---------------------------------------------------------------------------
# 出库生产工单 / 出库质检工单 联动
# ---------------------------------------------------------------------------
class ScheduleIdBody(BaseModel):
scheduleId: str
@router.post("/pilot-infusion-schedule/create-production-work-order")
async def api_create_outbound_production(
body: ScheduleIdBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
from app.crud import production_work_order as pwo_svc
wo = pwo_svc.create_outbound_production_work_order(
db,
schedule_id=body.scheduleId,
tenant_no=tenant_no,
user_id=user.get("user_id"),
create_dept_id=user.get("dept_id", ""),
)
pilot_application.mark_schedule_complete(
db, schedule_id=body.scheduleId, tenant_no=tenant_no, user_id=user.get("user_id")
)
return success_response(pwo_svc._wo_to_dict(wo), message="创建出库生产工单成功")
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
db.rollback()
return error_response(400, str(e))
class OutboundQcBody(BaseModel):
scheduleId: str
cellProductId: str = ""
@router.post("/pilot-infusion-schedule/create-qc-work-order")
async def api_create_outbound_qc(
body: OutboundQcBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
from app.crud import qc_work_order as qc_svc
wo = qc_svc.create_outbound_qc_work_order(
db,
schedule_id=body.scheduleId,
cell_product_id=body.cellProductId,
tenant_no=tenant_no,
user_id=user.get("user_id"),
create_dept_id=user.get("dept_id", ""),
)
return success_response(qc_svc._wo_to_dict(wo), message="创建出库质检工单成功")
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
db.rollback()
return error_response(400, str(e))