| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956 |
- """中试申请单-体检 CRUD(体检信息、预约项目、实际项目)"""
- from __future__ import annotations
- import uuid
- from datetime import datetime
- from typing import Any, Dict, List, Optional
- from sqlalchemy import func, or_
- from sqlalchemy.orm import Session
- from app.models.models import (
- BizCheckupInstitution,
- BizPilotApplication,
- BizPilotCheckup,
- BizPilotCheckupActualItem,
- BizPilotCheckupReserveItem,
- BizPilotPatient,
- )
- # ────────────────────────────────────────────
- # Helpers
- # ────────────────────────────────────────────
- def _parse_optional_datetime(val: Any) -> Optional[datetime]:
- if val is None or val == "":
- return None
- if isinstance(val, datetime):
- return val
- if isinstance(val, str):
- text = val.strip()
- for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M"):
- try:
- return datetime.strptime(text, fmt)
- except ValueError:
- continue
- try:
- return datetime.fromisoformat(text.replace("Z", "+00:00"))
- except ValueError:
- return None
- return None
- # ────────────────────────────────────────────
- # Helpers: _row_to_dict
- # ────────────────────────────────────────────
- def _generate_checkup_no(db: Session, tenant_no: str) -> str:
- today_str = datetime.now().strftime("%Y%m%d")
- prefix = f"ZSTJ{today_str}"
- existing = (
- db.query(BizPilotCheckup.checkup_status)
- .filter(
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.id.like(f"{prefix}%"),
- BizPilotCheckup.del_flag == 0,
- )
- .order_by(BizPilotCheckup.create_time.desc())
- .first()
- )
- seq = 1
- if existing:
- seq = 1
- return f"{prefix}{seq:04d}"
- def _row_to_dict_checkup(row, db=None, tenant_no=None) -> Dict[str, Any]:
- d: Dict[str, Any] = {
- "id": row.id,
- "pilotApplicationId": row.pilot_application_id or "",
- "pilotPatientId": row.pilot_patient_id or "",
- "checkupInstitutionId": row.checkup_institution_id or "",
- "reserveTime": row.reserve_time.strftime("%Y-%m-%d %H:%M:%S") if row.reserve_time else "",
- "reserveFee": float(row.reserve_fee) if row.reserve_fee is not None else None,
- "actualCheckupTime": row.actual_checkup_time.strftime("%Y-%m-%d %H:%M:%S") if row.actual_checkup_time else "",
- "actualCheckupFee": float(row.actual_checkup_fee) if row.actual_checkup_fee is not None else None,
- "feeSettled": row.fee_settled if row.fee_settled is not None else 0,
- "checkupStatus": row.checkup_status or "",
- "feeStatus": row.fee_status or "",
- "checkupRemark": row.checkup_remark or "",
- "reportAttachments": row.report_attachments or "",
- "tenantNo": row.tenant_no or "",
- "remark": row.remark or "",
- "createBy": row.create_by or "",
- "createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
- "updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
- }
- if db and tenant_no:
- # institutionName — lookup from BizCheckupInstitution
- if row.checkup_institution_id:
- inst = (
- db.query(BizCheckupInstitution.institution_name)
- .filter(
- BizCheckupInstitution.id == row.checkup_institution_id,
- BizCheckupInstitution.del_flag == 0,
- )
- .first()
- )
- d["institutionName"] = inst.institution_name if inst else ""
- else:
- d["institutionName"] = ""
- # patientName / sex / age / phoneNo — from BizPilotPatient
- if row.pilot_patient_id:
- patient = (
- db.query(BizPilotPatient)
- .filter(
- BizPilotPatient.id == row.pilot_patient_id,
- BizPilotPatient.del_flag == 0,
- )
- .first()
- )
- if patient:
- d["patientName"] = patient.patient_name or ""
- d["sex"] = patient.sex or ""
- d["age"] = patient.age
- d["phoneNo"] = patient.phone_no or ""
- else:
- d["patientName"] = ""
- d["sex"] = ""
- d["age"] = None
- d["phoneNo"] = ""
- else:
- d["patientName"] = ""
- d["sex"] = ""
- d["age"] = None
- d["phoneNo"] = ""
- # applicationNo / channelName — from BizPilotApplication
- if row.pilot_application_id:
- app = (
- db.query(BizPilotApplication)
- .filter(
- BizPilotApplication.id == row.pilot_application_id,
- BizPilotApplication.del_flag == 0,
- )
- .first()
- )
- if app:
- d["applicationNo"] = app.application_no or ""
- d["channelName"] = app.channel_name or ""
- else:
- d["applicationNo"] = ""
- d["channelName"] = ""
- else:
- d["applicationNo"] = ""
- d["channelName"] = ""
- # reserveItemCount
- reserve_count = (
- db.query(func.count(BizPilotCheckupReserveItem.id))
- .filter(
- BizPilotCheckupReserveItem.pilot_checkup_id == row.id,
- BizPilotCheckupReserveItem.tenant_no == tenant_no,
- BizPilotCheckupReserveItem.del_flag == 0,
- )
- .scalar()
- ) or 0
- d["reserveItemCount"] = reserve_count
- # actualItemCount
- actual_count = (
- db.query(func.count(BizPilotCheckupActualItem.id))
- .filter(
- BizPilotCheckupActualItem.pilot_checkup_id == row.id,
- BizPilotCheckupActualItem.tenant_no == tenant_no,
- BizPilotCheckupActualItem.del_flag == 0,
- )
- .scalar()
- ) or 0
- d["actualItemCount"] = actual_count
- return d
- def _row_to_dict_reserve_item(row) -> Dict[str, Any]:
- return {
- "id": row.id,
- "pilotCheckupId": row.pilot_checkup_id or "",
- "projectName": row.project_name or "",
- "projectFee": float(row.project_fee) if row.project_fee is not None else None,
- "orderNum": row.order_num if row.order_num is not None else 0,
- "remark": row.remark or "",
- "createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
- "updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
- }
- def _row_to_dict_actual_item(row) -> Dict[str, Any]:
- return {
- "id": row.id,
- "pilotCheckupId": row.pilot_checkup_id or "",
- "projectName": row.project_name or "",
- "projectFee": float(row.project_fee) if row.project_fee is not None else None,
- "orderNum": row.order_num if row.order_num is not None else 0,
- "remark": row.remark or "",
- "createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
- "updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
- }
- # ────────────────────────────────────────────
- # Checkup CRUD
- # ────────────────────────────────────────────
- def get_checkup_by_application(
- db: Session, application_id: str, tenant_no: str
- ) -> Optional[Dict[str, Any]]:
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.pilot_application_id == application_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- return None
- return _row_to_dict_checkup(row, db, tenant_no)
- def get_checkup_by_id(
- db: Session, checkup_id: str, tenant_no: str
- ) -> Optional[Dict[str, Any]]:
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.id == checkup_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- return None
- return _row_to_dict_checkup(row, db, tenant_no)
- def create_checkup(
- db: Session,
- application_id: str,
- data: dict,
- tenant_no: str,
- user_id: str,
- ) -> Dict[str, Any]:
- now = datetime.now()
- row = BizPilotCheckup(
- id=uuid.uuid4().hex,
- pilot_application_id=application_id,
- pilot_patient_id=data.get("pilotPatientId") or None,
- checkup_institution_id=data.get("checkupInstitutionId") or "",
- reserve_time=_parse_optional_datetime(data.get("reserveTime")),
- reserve_fee=data.get("reserveFee") if data.get("reserveFee") is not None else None,
- actual_checkup_time=data.get("actualCheckupTime") or None,
- actual_checkup_fee=data.get("actualCheckupFee") if data.get("actualCheckupFee") is not None else None,
- fee_settled=0,
- checkup_status="dtj",
- fee_status="wjs",
- checkup_remark=data.get("checkupRemark") or "",
- report_attachments=data.get("reportAttachments") or "",
- tenant_no=tenant_no,
- order_num=0,
- remark=data.get("remark") or "",
- create_by=user_id,
- create_dept_id="",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(row)
- db.commit()
- db.refresh(row)
- return _row_to_dict_checkup(row, db, tenant_no)
- def update_checkup(
- db: Session, checkup_id: str, data: dict, tenant_no: str
- ) -> Dict[str, Any]:
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.id == checkup_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise ValueError("体检信息不存在")
- fields = {
- "checkupInstitutionId": "checkup_institution_id",
- "reserveTime": "reserve_time",
- "reserveFee": "reserve_fee",
- "actualCheckupTime": "actual_checkup_time",
- "actualCheckupFee": "actual_checkup_fee",
- "checkupRemark": "checkup_remark",
- "reportAttachments": "report_attachments",
- "remark": "remark",
- }
- for camel, snake in fields.items():
- if camel in data and data[camel] is not None:
- val = data[camel]
- if snake in ("reserve_time", "actual_checkup_time"):
- val = _parse_optional_datetime(val)
- setattr(row, snake, val)
- row.update_time = datetime.now()
- db.commit()
- db.refresh(row)
- return _row_to_dict_checkup(row, db, tenant_no)
- def soft_delete_checkup(db: Session, checkup_id: str, tenant_no: str) -> None:
- now = datetime.now()
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.id == checkup_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise ValueError("体检信息不存在")
- row.del_flag = 1
- row.update_time = now
- # Cascade soft-delete to reserve items
- db.query(BizPilotCheckupReserveItem).filter(
- BizPilotCheckupReserveItem.pilot_checkup_id == checkup_id,
- BizPilotCheckupReserveItem.tenant_no == tenant_no,
- BizPilotCheckupReserveItem.del_flag == 0,
- ).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
- # Cascade soft-delete to actual items
- db.query(BizPilotCheckupActualItem).filter(
- BizPilotCheckupActualItem.pilot_checkup_id == checkup_id,
- BizPilotCheckupActualItem.tenant_no == tenant_no,
- BizPilotCheckupActualItem.del_flag == 0,
- ).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
- db.commit()
- def mark_checkup_checked(
- db: Session, checkup_id: str, tenant_no: str, user_id: str
- ) -> Dict[str, Any]:
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.id == checkup_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise ValueError("体检信息不存在")
- if row.checkup_status != "dtj":
- raise ValueError("当前状态不允许标记已体检")
- row.checkup_status = "ytj"
- if not row.actual_checkup_time:
- row.actual_checkup_time = datetime.now()
- row.update_by = user_id
- row.update_time = datetime.now()
- db.commit()
- db.refresh(row)
- return _row_to_dict_checkup(row, db, tenant_no)
- def settle_checkup_fee(
- db: Session, checkup_id: str, tenant_no: str, user_id: str
- ) -> Dict[str, Any]:
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.id == checkup_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise ValueError("体检信息不存在")
- if row.fee_status != "wjs":
- raise ValueError("当前状态不允许结算")
- row.fee_status = "yjs"
- row.fee_settled = 1
- row.update_by = user_id
- row.update_time = datetime.now()
- db.commit()
- db.refresh(row)
- return _row_to_dict_checkup(row, db, tenant_no)
- # ────────────────────────────────────────────
- # Reserve Items CRUD
- # ────────────────────────────────────────────
- def list_reserve_items(
- db: Session, checkup_id: str, tenant_no: str
- ) -> List[Dict[str, Any]]:
- rows = (
- db.query(BizPilotCheckupReserveItem)
- .filter(
- BizPilotCheckupReserveItem.pilot_checkup_id == checkup_id,
- BizPilotCheckupReserveItem.tenant_no == tenant_no,
- BizPilotCheckupReserveItem.del_flag == 0,
- )
- .order_by(
- BizPilotCheckupReserveItem.order_num.asc(),
- BizPilotCheckupReserveItem.create_time.asc(),
- )
- .all()
- )
- return [_row_to_dict_reserve_item(r) for r in rows]
- def save_reserve_items(
- db: Session,
- checkup_id: str,
- items_json: list,
- tenant_no: str,
- user_id: str,
- ) -> List[Dict[str, Any]]:
- now = datetime.now()
- # Soft-delete existing items
- db.query(BizPilotCheckupReserveItem).filter(
- BizPilotCheckupReserveItem.pilot_checkup_id == checkup_id,
- BizPilotCheckupReserveItem.tenant_no == tenant_no,
- BizPilotCheckupReserveItem.del_flag == 0,
- ).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
- # Insert new items
- for idx, item in enumerate(items_json):
- row = BizPilotCheckupReserveItem(
- id=uuid.uuid4().hex,
- pilot_checkup_id=checkup_id,
- project_name=item.get("projectName") or item.get("project_name") or "",
- project_fee=item.get("projectFee") if item.get("projectFee") is not None else (
- item.get("project_fee") if item.get("project_fee") is not None else None
- ),
- tenant_no=tenant_no,
- order_num=idx + 1,
- remark=item.get("remark") or "",
- create_by=user_id,
- create_dept_id="",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(row)
- db.commit()
- return list_reserve_items(db, checkup_id, tenant_no)
- def delete_reserve_item(db: Session, item_id: str, tenant_no: str) -> None:
- row = (
- db.query(BizPilotCheckupReserveItem)
- .filter(
- BizPilotCheckupReserveItem.id == item_id,
- BizPilotCheckupReserveItem.tenant_no == tenant_no,
- BizPilotCheckupReserveItem.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise ValueError("预约项目不存在")
- row.del_flag = 1
- row.update_time = datetime.now()
- db.commit()
- # ────────────────────────────────────────────
- # Actual Items CRUD
- # ────────────────────────────────────────────
- def list_actual_items(
- db: Session, checkup_id: str, tenant_no: str
- ) -> List[Dict[str, Any]]:
- rows = (
- db.query(BizPilotCheckupActualItem)
- .filter(
- BizPilotCheckupActualItem.pilot_checkup_id == checkup_id,
- BizPilotCheckupActualItem.tenant_no == tenant_no,
- BizPilotCheckupActualItem.del_flag == 0,
- )
- .order_by(
- BizPilotCheckupActualItem.order_num.asc(),
- BizPilotCheckupActualItem.create_time.asc(),
- )
- .all()
- )
- return [_row_to_dict_actual_item(r) for r in rows]
- def save_actual_items(
- db: Session,
- checkup_id: str,
- items_json: list,
- tenant_no: str,
- user_id: str,
- ) -> List[Dict[str, Any]]:
- now = datetime.now()
- # Soft-delete existing items
- db.query(BizPilotCheckupActualItem).filter(
- BizPilotCheckupActualItem.pilot_checkup_id == checkup_id,
- BizPilotCheckupActualItem.tenant_no == tenant_no,
- BizPilotCheckupActualItem.del_flag == 0,
- ).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
- # Insert new items
- for idx, item in enumerate(items_json):
- row = BizPilotCheckupActualItem(
- id=uuid.uuid4().hex,
- pilot_checkup_id=checkup_id,
- project_name=item.get("projectName") or item.get("project_name") or "",
- project_fee=item.get("projectFee") if item.get("projectFee") is not None else (
- item.get("project_fee") if item.get("project_fee") is not None else None
- ),
- tenant_no=tenant_no,
- order_num=idx + 1,
- remark=item.get("remark") or "",
- create_by=user_id,
- create_dept_id="",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(row)
- db.commit()
- return list_actual_items(db, checkup_id, tenant_no)
- def delete_actual_item(db: Session, item_id: str, tenant_no: str) -> None:
- row = (
- db.query(BizPilotCheckupActualItem)
- .filter(
- BizPilotCheckupActualItem.id == item_id,
- BizPilotCheckupActualItem.tenant_no == tenant_no,
- BizPilotCheckupActualItem.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise ValueError("实际项目不存在")
- row.del_flag = 1
- row.update_time = datetime.now()
- db.commit()
- # ────────────────────────────────────────────
- # Checkup List (Paginated)
- # ────────────────────────────────────────────
- def list_checkups_paginated(
- db: Session,
- tenant_no: str,
- page: int = 1,
- page_size: int = 10,
- checkup_status: Optional[str] = None,
- fee_status: Optional[str] = None,
- checkup_institution_id: Optional[str] = None,
- keyword: Optional[str] = None,
- application_no: Optional[str] = None,
- patient_name: Optional[str] = None,
- channel_id: Optional[str] = None,
- biz_type: Optional[str] = None,
- ) -> Dict[str, Any]:
- q = db.query(BizPilotCheckup).filter(
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- if biz_type:
- app_subq = (
- db.query(BizPilotApplication.id)
- .filter(
- BizPilotApplication.biz_type == biz_type,
- BizPilotApplication.del_flag == 0,
- )
- .subquery()
- )
- q = q.filter(BizPilotCheckup.pilot_application_id.in_(app_subq))
- if checkup_status:
- statuses = [s.strip() for s in checkup_status.split(",") if s.strip()]
- if statuses:
- q = q.filter(BizPilotCheckup.checkup_status.in_(statuses))
- if fee_status:
- statuses = [s.strip() for s in fee_status.split(",") if s.strip()]
- if statuses:
- q = q.filter(BizPilotCheckup.fee_status.in_(statuses))
- if checkup_institution_id:
- q = q.filter(BizPilotCheckup.checkup_institution_id == checkup_institution_id)
- # keyword: fuzzy search across application_no, patient_name, channel_name
- if keyword:
- kw = f"%{keyword}%"
- app_ids_by_no = (
- db.query(BizPilotApplication.id)
- .filter(
- BizPilotApplication.application_no.like(kw),
- BizPilotApplication.del_flag == 0,
- )
- .subquery()
- )
- app_ids_by_channel = (
- db.query(BizPilotApplication.id)
- .filter(
- BizPilotApplication.channel_name.like(kw),
- BizPilotApplication.del_flag == 0,
- )
- .subquery()
- )
- patient_ids_by_name = (
- db.query(BizPilotPatient.id)
- .filter(
- BizPilotPatient.patient_name.like(kw),
- BizPilotPatient.del_flag == 0,
- )
- .subquery()
- )
- q = q.filter(
- or_(
- BizPilotCheckup.pilot_application_id.in_(app_ids_by_no),
- BizPilotCheckup.pilot_application_id.in_(app_ids_by_channel),
- BizPilotCheckup.pilot_patient_id.in_(patient_ids_by_name),
- )
- )
- if application_no:
- app_subq = (
- db.query(BizPilotApplication.id)
- .filter(
- BizPilotApplication.application_no.like(f"%{application_no}%"),
- BizPilotApplication.del_flag == 0,
- )
- .subquery()
- )
- q = q.filter(BizPilotCheckup.pilot_application_id.in_(app_subq))
- if patient_name:
- patient_subq = (
- db.query(BizPilotPatient.id)
- .filter(
- BizPilotPatient.patient_name.like(f"%{patient_name}%"),
- BizPilotPatient.del_flag == 0,
- )
- .subquery()
- )
- q = q.filter(BizPilotCheckup.pilot_patient_id.in_(patient_subq))
- if channel_id:
- app_subq = (
- db.query(BizPilotApplication.id)
- .filter(
- BizPilotApplication.channel_id == channel_id,
- BizPilotApplication.del_flag == 0,
- )
- .subquery()
- )
- q = q.filter(BizPilotCheckup.pilot_application_id.in_(app_subq))
- total = q.order_by(None).with_entities(func.count(BizPilotCheckup.id)).scalar() or 0
- page = max(1, page)
- page_size = min(max(1, page_size), 100)
- rows = (
- q.order_by(BizPilotCheckup.create_time.desc())
- .offset((page - 1) * page_size)
- .limit(page_size)
- .all()
- )
- items = [_row_to_dict_checkup(r, db, tenant_no) for r in rows]
- return {
- "list": items,
- "pagination": {"page": page, "pageSize": page_size, "total": total},
- }
- def list_pending_patients(
- db: Session,
- tenant_no: str,
- page: int = 1,
- page_size: int = 10,
- application_no: Optional[str] = None,
- patient_name: Optional[str] = None,
- channel_id: Optional[str] = None,
- ) -> Dict[str, Any]:
- """查询 need_checkup=1 但尚未有 biz_pilot_checkup 记录的患者"""
- checked_patient_ids = (
- db.query(BizPilotCheckup.pilot_patient_id)
- .filter(
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- BizPilotCheckup.pilot_patient_id.isnot(None),
- BizPilotCheckup.pilot_patient_id != "",
- )
- .subquery()
- )
- q = db.query(BizPilotPatient).filter(
- BizPilotPatient.need_checkup == 1,
- BizPilotPatient.tenant_no == tenant_no,
- BizPilotPatient.del_flag == 0,
- ~BizPilotPatient.id.in_(checked_patient_ids),
- )
- if patient_name:
- q = q.filter(BizPilotPatient.patient_name.like(f"%{patient_name}%"))
- if application_no or channel_id:
- app_q = db.query(BizPilotApplication.id).filter(
- BizPilotApplication.tenant_no == tenant_no,
- BizPilotApplication.del_flag == 0,
- )
- if application_no:
- app_q = app_q.filter(
- BizPilotApplication.application_no.like(f"%{application_no}%")
- )
- if channel_id:
- app_q = app_q.filter(BizPilotApplication.channel_id == channel_id)
- app_subq = app_q.subquery()
- q = q.filter(BizPilotPatient.pilot_application_id.in_(app_subq))
- total = q.order_by(None).with_entities(func.count(BizPilotPatient.id)).scalar() or 0
- page = max(1, page)
- page_size = min(max(1, page_size), 100)
- rows = (
- q.order_by(BizPilotPatient.create_time.desc())
- .offset((page - 1) * page_size)
- .limit(page_size)
- .all()
- )
- items = []
- for r in rows:
- d = {
- "id": r.id,
- "pilotApplicationId": r.pilot_application_id or "",
- "patientName": r.patient_name or "",
- "sex": r.sex or "",
- "age": r.age,
- "phoneNo": r.phone_no or "",
- }
- app = (
- db.query(BizPilotApplication)
- .filter(
- BizPilotApplication.id == r.pilot_application_id,
- BizPilotApplication.del_flag == 0,
- )
- .first()
- )
- if app:
- d["applicationNo"] = app.application_no or ""
- d["channelName"] = app.channel_name or ""
- d["channelId"] = app.channel_id or ""
- else:
- d["applicationNo"] = ""
- d["channelName"] = ""
- d["channelId"] = ""
- items.append(d)
- return {
- "list": items,
- "pagination": {"page": page, "pageSize": page_size, "total": total},
- }
- def batch_create_checkups(
- db: Session,
- patient_ids: list,
- checkup_institution_id: str,
- reserve_time: Optional[str],
- reserve_items: list,
- tenant_no: str,
- user_id: str,
- ) -> list:
- """为多个患者批量创建体检记录 + 预约项目"""
- results = []
- now = datetime.now()
- for patient_id in patient_ids:
- patient = (
- db.query(BizPilotPatient)
- .filter(
- BizPilotPatient.id == patient_id,
- BizPilotPatient.tenant_no == tenant_no,
- BizPilotPatient.del_flag == 0,
- )
- .first()
- )
- if not patient:
- continue
- checkup_row = BizPilotCheckup(
- id=uuid.uuid4().hex,
- pilot_application_id=patient.pilot_application_id,
- pilot_patient_id=patient_id,
- checkup_institution_id=checkup_institution_id or "",
- reserve_time=_parse_optional_datetime(reserve_time),
- reserve_fee=sum(
- float(item.get("projectFee") or 0) for item in reserve_items
- ) if reserve_items else None,
- actual_checkup_time=None,
- actual_checkup_fee=None,
- fee_settled=0,
- checkup_status="dtj",
- fee_status="wjs",
- checkup_remark="",
- report_attachments="",
- tenant_no=tenant_no,
- order_num=0,
- remark="",
- create_by=user_id,
- create_dept_id="",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(checkup_row)
- db.flush()
- for idx, item in enumerate(reserve_items):
- ri = BizPilotCheckupReserveItem(
- id=uuid.uuid4().hex,
- pilot_checkup_id=checkup_row.id,
- project_name=item.get("projectName") or item.get("project_name") or "",
- project_fee=item.get("projectFee") if item.get("projectFee") is not None else (
- item.get("project_fee") if item.get("project_fee") is not None else None
- ),
- tenant_no=tenant_no,
- order_num=idx + 1,
- remark="",
- create_by=user_id,
- create_dept_id="",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(ri)
- results.append(_row_to_dict_checkup(checkup_row, db, tenant_no))
- db.commit()
- return results
- def ensure_checkup_for_patient(
- db: Session,
- patient_id: str,
- application_id: str,
- tenant_no: str,
- user_id: str,
- ) -> Optional[str]:
- """为患者创建体检记录(如不存在)。返回 checkup_id 或 None。"""
- existing = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.pilot_patient_id == patient_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if existing:
- return existing.id
- now = datetime.now()
- row = BizPilotCheckup(
- id=uuid.uuid4().hex,
- pilot_application_id=application_id,
- pilot_patient_id=patient_id,
- checkup_institution_id="",
- checkup_status="dtj",
- fee_status="wjs",
- fee_settled=0,
- checkup_remark="",
- report_attachments="",
- tenant_no=tenant_no,
- order_num=0,
- remark="",
- create_by=user_id,
- create_dept_id="",
- create_time=now,
- update_by=user_id,
- update_time=now,
- del_flag=0,
- )
- db.add(row)
- db.flush()
- return row.id
- def remove_checkup_for_patient(
- db: Session,
- patient_id: str,
- tenant_no: str,
- ) -> None:
- """软删除患者的体检记录(仅在初始状态,无机构数据时才删除)。"""
- row = (
- db.query(BizPilotCheckup)
- .filter(
- BizPilotCheckup.pilot_patient_id == patient_id,
- BizPilotCheckup.tenant_no == tenant_no,
- BizPilotCheckup.del_flag == 0,
- )
- .first()
- )
- if not row:
- return
- if row.checkup_institution_id:
- return
- now = datetime.now()
- row.del_flag = 1
- row.update_time = now
- db.query(BizPilotCheckupReserveItem).filter(
- BizPilotCheckupReserveItem.pilot_checkup_id == row.id,
- BizPilotCheckupReserveItem.tenant_no == tenant_no,
- BizPilotCheckupReserveItem.del_flag == 0,
- ).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
- db.query(BizPilotCheckupActualItem).filter(
- BizPilotCheckupActualItem.pilot_checkup_id == row.id,
- BizPilotCheckupActualItem.tenant_no == tenant_no,
- BizPilotCheckupActualItem.del_flag == 0,
- ).update({"del_flag": 1, "update_time": now}, synchronize_session=False)
|