| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- """生产部门样本接收 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_any_menu_code, require_menu_code
- from app.core.response import error_response, success_response
- from app.core.utils import parse_datetime
- from app.crud import sample_production_reception as prod_rec_svc
- from app.crud import sample_reception as sr_svc
- from app.models.models import SysUser
- router = APIRouter(prefix="/web/admin", tags=["生产管理"])
- PRODUCTION_RECEPTION_READ_MENU_CODES = (
- "production:work-order:list",
- "production:my-task:list",
- "biz:sampleReception:list",
- "production:sample-reception-completion:list",
- )
- PRODUCTION_RECEPTION_WRITE_MENU_CODES = (
- "production:work-order:list",
- "production:my-task:list",
- )
- 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
- def _get_current_user_id(user: dict) -> str:
- user_id = user.get("user_id")
- if not user_id:
- raise PermissionError("用户身份信息缺失")
- return str(user_id)
- class ProductionReceptionBody(BaseModel):
- model_config = ConfigDict(populate_by_name=True)
- reception_id: Optional[str] = Field(None, alias="receptionId")
- sample_code: Optional[str] = Field(None, alias="sampleCode")
- source_type: Optional[str] = Field("cell_storage", alias="sourceType")
- sample_type_name: Optional[str] = Field(None, alias="sampleTypeName")
- handler_user_id: Optional[str] = Field(None, alias="handlerUserId")
- handler_name: Optional[str] = Field(None, alias="handlerName")
- process_time: str = Field(..., alias="processTime")
- original_blood_weight_g: Optional[float] = Field(None, alias="originalBloodWeightG")
- hes_volume_ml: Optional[float] = Field(None, alias="hesVolumeMl")
- blood_type: Optional[str] = Field(None, alias="bloodType")
- infectious_four_test_result: Optional[str] = Field(None, alias="infectiousFourTestResult")
- enriched_blood_weight_g: Optional[float] = Field(None, alias="enrichedBloodWeightG")
- cryopreservation_volume_ml: Optional[float] = Field(None, alias="cryopreservationVolumeMl")
- inoculation_amount: Optional[float] = Field(None, alias="inoculationAmount")
- cryopreservation_time: Optional[str] = Field(None, alias="cryopreservationTime")
- storage_total_tubes: Optional[int] = Field(None, alias="storageTotalTubes")
- available_cell_count_1e7: Optional[float] = Field(None, alias="availableCellCount1e7")
- original_blood_volume_ml: Optional[float] = Field(None, alias="originalBloodVolumeMl")
- nucleated_cell_count_1e8: Optional[float] = Field(None, alias="nucleatedCellCount1e8")
- remark: Optional[str] = None
- def _body_to_payload(body: ProductionReceptionBody) -> dict:
- cryo_time = parse_datetime(body.cryopreservation_time) if body.cryopreservation_time else None
- process_time = parse_datetime(body.process_time)
- if not process_time:
- raise ValueError("处理时间格式无效")
- return {
- "handlerUserId": body.handler_user_id,
- "handlerName": body.handler_name,
- "processTime": process_time,
- "sampleTypeName": body.sample_type_name,
- "originalBloodWeightG": body.original_blood_weight_g,
- "hesVolumeMl": body.hes_volume_ml,
- "bloodType": body.blood_type,
- "infectiousFourTestResult": body.infectious_four_test_result,
- "enrichedBloodWeightG": body.enriched_blood_weight_g,
- "cryopreservationVolumeMl": body.cryopreservation_volume_ml,
- "inoculationAmount": body.inoculation_amount,
- "cryopreservationTime": cryo_time,
- "storageTotalTubes": body.storage_total_tubes,
- "availableCellCount1e7": body.available_cell_count_1e7,
- "originalBloodVolumeMl": body.original_blood_volume_ml,
- "nucleatedCellCount1e8": body.nucleated_cell_count_1e8,
- "remark": body.remark,
- }
- def _resolve_reception_id(
- db: Session,
- *,
- tenant_no: str,
- reception_id: Optional[str],
- sample_code: Optional[str],
- source_type: Optional[str],
- ) -> str:
- rid = (reception_id or "").strip()
- if rid:
- return rid
- code = (sample_code or "").strip()
- if not code:
- raise ValueError("请提供 receptionId 或 sampleCode")
- result = sr_svc.get_reception_by_sample_code(
- db, sample_code=code, source_type=source_type, tenant_no=tenant_no
- )
- found = (result.get("receptionId") or "").strip()
- if not found:
- raise ValueError("请先完成质检样本接收登记")
- return found
- @router.get("/production-sample-receptions/by-sample-code")
- async def get_production_reception_by_sample_code(
- sampleCode: str = Query(..., alias="sampleCode"),
- sourceType: Optional[str] = Query(None, alias="sourceType"),
- db: Session = Depends(get_db),
- user: dict = Depends(require_any_menu_code(*PRODUCTION_RECEPTION_READ_MENU_CODES)),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- base = sr_svc.get_reception_by_sample_code(
- db, sample_code=sampleCode, source_type=sourceType, tenant_no=tenant_no
- )
- reception_id = base.get("receptionId")
- qc_reception = None
- production_reception = None
- if reception_id:
- qc_reception = sr_svc.get_reception_by_id(db, reception_id, tenant_no)
- production_reception = prod_rec_svc.get_by_reception_id(db, reception_id, tenant_no)
- return success_response(
- {
- "receptionId": reception_id,
- "sampleInfo": base.get("sampleInfo"),
- "qcReception": qc_reception,
- "productionReception": production_reception,
- }
- )
- except PermissionError as e:
- return error_response(403, str(e))
- except Exception as e:
- return error_response(400, str(e))
- @router.get("/production-sample-receptions/{row_id}")
- async def get_production_reception_detail(
- row_id: str,
- db: Session = Depends(get_db),
- user: dict = Depends(require_any_menu_code(*PRODUCTION_RECEPTION_READ_MENU_CODES)),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- item = prod_rec_svc.get_by_id(db, row_id, tenant_no)
- if not item:
- return error_response(404, "生产接收记录不存在")
- return success_response(item)
- except PermissionError as e:
- return error_response(403, str(e))
- except Exception as e:
- return error_response(400, str(e))
- @router.post("/production-sample-receptions")
- async def post_production_reception(
- body: ProductionReceptionBody,
- db: Session = Depends(get_db),
- user: dict = Depends(require_any_menu_code(*PRODUCTION_RECEPTION_WRITE_MENU_CODES)),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- user_id = _get_current_user_id(user)
- sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
- dept_id = (sys_user.dept_id or "") if sys_user else ""
- reception_id = _resolve_reception_id(
- db,
- tenant_no=tenant_no,
- reception_id=body.reception_id,
- sample_code=body.sample_code,
- source_type=body.source_type,
- )
- payload = _body_to_payload(body)
- type_name = (body.sample_type_name or "").strip()
- if not type_name:
- sample_info = sr_svc.get_reception_by_sample_code(
- db,
- sample_code=(body.sample_code or "").strip(),
- source_type=body.source_type,
- tenant_no=tenant_no,
- ).get("sampleInfo")
- if isinstance(sample_info, dict):
- type_name = str(sample_info.get("sampleTypeName") or "")
- obj = prod_rec_svc.create_production_reception(
- db,
- reception_id=reception_id,
- tenant_no=tenant_no,
- user_id=user_id,
- create_dept_id=dept_id,
- data=payload,
- sample_type_name=type_name,
- )
- return success_response(prod_rec_svc.get_by_id(db, obj.id, tenant_no))
- except PermissionError as e:
- return error_response(403, str(e))
- except ValueError as e:
- return error_response(400, str(e))
- except Exception as e:
- return error_response(400, str(e))
- @router.put("/production-sample-receptions/{row_id}")
- async def put_production_reception(
- row_id: str,
- body: ProductionReceptionBody,
- db: Session = Depends(get_db),
- user: dict = Depends(require_any_menu_code(*PRODUCTION_RECEPTION_WRITE_MENU_CODES)),
- ):
- try:
- tenant_no = _get_tenant_no(db, user)
- user_id = _get_current_user_id(user)
- payload = _body_to_payload(body)
- obj = prod_rec_svc.update_production_reception(
- db,
- row_id=row_id,
- tenant_no=tenant_no,
- user_id=user_id,
- data=payload,
- )
- return success_response(prod_rec_svc.get_by_id(db, obj.id, tenant_no))
- except PermissionError as e:
- return error_response(403, str(e))
- except ValueError as e:
- return error_response(400, str(e))
- except Exception as e:
- return error_response(400, str(e))
|