pilot_infusion_schedule_mgmt.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. """中试输注计划管理 API"""
  2. from typing import List, Optional
  3. from fastapi import APIRouter, Depends, Query
  4. from pydantic import AliasChoices, BaseModel, ConfigDict, Field
  5. from sqlalchemy.orm import Session
  6. from app.core.database import get_db
  7. from app.core.menu_guard import require_admin_or_super_admin
  8. from app.core.response import error_response, format_api_error_message, success_response
  9. from app.crud import pilot_infusion_schedule_mgmt as crud
  10. from app.crud import outbound_handover as handover_crud
  11. from app.models.models import SysUser
  12. from app.schemas.outbound_handover import OutboundHandoverSaveBody
  13. router = APIRouter(prefix="/web/admin", tags=["中试输注计划管理"])
  14. def _get_tenant_no(db: Session, user: dict) -> str:
  15. user_id = user.get("user_id")
  16. if not user_id:
  17. raise PermissionError("用户身份信息缺失")
  18. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  19. if not sys_user or not sys_user.tenant_no:
  20. raise PermissionError("用户租户信息缺失")
  21. return sys_user.tenant_no
  22. # ---------------------------------------------------------------------------
  23. # Pydantic Schemas
  24. # ---------------------------------------------------------------------------
  25. class UpdateStatusBody(BaseModel):
  26. model_config = ConfigDict(populate_by_name=True)
  27. schedule_id: str = Field(..., validation_alias=AliasChoices("scheduleId", "schedule_id"))
  28. infusion_status: str = Field(..., validation_alias=AliasChoices("infusionStatus", "infusion_status"))
  29. cancel_reason: Optional[str] = Field(
  30. None, validation_alias=AliasChoices("cancelReason", "cancel_reason")
  31. )
  32. class UpdateQcStatusBody(BaseModel):
  33. model_config = ConfigDict(populate_by_name=True)
  34. schedule_id: str = Field(..., validation_alias=AliasChoices("scheduleId", "schedule_id"))
  35. qc_status: str = Field(..., validation_alias=AliasChoices("qcStatus", "qc_status"))
  36. class ProductItemBody(BaseModel):
  37. model_config = ConfigDict(populate_by_name=True)
  38. price_id: Optional[str] = Field(None, validation_alias=AliasChoices("priceId", "price_id"))
  39. project_code: Optional[str] = Field(None, validation_alias=AliasChoices("projectCode", "project_code"))
  40. project_name: Optional[str] = Field(None, validation_alias=AliasChoices("projectName", "project_name"))
  41. cell_type_name: Optional[str] = Field(
  42. None, validation_alias=AliasChoices("cellTypeName", "cell_type_name")
  43. )
  44. spec_text: Optional[str] = Field(None, validation_alias=AliasChoices("specText", "spec_text"))
  45. agreement_price: Optional[float] = Field(
  46. None, validation_alias=AliasChoices("agreementPrice", "agreement_price")
  47. )
  48. actual_price: Optional[float] = Field(
  49. None, validation_alias=AliasChoices("actualPrice", "actual_price")
  50. )
  51. quantity: Optional[int] = Field(None, validation_alias=AliasChoices("quantity", "quantity"))
  52. remark: Optional[str] = Field(None, validation_alias=AliasChoices("remark", "remark"))
  53. class UpdateScheduleBody(BaseModel):
  54. model_config = ConfigDict(populate_by_name=True)
  55. id: str = Field(...)
  56. cell_type: Optional[str] = Field(None, validation_alias=AliasChoices("cellType", "cell_type"))
  57. spec_text: Optional[str] = Field(None, validation_alias=AliasChoices("specText", "spec_text"))
  58. infusion_time: Optional[str] = Field(
  59. None, validation_alias=AliasChoices("infusionTime", "infusion_time")
  60. )
  61. infusion_location: Optional[str] = Field(
  62. None, validation_alias=AliasChoices("infusionLocation", "infusion_location")
  63. )
  64. infusion_method: Optional[str] = Field(
  65. None, validation_alias=AliasChoices("infusionMethod", "infusion_method")
  66. )
  67. products: Optional[List[ProductItemBody]] = None
  68. # ---------------------------------------------------------------------------
  69. # Endpoints
  70. # ---------------------------------------------------------------------------
  71. @router.get("/pilot-infusion-schedule-mgmt/list")
  72. async def api_list(
  73. page: int = Query(1, ge=1),
  74. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  75. keyword: Optional[str] = Query(None, alias="keyword"),
  76. infusionStatus: Optional[str] = Query(None, alias="infusionStatus"),
  77. qcStatus: Optional[str] = Query(None, alias="qcStatus"),
  78. infusionTimeStart: Optional[str] = Query(None, alias="infusionTimeStart"),
  79. infusionTimeEnd: Optional[str] = Query(None, alias="infusionTimeEnd"),
  80. bizType: Optional[str] = Query(None, alias="bizType"),
  81. user: dict = Depends(require_admin_or_super_admin),
  82. db: Session = Depends(get_db),
  83. ):
  84. tenant_no = _get_tenant_no(db, user)
  85. result = crud.list_infusion_schedules_mgmt(
  86. db,
  87. tenant_no=tenant_no,
  88. page=page,
  89. page_size=pageSize,
  90. keyword=keyword,
  91. infusion_status=infusionStatus,
  92. qc_status=qcStatus,
  93. infusion_time_start=infusionTimeStart,
  94. infusion_time_end=infusionTimeEnd,
  95. biz_type=bizType,
  96. )
  97. return success_response(result)
  98. @router.get("/pilot-infusion-schedule-mgmt/detail")
  99. async def api_detail(
  100. scheduleId: str = Query(..., alias="scheduleId"),
  101. user: dict = Depends(require_admin_or_super_admin),
  102. db: Session = Depends(get_db),
  103. ):
  104. tenant_no = _get_tenant_no(db, user)
  105. result = crud.get_infusion_schedule_detail(
  106. db, schedule_id=scheduleId, tenant_no=tenant_no
  107. )
  108. if result is None:
  109. return success_response(None)
  110. return success_response(result)
  111. @router.put("/pilot-infusion-schedule-mgmt/update-status")
  112. async def api_update_status(
  113. body: UpdateStatusBody,
  114. user: dict = Depends(require_admin_or_super_admin),
  115. db: Session = Depends(get_db),
  116. ):
  117. try:
  118. tenant_no = _get_tenant_no(db, user)
  119. result = crud.update_infusion_status(
  120. db,
  121. schedule_id=body.schedule_id,
  122. infusion_status=body.infusion_status,
  123. cancel_reason=body.cancel_reason,
  124. tenant_no=tenant_no,
  125. user_id=user.get("user_id"),
  126. )
  127. return success_response(result, message="状态更新成功")
  128. except ValueError as e:
  129. return error_response(400, str(e))
  130. @router.put("/pilot-infusion-schedule-mgmt/update-qc-status")
  131. async def api_update_qc_status(
  132. body: UpdateQcStatusBody,
  133. user: dict = Depends(require_admin_or_super_admin),
  134. db: Session = Depends(get_db),
  135. ):
  136. try:
  137. tenant_no = _get_tenant_no(db, user)
  138. result = crud.update_qc_status(
  139. db,
  140. schedule_id=body.schedule_id,
  141. qc_status=body.qc_status,
  142. tenant_no=tenant_no,
  143. user_id=user.get("user_id"),
  144. )
  145. return success_response(result, message="质检状态更新成功")
  146. except ValueError as e:
  147. return error_response(400, str(e))
  148. @router.put("/pilot-infusion-schedule-mgmt/update")
  149. async def api_update(
  150. body: UpdateScheduleBody,
  151. user: dict = Depends(require_admin_or_super_admin),
  152. db: Session = Depends(get_db),
  153. ):
  154. try:
  155. tenant_no = _get_tenant_no(db, user)
  156. data = {
  157. "cellType": body.cell_type,
  158. "specText": body.spec_text,
  159. "infusionTime": body.infusion_time,
  160. "infusionLocation": body.infusion_location,
  161. "infusionMethod": body.infusion_method,
  162. }
  163. if body.products is not None:
  164. data["products"] = [p.model_dump(by_alias=False) for p in body.products]
  165. result = crud.update_infusion_schedule_mgmt(
  166. db,
  167. schedule_id=body.id,
  168. data=data,
  169. tenant_no=tenant_no,
  170. user_id=user.get("user_id"),
  171. )
  172. return success_response(result, message="更新成功")
  173. except ValueError as e:
  174. return error_response(400, str(e))
  175. @router.get("/pilot-infusion-schedule-mgmt/{schedule_id}/outbound-handover")
  176. async def api_get_outbound_handover(
  177. schedule_id: str,
  178. user: dict = Depends(require_admin_or_super_admin),
  179. db: Session = Depends(get_db),
  180. ):
  181. try:
  182. tenant_no = _get_tenant_no(db, user)
  183. result = handover_crud.get_outbound_handover(db, schedule_id, tenant_no)
  184. return success_response(result)
  185. except PermissionError as e:
  186. return error_response(403, str(e))
  187. except Exception as e:
  188. if hasattr(e, "status_code") and getattr(e, "status_code") == 404:
  189. return error_response(404, format_api_error_message(e, fallback="资源不存在"))
  190. return error_response(400, format_api_error_message(e))
  191. @router.put("/pilot-infusion-schedule-mgmt/{schedule_id}/outbound-handover")
  192. async def api_save_outbound_handover(
  193. schedule_id: str,
  194. body: OutboundHandoverSaveBody,
  195. user: dict = Depends(require_admin_or_super_admin),
  196. db: Session = Depends(get_db),
  197. ):
  198. try:
  199. tenant_no = _get_tenant_no(db, user)
  200. user_id = user.get("user_id") or ""
  201. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  202. dept_id = (sys_user.dept_id or "") if sys_user else ""
  203. om = body.outboundMaterial
  204. result = handover_crud.save_outbound_handover(
  205. db,
  206. schedule_id,
  207. outbound_material={
  208. "remark": om.remark,
  209. "cells": [c.model_dump(by_alias=True) for c in om.cells],
  210. "qcMaterials": [m.model_dump(by_alias=True) for m in om.qcMaterials],
  211. "paperMaterials": [p.model_dump(by_alias=True) for p in om.paperMaterials],
  212. },
  213. handover=body.handover.model_dump(by_alias=True),
  214. tenant_no=tenant_no,
  215. user_id=user_id,
  216. create_dept_id=dept_id,
  217. )
  218. db.commit()
  219. return success_response(result, message="出库交接已保存")
  220. except PermissionError as e:
  221. db.rollback()
  222. return error_response(403, str(e))
  223. except Exception as e:
  224. db.rollback()
  225. if hasattr(e, "status_code"):
  226. code = int(getattr(e, "status_code", 400))
  227. return error_response(code, format_api_error_message(e))
  228. return error_response(400, format_api_error_message(e, fallback="出库交接保存失败"))