"""样本级储存证 / 保险办理 API""" from typing import Optional from fastapi import APIRouter, Depends 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, require_any_menu_code from app.core.response import error_response, success_response from app.core.utils import parse_datetime from app.crud import sample_insurance as insurance_crud from app.crud import sample_storage_cert as cert_crud from app.models.models import BizProtocolSample, SysUser router = APIRouter(prefix="/web/admin", tags=["样本储存证保险"]) _MENU = require_any_menu_code("cell-storage:sample-info:list") _WRITE_MENU = require_admin_or_super_admin 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) def _resolve_protocol_id(db: Session, tenant_no: str, sample_code: str) -> Optional[str]: row = ( db.query(BizProtocolSample) .filter( BizProtocolSample.tenant_no == tenant_no, BizProtocolSample.sample_code == sample_code, BizProtocolSample.del_flag == 0, ) .first() ) return (row.collection_protocol_id or "").strip() or None if row else None class SampleHandleBody(BaseModel): model_config = ConfigDict(populate_by_name=True) handle_time: Optional[str] = Field(None, alias="handleTime") handler_user_id: Optional[str] = Field(None, alias="handlerUserId") cert_image_urls: Optional[str] = Field(None, alias="certImageUrls") insurance_attachments: Optional[str] = Field(None, alias="insuranceAttachments") remark: Optional[str] = None handle_remark: Optional[str] = Field(None, alias="handleRemark") mail_cert_time: Optional[str] = Field(None, alias="mailCertTime") policy_send_time: Optional[str] = Field(None, alias="policySendTime") collection_protocol_id: Optional[str] = Field(None, alias="collectionProtocolId") @router.get("/sample-storage-certs/{sample_code}") async def get_sample_storage_cert( sample_code: str, db: Session = Depends(get_db), user: dict = Depends(_MENU), ): try: tenant_no = _get_tenant_no(db, user) code = (sample_code or "").strip() row = cert_crud.get_by_sample_code(db, tenant_no, code) if row is None: return success_response( { "sampleCode": code, "storageCertHandled": False, "handleTime": "", "handlerUserId": "", "certImageUrls": "", "mailCertTime": "", "remark": "", } ) return success_response(cert_crud.row_to_dict(row)) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.put("/sample-storage-certs/{sample_code}") async def put_sample_storage_cert( sample_code: str, body: SampleHandleBody, db: Session = Depends(get_db), user: dict = Depends(_WRITE_MENU), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) code = (sample_code or "").strip() pid = (body.collection_protocol_id or "").strip() or _resolve_protocol_id( db, tenant_no, code ) ht = parse_datetime(body.handle_time) if body.handle_time else None mail_raw = body.mail_cert_time update_mail = mail_raw is not None mail_t = parse_datetime(mail_raw) if mail_raw and str(mail_raw).strip() else None row = cert_crud.upsert_handle( db, tenant_no=tenant_no, sample_code=code, collection_protocol_id=pid, user_id=user_id, handle_time=ht, handler_user_id=body.handler_user_id, cert_image_urls=body.cert_image_urls or "", remark=body.remark or "", mail_cert_time=mail_t, update_mail_cert_time=update_mail, ) db.commit() return success_response(cert_crud.row_to_dict(row), message="保存成功") except PermissionError as e: db.rollback() return error_response(403, str(e)) except ValueError as e: db.rollback() return error_response(400, str(e)) except Exception as e: db.rollback() return error_response(400, str(e)) @router.get("/sample-insurances/{sample_code}") async def get_sample_insurance( sample_code: str, db: Session = Depends(get_db), user: dict = Depends(_MENU), ): try: tenant_no = _get_tenant_no(db, user) code = (sample_code or "").strip() row = insurance_crud.get_by_sample_code(db, tenant_no, code) if row is None: return success_response( { "sampleCode": code, "insuranceHandled": False, "handleTime": "", "handlerUserId": "", "insuranceAttachments": "", "policySendTime": "", "handleRemark": "", } ) return success_response(insurance_crud.row_to_dict(row)) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.put("/sample-insurances/{sample_code}") async def put_sample_insurance( sample_code: str, body: SampleHandleBody, db: Session = Depends(get_db), user: dict = Depends(_WRITE_MENU), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) code = (sample_code or "").strip() pid = (body.collection_protocol_id or "").strip() or _resolve_protocol_id( db, tenant_no, code ) ht = parse_datetime(body.handle_time) if body.handle_time else None policy_raw = body.policy_send_time update_policy = policy_raw is not None policy_t = ( parse_datetime(policy_raw) if policy_raw and str(policy_raw).strip() else None ) row = insurance_crud.upsert_handle( db, tenant_no=tenant_no, sample_code=code, collection_protocol_id=pid, user_id=user_id, handle_time=ht, handler_user_id=body.handler_user_id, insurance_attachments=body.insurance_attachments or "", handle_remark=body.handle_remark or body.remark or "", policy_send_time=policy_t, update_policy_send_time=update_policy, ) db.commit() return success_response(insurance_crud.row_to_dict(row), message="保存成功") except PermissionError as e: db.rollback() return error_response(403, str(e)) except ValueError as e: db.rollback() return error_response(400, str(e)) except Exception as e: db.rollback() return error_response(400, str(e))