Browse Source

feat(qc): extend outbound work order list API with application/infusion fields and bizType filter

zlwu 5 tháng trước cách đây
mục cha
commit
c9f369a276
2 tập tin đã thay đổi với 83 bổ sung và 1 xóa
  1. 2 0
      backend/app/api/qc.py
  2. 81 1
      backend/app/crud/qc_work_order.py

+ 2 - 0
backend/app/api/qc.py

@@ -596,6 +596,7 @@ async def list_qc_work_orders(
     keyword: Optional[str] = None,
     workOrderStatus: Optional[str] = None,
     workOrderType: Optional[str] = None,
+    bizType: Optional[str] = Query(None, alias="bizType"),
     createTimeStart: Optional[str] = Query(None, alias="createTimeStart"),
     createTimeEnd: Optional[str] = Query(None, alias="createTimeEnd"),
     db: Session = Depends(get_db),
@@ -610,6 +611,7 @@ async def list_qc_work_orders(
             keyword=keyword,
             work_order_status=workOrderStatus,
             work_order_type=workOrderType,
+            biz_type=bizType,
             create_time_start=createTimeStart,
             create_time_end=createTimeEnd,
             tenant_no=tenant_no,

+ 81 - 1
backend/app/crud/qc_work_order.py

@@ -21,6 +21,9 @@ from app.models.models import (
     BizCellProduct,
     BizCellSampleType,
     BizCollectionProtocol,
+    BizPilotApplication,
+    BizPilotInfusionPlan,
+    BizPilotInfusionSchedule,
     BizProtocolSample,
     BizQcLabTestItem,
     BizQcTask,
@@ -53,6 +56,7 @@ _DONATION_STATUS_LABELS = {
     "dsx": "待生效", "sxz": "生效中", "ygq": "已过期", "yzf": "已作废", "zckc": "转细胞存储"
 }
 _CONCLUSION_LABELS = {"tg": "通过", "btg": "不通过"}
+_OUTBOUND_BIZ_TYPE_LABELS = {"pilot_service": "中试服务", "clinical_application": "临床应用"}
 
 
 def _dec(v: Any) -> Optional[float]:
@@ -130,6 +134,63 @@ def _enrich_work_order_cell_products(
     return items
 
 
+def _enrich_outbound_work_orders(
+    db: Session, tenant_no: str, items: List[Dict[str, Any]]
+) -> List[Dict[str, Any]]:
+    """为 outbound 类型工单批量补充申请单/输注计划信息。"""
+    outbound = [x for x in items if x.get("workOrderType") == "outbound"]
+    if not outbound:
+        return items
+    schedule_ids = list({x.get("infusionScheduleId") for x in outbound if x.get("infusionScheduleId")})
+    if not schedule_ids:
+        return items
+    # 查输注计划 → 输注方案 → 申请单
+    schedules = {
+        s.id: s
+        for s in db.query(BizPilotInfusionSchedule)
+        .filter(
+            BizPilotInfusionSchedule.id.in_(schedule_ids),
+            BizPilotInfusionSchedule.tenant_no == tenant_no,
+            BizPilotInfusionSchedule.del_flag == 0,
+        )
+        .all()
+    }
+    plan_ids = list({s.infusion_plan_id for s in schedules.values() if s.infusion_plan_id})
+    plans = {
+        p.id: p
+        for p in db.query(BizPilotInfusionPlan)
+        .filter(
+            BizPilotInfusionPlan.id.in_(plan_ids),
+            BizPilotInfusionPlan.tenant_no == tenant_no,
+            BizPilotInfusionPlan.del_flag == 0,
+        )
+        .all()
+    } if plan_ids else {}
+    app_ids = list({p.pilot_application_id for p in plans.values() if p.pilot_application_id})
+    apps = {
+        a.id: a
+        for a in db.query(BizPilotApplication)
+        .filter(
+            BizPilotApplication.id.in_(app_ids),
+            BizPilotApplication.tenant_no == tenant_no,
+            BizPilotApplication.del_flag == 0,
+        )
+        .all()
+    } if app_ids else {}
+    for item in outbound:
+        sid = item.get("infusionScheduleId") or ""
+        sched = schedules.get(sid)
+        if not sched:
+            continue
+        plan = plans.get(sched.infusion_plan_id or "")
+        app = apps.get(plan.pilot_application_id or "") if plan else None
+        item["applicationNo"] = app.application_no or "" if app else ""
+        item["applicationName"] = app.application_name or "" if app else ""
+        item["channelName"] = app.channel_name or "" if app else ""
+        item["infusionTime"] = _fmt_dt(sched.infusion_time)
+    return items
+
+
 def _accept_conclusion_label(v: Optional[str]) -> str:
     key = (v or "").strip()
     return _ACCEPT_CONCLUSION_LABELS.get(key, key)
@@ -290,13 +351,20 @@ def _gen_daily_no(db: Session, tenant_no: str, prefix: str) -> str:
     return f"{prefix}{day}{cnt + 1:04d}"
 
 
-def _wo_to_dict(wo: BizQcWorkOrder) -> Dict[str, Any]:
+def _wo_to_dict(
+    wo: BizQcWorkOrder,
+    *,
+    outbound_info: Optional[Dict[str, Any]] = None,
+) -> Dict[str, Any]:
     wo_type = getattr(wo, "work_order_type", None) or "inbound"
     recv_source_raw = getattr(wo, "reception_source", None)
     if wo_type == "temporary":
         recv_source = (recv_source_raw or "").strip()
     else:
         recv_source = (recv_source_raw or "cell_storage").strip() or "cell_storage"
+    biz_type = getattr(wo, "biz_type", None) or ""
+    is_outbound = wo_type == "outbound"
+    ob = outbound_info or {}
     return {
         "id": wo.id,
         "workOrderNo": wo.work_order_no,
@@ -311,6 +379,12 @@ def _wo_to_dict(wo: BizQcWorkOrder) -> Dict[str, Any]:
         "qcBizType": _work_order_qc_biz_type(wo),
         "qcBizTypeLabel": _qc_biz_type_label(_work_order_qc_biz_type(wo)),
         "infusionScheduleId": getattr(wo, "infusion_schedule_id", None) or None,
+        "bizType": biz_type if is_outbound else "",
+        "bizTypeLabel": _OUTBOUND_BIZ_TYPE_LABELS.get(biz_type, "") if is_outbound else "",
+        "applicationNo": ob.get("applicationNo", "") if is_outbound else "",
+        "applicationName": ob.get("applicationName", "") if is_outbound else "",
+        "channelName": ob.get("channelName", "") if is_outbound else "",
+        "infusionTime": ob.get("infusionTime", "") if is_outbound else "",
         "cellProductId": getattr(wo, "cell_product_id", None) or "",
         "cellProductCode": "",
         "cellProductName": "",
@@ -458,6 +532,7 @@ def list_work_orders(
     keyword: Optional[str] = None,
     work_order_status: Optional[str] = None,
     work_order_type: Optional[str] = None,
+    biz_type: Optional[str] = None,
     create_time_start: Optional[str] = None,
     create_time_end: Optional[str] = None,
     tenant_no: Optional[str] = None,
@@ -470,6 +545,9 @@ def list_work_orders(
         wt = (work_order_type or "").strip()
         if wt:
             q0 = q0.filter(BizQcWorkOrder.work_order_type == wt)
+        bt = (biz_type or "").strip()
+        if bt:
+            q0 = q0.filter(BizQcWorkOrder.biz_type == bt)
         kw = (keyword or "").strip()
         if kw:
             like = f"%{kw}%"
@@ -511,6 +589,7 @@ def list_work_orders(
     items = _enrich_work_order_list_items(db, items)
     if tenant_no:
         items = _enrich_work_order_cell_products(db, tenant_no, items)
+        items = _enrich_outbound_work_orders(db, tenant_no, items)
     return items, int(total)
 
 
@@ -1500,6 +1579,7 @@ def create_outbound_qc_work_order(
         sample_code=None,
         reception_source="outbound",
         qc_biz_type="outbound",
+        biz_type="pilot_service",
         infusion_schedule_id=schedule_id,
         cell_product_id=cell_product_id or None,
         work_order_status="dpz",