operations.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. """运营支撑 API(洁净室、进出记录、仓储设施、储位、展厅预约)"""
  2. from datetime import datetime
  3. from typing import Any, Optional
  4. from fastapi import APIRouter, Depends, Query
  5. from pydantic import BaseModel, ConfigDict, Field
  6. from sqlalchemy.orm import Session
  7. from app.core.data_scope import apply_data_scope
  8. from app.core.database import get_db
  9. from app.core.menu_guard import require_admin_or_super_admin
  10. from app.core.response import error_response, success_response
  11. from app.crud import clean_room as clean_room_svc
  12. from app.crud import exhibition_appointment as exhibition_svc
  13. from app.crud import storage_facility as storage_facility_svc
  14. from app.models.models import BizCleanRoom, BizCleanRoomAccessLog, BizExhibitionAppointment, BizStorageFacility, BizStorageFacilitySlot, SysUser
  15. router = APIRouter(prefix="/web/admin", tags=["运营支撑"])
  16. def _get_tenant_no(db: Session, user: dict) -> str:
  17. user_id = user.get("user_id")
  18. if not user_id:
  19. raise PermissionError("用户身份信息缺失,无法确定租户")
  20. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  21. if not sys_user or not sys_user.tenant_no:
  22. raise PermissionError("用户租户信息缺失")
  23. return sys_user.tenant_no
  24. # ---------------------------------------------------------------------------
  25. # 洁净室
  26. # ---------------------------------------------------------------------------
  27. class CleanRoomCreateBody(BaseModel):
  28. model_config = ConfigDict(populate_by_name=True)
  29. room_code: str = Field(..., alias="roomCode")
  30. room_name: str = Field(..., alias="roomName")
  31. room_grade: Optional[str] = Field(None, alias="roomGrade")
  32. room_status: Optional[str] = Field("zc", alias="roomStatus")
  33. area_sqm: Optional[float] = Field(None, alias="areaSqm")
  34. order_num: Optional[int] = Field(0, alias="orderNum")
  35. remark: Optional[str] = None
  36. class CleanRoomUpdateBody(BaseModel):
  37. model_config = ConfigDict(populate_by_name=True)
  38. room_code: Optional[str] = Field(None, alias="roomCode")
  39. room_name: Optional[str] = Field(None, alias="roomName")
  40. room_grade: Optional[str] = Field(None, alias="roomGrade")
  41. room_status: Optional[str] = Field(None, alias="roomStatus")
  42. area_sqm: Optional[float] = Field(None, alias="areaSqm")
  43. order_num: Optional[int] = Field(None, alias="orderNum")
  44. remark: Optional[str] = None
  45. @router.get("/clean-rooms")
  46. async def list_clean_rooms(
  47. page: int = Query(1, ge=1),
  48. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  49. keyword: Optional[str] = None,
  50. user: dict = Depends(require_admin_or_super_admin),
  51. db: Session = Depends(get_db),
  52. ):
  53. tenant_no = _get_tenant_no(db, user)
  54. query = db.query(BizCleanRoom).filter(
  55. BizCleanRoom.del_flag == 0,
  56. BizCleanRoom.tenant_no == tenant_no,
  57. )
  58. if keyword:
  59. query = query.filter(BizCleanRoom.room_name.like(f"%{keyword}%"))
  60. query = apply_data_scope(query, BizCleanRoom, user, db, tenant_no)
  61. total = query.count()
  62. items = query.order_by(BizCleanRoom.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
  63. result_list = [clean_room_svc._row_to_dict(r) for r in items]
  64. tp = clean_room_svc.total_pages(total, pageSize)
  65. return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
  66. @router.post("/clean-rooms")
  67. async def create_clean_room(
  68. body: CleanRoomCreateBody,
  69. user: dict = Depends(require_admin_or_super_admin),
  70. db: Session = Depends(get_db),
  71. ):
  72. try:
  73. tenant_no = _get_tenant_no(db, user)
  74. obj = clean_room_svc.create_clean_room(
  75. db,
  76. room_code=body.room_code,
  77. room_name=body.room_name,
  78. room_grade=body.room_grade,
  79. room_status=body.room_status or "zc",
  80. area_sqm=body.area_sqm,
  81. tenant_no=tenant_no,
  82. order_num=body.order_num,
  83. remark=body.remark,
  84. user_id=user.get("user_id"),
  85. )
  86. return success_response(clean_room_svc._row_to_dict(obj), message="创建成功")
  87. except ValueError as e:
  88. return error_response(400, str(e))
  89. @router.put("/clean-rooms/{room_id}")
  90. async def update_clean_room(
  91. room_id: str,
  92. body: CleanRoomUpdateBody,
  93. user: dict = Depends(require_admin_or_super_admin),
  94. db: Session = Depends(get_db),
  95. ):
  96. try:
  97. tenant_no = _get_tenant_no(db, user)
  98. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  99. obj = clean_room_svc.update_clean_room(
  100. db,
  101. room_id,
  102. room_code=payload.get("room_code"),
  103. room_name=payload.get("room_name"),
  104. room_grade=payload.get("room_grade"),
  105. room_status=payload.get("room_status"),
  106. area_sqm=payload.get("area_sqm"),
  107. order_num=payload.get("order_num"),
  108. remark=payload.get("remark"),
  109. user_id=user.get("user_id"),
  110. tenant_no=tenant_no,
  111. )
  112. return success_response(clean_room_svc._row_to_dict(obj), message="更新成功")
  113. except ValueError as e:
  114. return error_response(400, str(e))
  115. @router.delete("/clean-rooms/{room_id}")
  116. async def delete_clean_room(
  117. room_id: str,
  118. user: dict = Depends(require_admin_or_super_admin),
  119. db: Session = Depends(get_db),
  120. ):
  121. try:
  122. tenant_no = _get_tenant_no(db, user)
  123. clean_room_svc.soft_delete_clean_room(db, room_id, tenant_no=tenant_no)
  124. return success_response(None, message="删除成功")
  125. except ValueError as e:
  126. return error_response(400, str(e))
  127. # ---------------------------------------------------------------------------
  128. # 进出记录
  129. # ---------------------------------------------------------------------------
  130. class AccessLogCreateBody(BaseModel):
  131. model_config = ConfigDict(populate_by_name=True)
  132. clean_room_id: str = Field(..., alias="cleanRoomId")
  133. person_name: Optional[str] = Field(None, alias="personName")
  134. dept_name: Optional[str] = Field(None, alias="deptName")
  135. enter_time: Optional[str] = Field(None, alias="enterTime")
  136. purpose: Optional[str] = None
  137. leave_time: Optional[str] = Field(None, alias="leaveTime")
  138. body_temp: Optional[str] = Field(None, alias="bodyTemp")
  139. order_num: Optional[int] = Field(0, alias="orderNum")
  140. remark: Optional[str] = None
  141. class AccessLogUpdateBody(BaseModel):
  142. model_config = ConfigDict(populate_by_name=True)
  143. person_name: Optional[str] = Field(None, alias="personName")
  144. dept_name: Optional[str] = Field(None, alias="deptName")
  145. enter_time: Optional[str] = Field(None, alias="enterTime")
  146. purpose: Optional[str] = None
  147. leave_time: Optional[str] = Field(None, alias="leaveTime")
  148. body_temp: Optional[str] = Field(None, alias="bodyTemp")
  149. order_num: Optional[int] = Field(None, alias="orderNum")
  150. remark: Optional[str] = None
  151. def _parse_optional_dt(value: Optional[str]) -> Optional[datetime]:
  152. if not value:
  153. return None
  154. return datetime.fromisoformat(value)
  155. @router.get("/clean-room-access-logs")
  156. async def list_access_logs(
  157. page: int = Query(1, ge=1),
  158. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  159. cleanRoomId: str = Query(..., alias="cleanRoomId"),
  160. keyword: Optional[str] = None,
  161. user: dict = Depends(require_admin_or_super_admin),
  162. db: Session = Depends(get_db),
  163. ):
  164. tenant_no = _get_tenant_no(db, user)
  165. query = db.query(BizCleanRoomAccessLog).filter(
  166. BizCleanRoomAccessLog.del_flag == 0,
  167. BizCleanRoomAccessLog.tenant_no == tenant_no,
  168. BizCleanRoomAccessLog.clean_room_id == cleanRoomId,
  169. )
  170. if keyword:
  171. kw = f"%{keyword}%"
  172. query = query.filter(
  173. BizCleanRoomAccessLog.person_name.like(kw)
  174. | BizCleanRoomAccessLog.dept_name.like(kw)
  175. )
  176. query = apply_data_scope(query, BizCleanRoomAccessLog, user, db, tenant_no)
  177. total = query.count()
  178. items = query.order_by(BizCleanRoomAccessLog.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
  179. result_list = [clean_room_svc._access_log_to_dict(r) for r in items]
  180. tp = clean_room_svc.total_pages(total, pageSize)
  181. return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
  182. @router.post("/clean-room-access-logs")
  183. async def create_access_log(
  184. body: AccessLogCreateBody,
  185. user: dict = Depends(require_admin_or_super_admin),
  186. db: Session = Depends(get_db),
  187. ):
  188. try:
  189. tenant_no = _get_tenant_no(db, user)
  190. obj = clean_room_svc.create_access_log(
  191. db,
  192. clean_room_id=body.clean_room_id,
  193. person_name=body.person_name,
  194. dept_name=body.dept_name,
  195. enter_time=_parse_optional_dt(body.enter_time),
  196. purpose=body.purpose,
  197. leave_time=_parse_optional_dt(body.leave_time),
  198. body_temp=body.body_temp,
  199. tenant_no=tenant_no,
  200. order_num=body.order_num,
  201. remark=body.remark,
  202. user_id_param=user.get("user_id"),
  203. )
  204. return success_response(clean_room_svc._access_log_to_dict(obj), message="创建成功")
  205. except ValueError as e:
  206. return error_response(400, str(e))
  207. @router.put("/clean-room-access-logs/{log_id}")
  208. async def update_access_log(
  209. log_id: str,
  210. body: AccessLogUpdateBody,
  211. user: dict = Depends(require_admin_or_super_admin),
  212. db: Session = Depends(get_db),
  213. ):
  214. try:
  215. tenant_no = _get_tenant_no(db, user)
  216. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  217. obj = clean_room_svc.update_access_log(
  218. db,
  219. log_id,
  220. person_name=payload.get("person_name"),
  221. dept_name=payload.get("dept_name"),
  222. enter_time=_parse_optional_dt(payload.get("enter_time")) if "enter_time" in payload else None,
  223. purpose=payload.get("purpose"),
  224. leave_time=_parse_optional_dt(payload.get("leave_time")) if "leave_time" in payload else None,
  225. body_temp=payload.get("body_temp"),
  226. order_num=payload.get("order_num"),
  227. remark=payload.get("remark"),
  228. user_id_param=user.get("user_id"),
  229. tenant_no=tenant_no,
  230. )
  231. return success_response(clean_room_svc._access_log_to_dict(obj), message="更新成功")
  232. except ValueError as e:
  233. return error_response(400, str(e))
  234. @router.delete("/clean-room-access-logs/{log_id}")
  235. async def delete_access_log(
  236. log_id: str,
  237. user: dict = Depends(require_admin_or_super_admin),
  238. db: Session = Depends(get_db),
  239. ):
  240. try:
  241. tenant_no = _get_tenant_no(db, user)
  242. clean_room_svc.soft_delete_access_log(db, log_id, tenant_no=tenant_no)
  243. return success_response(None, message="删除成功")
  244. except ValueError as e:
  245. return error_response(400, str(e))
  246. # ---------------------------------------------------------------------------
  247. # 仓储设施
  248. # ---------------------------------------------------------------------------
  249. class StorageFacilityCreateBody(BaseModel):
  250. model_config = ConfigDict(populate_by_name=True)
  251. parent_id: str = Field("0", alias="parentId")
  252. node_name: str = Field(..., alias="nodeName")
  253. node_type: Optional[str] = Field(None, alias="nodeType")
  254. is_leaf: Optional[int] = Field(0, alias="isLeaf")
  255. order_num: Optional[int] = Field(0, alias="orderNum")
  256. remark: Optional[str] = None
  257. class StorageFacilityUpdateBody(BaseModel):
  258. model_config = ConfigDict(populate_by_name=True)
  259. node_name: Optional[str] = Field(None, alias="nodeName")
  260. node_type: Optional[str] = Field(None, alias="nodeType")
  261. is_leaf: Optional[int] = Field(None, alias="isLeaf")
  262. order_num: Optional[int] = Field(None, alias="orderNum")
  263. remark: Optional[str] = None
  264. @router.get("/storage-facilities")
  265. async def list_storage_facilities(
  266. keyword: Optional[str] = None,
  267. user: dict = Depends(require_admin_or_super_admin),
  268. db: Session = Depends(get_db),
  269. ):
  270. tenant_no = _get_tenant_no(db, user)
  271. query = db.query(BizStorageFacility).filter(
  272. BizStorageFacility.del_flag == 0,
  273. BizStorageFacility.tenant_no == tenant_no,
  274. )
  275. if keyword:
  276. query = query.filter(BizStorageFacility.node_name.like(f"%{keyword}%"))
  277. query = apply_data_scope(query, BizStorageFacility, user, db, tenant_no)
  278. items = query.order_by(BizStorageFacility.create_time.desc()).all()
  279. result_list = [storage_facility_svc._row_to_dict(r) for r in items]
  280. return success_response(result_list)
  281. @router.post("/storage-facilities")
  282. async def create_storage_facility(
  283. body: StorageFacilityCreateBody,
  284. user: dict = Depends(require_admin_or_super_admin),
  285. db: Session = Depends(get_db),
  286. ):
  287. try:
  288. tenant_no = _get_tenant_no(db, user)
  289. obj = storage_facility_svc.create_facility(
  290. db,
  291. parent_id=body.parent_id,
  292. node_name=body.node_name,
  293. node_type=body.node_type,
  294. is_leaf=body.is_leaf,
  295. tenant_no=tenant_no,
  296. order_num=body.order_num,
  297. remark=body.remark,
  298. user_id=user.get("user_id"),
  299. )
  300. return success_response(storage_facility_svc._row_to_dict(obj), message="创建成功")
  301. except ValueError as e:
  302. return error_response(400, str(e))
  303. @router.put("/storage-facilities/{node_id}")
  304. async def update_storage_facility(
  305. node_id: str,
  306. body: StorageFacilityUpdateBody,
  307. user: dict = Depends(require_admin_or_super_admin),
  308. db: Session = Depends(get_db),
  309. ):
  310. try:
  311. tenant_no = _get_tenant_no(db, user)
  312. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  313. obj = storage_facility_svc.update_facility(
  314. db,
  315. node_id,
  316. node_name=payload.get("node_name"),
  317. node_type=payload.get("node_type"),
  318. is_leaf=payload.get("is_leaf"),
  319. order_num=payload.get("order_num"),
  320. remark=payload.get("remark"),
  321. user_id=user.get("user_id"),
  322. tenant_no=tenant_no,
  323. )
  324. return success_response(storage_facility_svc._row_to_dict(obj), message="更新成功")
  325. except ValueError as e:
  326. return error_response(400, str(e))
  327. @router.delete("/storage-facilities/{node_id}")
  328. async def delete_storage_facility(
  329. node_id: str,
  330. user: dict = Depends(require_admin_or_super_admin),
  331. db: Session = Depends(get_db),
  332. ):
  333. try:
  334. tenant_no = _get_tenant_no(db, user)
  335. storage_facility_svc.soft_delete_facility(db, node_id, tenant_no=tenant_no)
  336. return success_response(None, message="删除成功")
  337. except ValueError as e:
  338. return error_response(400, str(e))
  339. # ---------------------------------------------------------------------------
  340. # 储位
  341. # ---------------------------------------------------------------------------
  342. class StorageSlotCreateBody(BaseModel):
  343. model_config = ConfigDict(populate_by_name=True)
  344. facility_node_id: str = Field(..., alias="facilityNodeId")
  345. slot_code: Optional[str] = Field(None, alias="slotCode")
  346. slot_status: Optional[str] = Field(None, alias="slotStatus")
  347. order_num: Optional[int] = Field(0, alias="orderNum")
  348. remark: Optional[str] = None
  349. class StorageSlotBatchCreateBody(BaseModel):
  350. model_config = ConfigDict(populate_by_name=True)
  351. facility_node_id: str = Field(..., alias="facilityNodeId")
  352. rows: int = Field(..., ge=1, le=50)
  353. cols: int = Field(..., ge=1, le=50)
  354. code_prefix: Optional[str] = Field("", alias="codePrefix")
  355. class StorageSlotUpdateBody(BaseModel):
  356. model_config = ConfigDict(populate_by_name=True)
  357. slot_code: Optional[str] = Field(None, alias="slotCode")
  358. slot_status: Optional[str] = Field(None, alias="slotStatus")
  359. order_num: Optional[int] = Field(None, alias="orderNum")
  360. remark: Optional[str] = None
  361. @router.get("/storage-facility-slots")
  362. async def list_storage_slots(
  363. facilityNodeId: str = Query(..., alias="facilityNodeId"),
  364. user: dict = Depends(require_admin_or_super_admin),
  365. db: Session = Depends(get_db),
  366. ):
  367. tenant_no = _get_tenant_no(db, user)
  368. query = db.query(BizStorageFacilitySlot).filter(
  369. BizStorageFacilitySlot.del_flag == 0,
  370. BizStorageFacilitySlot.tenant_no == tenant_no,
  371. BizStorageFacilitySlot.facility_node_id == facilityNodeId,
  372. )
  373. query = apply_data_scope(query, BizStorageFacilitySlot, user, db, tenant_no)
  374. items = query.order_by(BizStorageFacilitySlot.order_num.asc(), BizStorageFacilitySlot.create_time.asc()).all()
  375. result_list = [storage_facility_svc._slot_to_dict(r) for r in items]
  376. return success_response(result_list)
  377. @router.post("/storage-facility-slots/batch")
  378. async def batch_create_storage_slots(
  379. body: StorageSlotBatchCreateBody,
  380. user: dict = Depends(require_admin_or_super_admin),
  381. db: Session = Depends(get_db),
  382. ):
  383. try:
  384. tenant_no = _get_tenant_no(db, user)
  385. items = storage_facility_svc.batch_create_slots(
  386. db,
  387. facility_node_id=body.facility_node_id,
  388. rows=body.rows,
  389. cols=body.cols,
  390. code_prefix=body.code_prefix,
  391. tenant_no=tenant_no,
  392. user_id=user.get("user_id"),
  393. )
  394. return success_response(items, message=f"批量创建 {len(items)} 个格子成功")
  395. except ValueError as e:
  396. return error_response(400, str(e))
  397. @router.post("/storage-facility-slots")
  398. async def create_storage_slot(
  399. body: StorageSlotCreateBody,
  400. user: dict = Depends(require_admin_or_super_admin),
  401. db: Session = Depends(get_db),
  402. ):
  403. try:
  404. tenant_no = _get_tenant_no(db, user)
  405. obj = storage_facility_svc.create_slot(
  406. db,
  407. facility_node_id=body.facility_node_id,
  408. slot_code=body.slot_code,
  409. slot_status=body.slot_status,
  410. tenant_no=tenant_no,
  411. order_num=body.order_num,
  412. remark=body.remark,
  413. user_id=user.get("user_id"),
  414. )
  415. return success_response(storage_facility_svc._slot_to_dict(obj), message="创建成功")
  416. except ValueError as e:
  417. return error_response(400, str(e))
  418. @router.put("/storage-facility-slots/{slot_id}")
  419. async def update_storage_slot(
  420. slot_id: str,
  421. body: StorageSlotUpdateBody,
  422. user: dict = Depends(require_admin_or_super_admin),
  423. db: Session = Depends(get_db),
  424. ):
  425. try:
  426. tenant_no = _get_tenant_no(db, user)
  427. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  428. obj = storage_facility_svc.update_slot(
  429. db,
  430. slot_id,
  431. slot_code=payload.get("slot_code"),
  432. slot_status=payload.get("slot_status"),
  433. order_num=payload.get("order_num"),
  434. remark=payload.get("remark"),
  435. user_id=user.get("user_id"),
  436. tenant_no=tenant_no,
  437. )
  438. return success_response(storage_facility_svc._slot_to_dict(obj), message="更新成功")
  439. except ValueError as e:
  440. return error_response(400, str(e))
  441. @router.delete("/storage-facility-slots/{slot_id}")
  442. async def delete_storage_slot(
  443. slot_id: str,
  444. user: dict = Depends(require_admin_or_super_admin),
  445. db: Session = Depends(get_db),
  446. ):
  447. try:
  448. tenant_no = _get_tenant_no(db, user)
  449. storage_facility_svc.soft_delete_slot(db, slot_id, tenant_no=tenant_no)
  450. return success_response(None, message="删除成功")
  451. except ValueError as e:
  452. return error_response(400, str(e))
  453. # ---------------------------------------------------------------------------
  454. # 展厅预约
  455. # ---------------------------------------------------------------------------
  456. class ExhibitionAppointmentCreateBody(BaseModel):
  457. model_config = ConfigDict(populate_by_name=True)
  458. appointment_no: Optional[str] = Field(None, alias="appointmentNo")
  459. visit_org: Optional[str] = Field(None, alias="visitOrg")
  460. contact_name: Optional[str] = Field(None, alias="contactName")
  461. visit_date: Optional[str] = Field(None, alias="visitDate")
  462. visitor_count: Optional[int] = Field(None, alias="visitorCount")
  463. reception_user_id: Optional[str] = Field(None, alias="receptionUserId")
  464. order_num: Optional[int] = Field(0, alias="orderNum")
  465. remark: Optional[str] = None
  466. class ExhibitionAppointmentUpdateBody(BaseModel):
  467. model_config = ConfigDict(populate_by_name=True)
  468. visit_org: Optional[str] = Field(None, alias="visitOrg")
  469. contact_name: Optional[str] = Field(None, alias="contactName")
  470. visit_date: Optional[str] = Field(None, alias="visitDate")
  471. visitor_count: Optional[int] = Field(None, alias="visitorCount")
  472. reception_user_id: Optional[str] = Field(None, alias="receptionUserId")
  473. order_num: Optional[int] = Field(None, alias="orderNum")
  474. remark: Optional[str] = None
  475. @router.get("/exhibition-appointments")
  476. async def list_exhibition_appointments(
  477. page: int = Query(1, ge=1),
  478. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  479. keyword: Optional[str] = None,
  480. user: dict = Depends(require_admin_or_super_admin),
  481. db: Session = Depends(get_db),
  482. ):
  483. tenant_no = _get_tenant_no(db, user)
  484. query = db.query(BizExhibitionAppointment).filter(
  485. BizExhibitionAppointment.del_flag == 0,
  486. BizExhibitionAppointment.tenant_no == tenant_no,
  487. )
  488. if keyword:
  489. query = query.filter(BizExhibitionAppointment.visit_org.like(f"%{keyword}%"))
  490. query = apply_data_scope(query, BizExhibitionAppointment, user, db, tenant_no)
  491. total = query.count()
  492. items = query.order_by(BizExhibitionAppointment.create_time.desc()).offset((page - 1) * pageSize).limit(pageSize).all()
  493. result_list = [exhibition_svc._row_to_dict(r) for r in items]
  494. tp = exhibition_svc.total_pages(total, pageSize)
  495. return success_response({"list": result_list, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
  496. @router.post("/exhibition-appointments")
  497. async def create_exhibition_appointment(
  498. body: ExhibitionAppointmentCreateBody,
  499. user: dict = Depends(require_admin_or_super_admin),
  500. db: Session = Depends(get_db),
  501. ):
  502. try:
  503. tenant_no = _get_tenant_no(db, user)
  504. obj = exhibition_svc.create_exhibition_appointment(
  505. db,
  506. appointment_no=body.appointment_no,
  507. visit_org=body.visit_org,
  508. contact_name=body.contact_name,
  509. visit_date=body.visit_date,
  510. visitor_count=body.visitor_count,
  511. reception_user_id=body.reception_user_id,
  512. tenant_no=tenant_no,
  513. order_num=body.order_num,
  514. remark=body.remark,
  515. user_id=user.get("user_id"),
  516. )
  517. return success_response(exhibition_svc._row_to_dict(obj), message="创建成功")
  518. except ValueError as e:
  519. return error_response(400, str(e))
  520. @router.put("/exhibition-appointments/{appointment_id}")
  521. async def update_exhibition_appointment(
  522. appointment_id: str,
  523. body: ExhibitionAppointmentUpdateBody,
  524. user: dict = Depends(require_admin_or_super_admin),
  525. db: Session = Depends(get_db),
  526. ):
  527. try:
  528. tenant_no = _get_tenant_no(db, user)
  529. payload: dict[str, Any] = body.model_dump(exclude_unset=True, by_alias=False)
  530. obj = exhibition_svc.update_exhibition_appointment(
  531. db,
  532. appointment_id,
  533. visit_org=payload.get("visit_org"),
  534. contact_name=payload.get("contact_name"),
  535. visit_date=payload.get("visit_date"),
  536. visitor_count=payload.get("visitor_count"),
  537. reception_user_id=payload.get("reception_user_id"),
  538. order_num=payload.get("order_num"),
  539. remark=payload.get("remark"),
  540. user_id=user.get("user_id"),
  541. )
  542. return success_response(exhibition_svc._row_to_dict(obj), message="更新成功")
  543. except ValueError as e:
  544. return error_response(400, str(e))
  545. @router.delete("/exhibition-appointments/{appointment_id}")
  546. async def delete_exhibition_appointment(
  547. appointment_id: str,
  548. user: dict = Depends(require_admin_or_super_admin),
  549. db: Session = Depends(get_db),
  550. ):
  551. try:
  552. exhibition_svc.soft_delete_exhibition_appointment(db, appointment_id)
  553. return success_response(None, message="删除成功")
  554. except ValueError as e:
  555. return error_response(400, str(e))