Files
zkxw/backend/app/api/protocol_sample_collection.py
T

309 lines
12 KiB
Python

"""采集协议样本采集信息 API"""
from typing import List, 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
from app.core.response import error_response, success_response
from app.core.utils import parse_date, parse_datetime
from app.crud import protocol_sample_collection as svc
from app.crud import sample_info_quick_register as quick_svc
from app.crud.collection_protocol import total_pages
from app.models.models import SysUser
from app.services.sample_customer_service_register import has_any_customer_service_payload
router = APIRouter(prefix="/web/admin", tags=["采集协议样本采集信息"])
_SAMPLE_INFO_MENU = require_any_menu_code(
"cell-storage:sample-info:list",
)
_QUICK_REGISTER_MENU = require_any_menu_code(
"cell-storage:sample-info:list",
"research-donation:sample-info: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 _page_payload(items: list, total: int, page: int, page_size: int) -> dict:
return {
"list": items,
"total": total,
"page": page,
"pageSize": page_size,
"totalPages": total_pages(total, page_size),
}
@router.get("/protocol-sample-collections/list")
async def list_protocol_sample_collections(
page: int = Query(1, ge=1),
pageSize: int = Query(10, ge=1, le=100),
keyword: Optional[str] = None,
sampleStatus: Optional[str] = Query(None, alias="sampleStatus"),
collectionFlag: Optional[str] = Query(None, alias="collectionFlag"),
receptionFlag: Optional[str] = Query(None, alias="receptionFlag"),
qcFlag: Optional[str] = Query(None, alias="qcFlag"),
storageFlag: Optional[str] = Query(None, alias="storageFlag"),
storageCertHandled: Optional[str] = Query(None, alias="storageCertHandled"),
insuranceHandled: Optional[str] = Query(None, alias="insuranceHandled"),
db: Session = Depends(get_db),
user: dict = Depends(_SAMPLE_INFO_MENU),
):
try:
tenant_no = _get_tenant_no(db, user)
items, total = svc.list_protocol_sample_collections(
db,
page=page,
page_size=pageSize,
tenant_no=tenant_no,
keyword=keyword,
sample_status=sampleStatus,
collection_flag=collectionFlag,
reception_flag=receptionFlag,
qc_flag=qcFlag,
storage_flag=storageFlag,
storage_cert_handled=storageCertHandled,
insurance_handled=insuranceHandled,
user_info=user,
)
return success_response(_page_payload(items, total, page, pageSize))
except PermissionError as e:
return error_response(403, str(e))
except Exception as e:
return error_response(400, str(e))
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 QuickRegisterItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
sample_code: str = Field(..., alias="sampleCode")
protocol_id: str = Field(..., alias="protocolId")
class QuickRegisterBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
kind: str
reception_source: str = Field(..., alias="receptionSource")
items: List[QuickRegisterItem]
handle_time: Optional[str] = Field(None, alias="handleTime")
handler_user_id: Optional[str] = Field(None, alias="handlerUserId")
attachments: Optional[str] = None
remark: Optional[str] = None
mail_cert_time: Optional[str] = Field(None, alias="mailCertTime")
policy_send_time: Optional[str] = Field(None, alias="policySendTime")
sms_send_date: Optional[str] = Field(None, alias="smsSendDate")
sms_send_status: Optional[str] = Field(None, alias="smsSendStatus")
microbe_followup_date: Optional[str] = Field(None, alias="microbeFollowupDate")
microbe_followup_status: Optional[str] = Field(None, alias="microbeFollowupStatus")
fullmoon_followup_date: Optional[str] = Field(None, alias="fullmoonFollowupDate")
fullmoon_followup_status: Optional[str] = Field(None, alias="fullmoonFollowupStatus")
household_registered: Optional[str] = Field(None, alias="householdRegistered")
einvoice_send_time: Optional[str] = Field(None, alias="einvoiceSendTime")
effective_address: Optional[str] = Field(None, alias="effectiveAddress")
def _optional_dt(raw: Optional[str]):
if raw is None:
return None, False
text = str(raw).strip()
if not text:
return None, False
return parse_datetime(text), True
def _customer_service_incoming_from_body(body: QuickRegisterBody) -> dict:
return {
"sms_send_date": parse_date(body.sms_send_date) if body.sms_send_date else None,
"sms_send_status": body.sms_send_status,
"microbe_followup_date": parse_date(body.microbe_followup_date)
if body.microbe_followup_date
else None,
"microbe_followup_status": body.microbe_followup_status,
"fullmoon_followup_date": parse_date(body.fullmoon_followup_date)
if body.fullmoon_followup_date
else None,
"fullmoon_followup_status": body.fullmoon_followup_status,
"household_registered": body.household_registered,
"einvoice_send_time": parse_datetime(body.einvoice_send_time)
if body.einvoice_send_time
else None,
"effective_address": body.effective_address,
}
@router.post("/sample-info/quick-register")
async def quick_register_sample_info(
body: QuickRegisterBody,
db: Session = Depends(get_db),
user: dict = Depends(_QUICK_REGISTER_MENU),
):
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
ht = parse_datetime(body.handle_time) if body.handle_time else None
items = [
{"sampleCode": it.sample_code, "protocolId": it.protocol_id} for it in (body.items or [])
]
kind = (body.kind or "").strip()
source = (body.reception_source or "").strip()
attachments = body.attachments or ""
remark = body.remark or ""
mail_t, update_mail = _optional_dt(body.mail_cert_time)
policy_t, update_policy = _optional_dt(body.policy_send_time)
cs_incoming = _customer_service_incoming_from_body(body)
if kind == "storage":
if ht is None:
return error_response(400, "请填写办理时间")
result = quick_svc.batch_quick_register_storage(
db,
items=items,
reception_source=source,
tenant_no=tenant_no,
user=user,
user_id=user_id,
handle_time=ht,
handler_user_id=body.handler_user_id,
attachments=attachments,
remark=remark,
)
elif kind == "storage_cert":
if source != "cell_storage":
return error_response(400, "储存证仅支持细胞存储样本")
if ht is None:
return error_response(400, "请填写办理时间")
result = quick_svc.batch_handle_storage_certs(
db,
items=items,
tenant_no=tenant_no,
user=user,
user_id=user_id,
handle_time=ht,
handler_user_id=body.handler_user_id,
attachments=attachments,
remark=remark,
mail_cert_time=mail_t,
update_mail_cert_time=update_mail,
)
elif kind == "insurance":
if source != "cell_storage":
return error_response(400, "保险仅支持细胞存储样本")
if ht is None:
return error_response(400, "请填写办理时间")
result = quick_svc.batch_handle_insurance(
db,
items=items,
tenant_no=tenant_no,
user=user,
user_id=user_id,
handle_time=ht,
handler_user_id=body.handler_user_id,
attachments=attachments,
remark=remark,
policy_send_time=policy_t,
update_policy_send_time=update_policy,
)
elif kind in ("sms", "followup", "fulfillment"):
if source != "cell_storage":
return error_response(400, "客户服务登记仅支持细胞存储样本")
result = quick_svc.batch_register_customer_service(
db,
items=items,
tenant_no=tenant_no,
user=user,
user_id=user_id,
incoming=cs_incoming,
)
elif kind == "all":
if source != "cell_storage":
return error_response(400, "登记全部仅支持细胞存储样本")
wrote_any = False
success = 0
fail = 0
if ht is not None:
cert_r = quick_svc.batch_handle_storage_certs(
db,
items=items,
tenant_no=tenant_no,
user=user,
user_id=user_id,
handle_time=ht,
handler_user_id=body.handler_user_id,
attachments=attachments,
remark=remark,
mail_cert_time=mail_t,
update_mail_cert_time=update_mail,
do_commit=False,
)
ins_r = quick_svc.batch_handle_insurance(
db,
items=items,
tenant_no=tenant_no,
user=user,
user_id=user_id,
handle_time=ht,
handler_user_id=body.handler_user_id,
attachments=attachments,
remark=remark,
policy_send_time=policy_t,
update_policy_send_time=update_policy,
do_commit=False,
)
success = max(cert_r.get("success", 0), ins_r.get("success", 0))
fail = max(cert_r.get("fail", 0), ins_r.get("fail", 0))
wrote_any = True
elif update_mail or update_policy:
# 仅补时间线字段时仍要求办理时间走证险主路径;此处拒绝避免半写
return error_response(400, "登记储存证/保险请填写办理时间")
if has_any_customer_service_payload(cs_incoming):
cs_r = quick_svc.batch_register_customer_service(
db,
items=items,
tenant_no=tenant_no,
user=user,
user_id=user_id,
incoming=cs_incoming,
do_commit=False,
)
success = max(success, cs_r.get("success", 0))
fail = max(fail, cs_r.get("fail", 0))
wrote_any = True
if not wrote_any:
return error_response(400, "请至少填写一项登记信息")
db.commit()
result = {"success": success, "fail": fail}
else:
return error_response(400, "登记类型无效")
return success_response(result, 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))