| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- """中试服务申请单管理 API(申请单、患者信息、输注方案、输注计划、出库联动)"""
- from typing import Optional
- from fastapi import APIRouter, Depends, Query
- from pydantic import BaseModel, ConfigDict, Field
- 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):
- model_config = ConfigDict(populate_by_name=True)
- id: str
- class PlanIdBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- plan_id: str = Field(..., alias="infusionPlanId")
- class ApplicationCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- channel_service_order_id: Optional[str] = Field(None, alias="channelServiceOrderId")
- remark: Optional[str] = None
- biz_type: Optional[str] = Field(None, alias="bizType")
- project_title: Optional[str] = Field(None, alias="projectTitle")
- customer_name: Optional[str] = Field(None, alias="customerName")
- customer_contact: Optional[str] = Field(None, alias="customerContact")
- customer_phone: Optional[str] = Field(None, alias="customerPhone")
- class ApplicationUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- id: str
- remark: Optional[str] = None
- project_title: Optional[str] = Field(None, alias="projectTitle")
- customer_name: Optional[str] = Field(None, alias="customerName")
- customer_contact: Optional[str] = Field(None, alias="customerContact")
- customer_phone: Optional[str] = Field(None, alias="customerPhone")
- class PatientSaveBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- application_id: str = Field(..., alias="pilotApplicationId")
- customer_id: Optional[str] = Field(None, alias="customerId")
- patient_name: str = Field(..., alias="patientName")
- sex: Optional[str] = None
- age: Optional[int] = None
- id_card_no: Optional[str] = Field(None, alias="idCardNo")
- phone_no: Optional[str] = Field(None, alias="phoneNo")
- emergency_contact: Optional[str] = Field(None, alias="emergencyContact")
- emergency_phone: Optional[str] = Field(None, alias="emergencyPhone")
- clinical_diagnosis: Optional[str] = Field(None, alias="clinicalDiagnosis")
- diagnosis_attachments: Optional[str] = Field(None, alias="diagnosisAttachments")
- need_checkup: int = Field(1, alias="needCheckup")
- class InfusionPlanCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- pilot_application_id: str = Field(..., alias="pilotApplicationId")
- cell_type: str = Field(..., alias="cellType")
- spec_text: Optional[str] = Field(None, alias="specText")
- total_course_count: int = Field(..., alias="totalCourseCount")
- course_interval: Optional[str] = Field(None, alias="courseInterval")
- infusion_route: Optional[str] = Field(None, alias="infusionRoute")
- infusion_start_date: Optional[str] = Field(None, alias="infusionStartDate")
- class InfusionPlanUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- id: str
- cell_type: Optional[str] = Field(None, alias="cellType")
- spec_text: Optional[str] = Field(None, alias="specText")
- total_course_count: Optional[int] = Field(None, alias="totalCourseCount")
- course_interval: Optional[str] = Field(None, alias="courseInterval")
- infusion_route: Optional[str] = Field(None, alias="infusionRoute")
- infusion_start_date: Optional[str] = Field(None, alias="infusionStartDate")
- class InfusionScheduleUpdateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- id: str
- infusion_time: Optional[str] = Field(None, alias="infusionTime")
- remark: Optional[str] = None
- attachments: Optional[str] = None
- class ScheduleCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- plan_id: str = Field(..., alias="infusionPlanId")
- infusion_time: Optional[str] = Field(None, alias="infusionTime")
- remark: Optional[str] = None
- amount: Optional[float] = None
- # --- 复合保存 Schema(参考采集协议单一次性保存模式)---
- class PatientNestedBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- customer_id: Optional[str] = Field(None, alias="customerId")
- patient_name: Optional[str] = Field(None, alias="patientName")
- sex: Optional[str] = None
- age: Optional[int] = None
- id_card_no: Optional[str] = Field(None, alias="idCardNo")
- phone_no: Optional[str] = Field(None, alias="phoneNo")
- emergency_contact: Optional[str] = Field(None, alias="emergencyContact")
- emergency_phone: Optional[str] = Field(None, alias="emergencyPhone")
- clinical_diagnosis: Optional[str] = Field(None, alias="clinicalDiagnosis")
- diagnosis_attachments: Optional[str] = Field(None, alias="diagnosisAttachments")
- need_checkup: int = Field(1, alias="needCheckup")
- class ScheduleProductNestedBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- id: Optional[str] = None
- price_id: Optional[str] = Field(None, alias="priceId")
- project_code: Optional[str] = Field(None, alias="projectCode")
- project_name: Optional[str] = Field(None, alias="projectName")
- cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
- spec_text: Optional[str] = Field(None, alias="specText")
- agreement_price: Optional[float] = Field(None, alias="agreementPrice")
- actual_price: Optional[float] = Field(None, alias="actualPrice")
- quantity: Optional[int] = 1
- status: Optional[str] = None
- remark: Optional[str] = None
- class InfusionScheduleNestedBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- id: Optional[str] = None
- sort_no: Optional[int] = Field(None, alias="sortNo")
- infusion_time: Optional[str] = Field(None, alias="infusionTime")
- infusion_location: Optional[str] = Field(None, alias="infusionLocation")
- infusion_method: Optional[str] = Field(None, alias="infusionMethod")
- amount: Optional[float] = None
- cell_products: Optional[list[ScheduleProductNestedBody]] = Field(None, alias="cellProducts")
- class InfusionPlanNestedBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- id: Optional[str] = None
- cell_type: Optional[str] = Field(None, alias="cellType")
- spec_text: Optional[str] = Field(None, alias="specText")
- total_course_count: Optional[int] = Field(None, alias="totalCourseCount")
- course_interval: Optional[str] = Field(None, alias="courseInterval")
- infusion_route: Optional[str] = Field(None, alias="infusionRoute")
- infusion_start_date: Optional[str] = Field(None, alias="infusionStartDate")
- channel_agreement_id: Optional[str] = Field(None, alias="channelAgreementId")
- class ApplicationSaveBody(BaseModel):
- """一揽子保存:申请单 + 患者信息 + 输注方案 + 输注计划(含细胞产品)"""
- model_config = ConfigDict(populate_by_name=True)
- id: Optional[str] = None
- application_name: Optional[str] = Field(None, alias="applicationName")
- channel_service_order_id: Optional[str] = Field(None, alias="channelServiceOrderId")
- channel_id: Optional[str] = Field(None, alias="channelId")
- biz_type: Optional[str] = Field(None, alias="bizType")
- project_title: Optional[str] = Field(None, alias="projectTitle")
- customer_name: Optional[str] = Field(None, alias="customerName")
- customer_contact: Optional[str] = Field(None, alias="customerContact")
- customer_phone: Optional[str] = Field(None, alias="customerPhone")
- remark: Optional[str] = None
- consent_form_urls: Optional[str] = Field(None, alias="consentFormUrls")
- consent_attachments: Optional[list] = Field(None, alias="consentAttachments")
- attachment_urls: Optional[str] = Field(None, alias="attachmentUrls")
- other_attachments: Optional[list] = Field(None, alias="otherAttachments")
- patient: Optional[PatientNestedBody] = None
- infusion_plan: Optional[InfusionPlanNestedBody] = Field(None, alias="infusionPlan")
- infusion_schedules: Optional[list[InfusionScheduleNestedBody]] = Field(
- None, alias="infusionSchedules"
- )
- infusion_plans: Optional[list[InfusionPlanNestedBody]] = Field(None, alias="infusionPlans")
- # ---------------------------------------------------------------------------
- # 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"),
- keyword: Optional[str] = Query(None, alias="keyword"),
- channelId: Optional[str] = Query(None, alias="channelId"),
- applicationStatus: Optional[str] = Query(None, alias="applicationStatus"),
- channelServiceOrderId: Optional[str] = Query(None, alias="channelServiceOrderId"),
- bizType: Optional[str] = Query(None, alias="bizType"),
- 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,
- keyword=keyword,
- biz_type=bizType,
- )
- 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 or "",
- "remark": body.remark,
- "bizType": body.biz_type or "pilot_service",
- "projectTitle": body.project_title,
- "customerName": body.customer_name,
- "customerContact": body.customer_contact,
- "customerPhone": body.customer_phone,
- }
- 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
- if body.project_title is not None:
- data["projectTitle"] = body.project_title
- if body.customer_name is not None:
- data["customerName"] = body.customer_name
- if body.customer_contact is not None:
- data["customerContact"] = body.customer_contact
- if body.customer_phone is not None:
- data["customerPhone"] = body.customer_phone
- 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.post("/pilot-application/save")
- async def api_save_application(
- body: ApplicationSaveBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- user_id = user.get("user_id")
- dept_id = user.get("region_id", "")
- is_edit = bool(body.id)
- biz_type = body.biz_type or "pilot_service"
- app_data: dict = {
- "channelServiceOrderId": body.channel_service_order_id or "",
- "channelId": body.channel_id or "",
- "bizType": biz_type,
- "remark": body.remark,
- }
- if body.application_name is not None:
- app_data["applicationName"] = body.application_name
- if body.project_title is not None:
- app_data["projectTitle"] = body.project_title
- if body.customer_name is not None:
- app_data["customerName"] = body.customer_name
- if body.customer_contact is not None:
- app_data["customerContact"] = body.customer_contact
- if body.customer_phone is not None:
- app_data["customerPhone"] = body.customer_phone
- if body.consent_attachments is not None:
- app_data["consentAttachments"] = body.consent_attachments
- elif body.consent_form_urls is not None:
- app_data["consentFormUrls"] = body.consent_form_urls
- if body.other_attachments is not None:
- app_data["otherAttachments"] = body.other_attachments
- elif body.attachment_urls is not None:
- app_data["attachmentUrls"] = body.attachment_urls
- if (
- not is_edit
- and biz_type not in ("clinical_application", "basic_experiment")
- and not app_data["channelServiceOrderId"]
- ):
- return error_response(400, "请选择渠道服务订单")
- if not is_edit and biz_type == "clinical_application" and not app_data["channelId"]:
- return error_response(400, "请选择渠道")
- if is_edit:
- app_result = pilot_application.update_application(
- db,
- app_id=body.id,
- data=app_data,
- tenant_no=tenant_no,
- )
- app_id = body.id
- else:
- app_result = pilot_application.create_application(
- db,
- data=app_data,
- tenant_no=tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- )
- app_id = app_result.get("id")
- if not app_id:
- return error_response(500, "创建申请单失败,未获取到ID")
- if body.patient:
- pilot_application.save_patient(
- db,
- app_id=app_id,
- data=body.patient.model_dump(by_alias=True),
- tenant_no=tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- )
- if body.infusion_plan is not None or body.infusion_schedules:
- plan_dict = (
- body.infusion_plan.model_dump(by_alias=True) if body.infusion_plan else {}
- )
- schedules = [
- s.model_dump(by_alias=True) for s in (body.infusion_schedules or [])
- ]
- if is_edit:
- pilot_application.upsert_application_infusion(
- db,
- app_id=app_id,
- plan_data=plan_dict,
- schedules_data=schedules,
- tenant_no=tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- )
- else:
- pilot_application.save_application_infusion(
- db,
- app_id=app_id,
- plan_data=plan_dict,
- schedules_data=schedules,
- tenant_no=tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- replace_existing=False,
- )
- elif body.infusion_plans:
- for plan_data in body.infusion_plans:
- plan_result = pilot_application.create_infusion_plan(
- db,
- app_id=app_id,
- data=plan_data.model_dump(by_alias=True),
- tenant_no=tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- )
- plan_id = plan_result.get("id")
- if plan_id and (plan_data.total_course_count or 0) > 0:
- pilot_application.auto_generate_schedules(
- db,
- plan_id=plan_id,
- tenant_no=tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- )
- app_result = pilot_application.get_application(db, app_id=app_id, tenant_no=tenant_no)
- return success_response(app_result, message="保存成功")
- except ValueError as e:
- return error_response(400, str(e))
- except Exception as e:
- return error_response(500, 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))
- class SaleScheduleProductLineBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- price_id: Optional[str] = Field(None, alias="priceId")
- project_code: Optional[str] = Field(None, alias="projectCode")
- project_name: Optional[str] = Field(None, alias="projectName")
- cell_type_name: Optional[str] = Field(None, alias="cellTypeName")
- spec_text: Optional[str] = Field(None, alias="specText")
- agreement_price: Optional[float] = Field(None, alias="agreementPrice")
- actual_price: Optional[float] = Field(None, alias="actualPrice")
- quantity: Optional[int] = 1
- pickup_time: Optional[str] = Field(None, alias="pickupTime")
- amount: Optional[float] = None
- class SaleSchedulesCreateBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- product_lines: list[SaleScheduleProductLineBody] = Field(..., alias="productLines")
- @router.post("/pilot-application/{application_id}/sale-schedules")
- async def api_create_sale_schedules(
- application_id: str,
- body: SaleSchedulesCreateBody,
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- product_lines = [line.model_dump(by_alias=True) for line in body.product_lines]
- results = pilot_application.create_basic_experiment_sale_schedules(
- db,
- application_id=application_id,
- product_lines=product_lines,
- tenant_no=tenant_no,
- user_id=user.get("user_id"),
- dept_id=user.get("region_id", ""),
- )
- return success_response(results, 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(..., alias="pilotApplicationId"),
- 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(..., alias="pilotApplicationId"),
- 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.get("/pilot-infusion-schedule/list")
- async def api_list_schedules(
- plan_id: str = Query(..., alias="infusionPlanId"),
- user: dict = Depends(require_admin_or_super_admin),
- db: Session = Depends(get_db),
- ):
- tenant_no = _get_tenant_no(db, user)
- result = pilot_application.list_schedules_by_plan(
- db, plan_id=plan_id, tenant_no=tenant_no
- )
- return success_response(result)
- @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,
- "amount": body.amount,
- }
- 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
- from app.crud.qc_work_order import resolve_outbound_biz_type_for_schedule
- outbound_biz_type = resolve_outbound_biz_type_for_schedule(
- db, body.scheduleId, tenant_no
- )
- 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", ""),
- biz_type=outbound_biz_type,
- )
- db.commit()
- db.refresh(wo)
- 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
- from app.crud.qc_work_order import resolve_outbound_biz_type_for_schedule
- outbound_biz_type = resolve_outbound_biz_type_for_schedule(
- db, body.scheduleId, tenant_no
- )
- 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", ""),
- biz_type=outbound_biz_type,
- )
- 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))
|