clean_room.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. """洁净室及进出记录、紫外线消毒记录 CRUD(biz_clean_room + biz_clean_room_access_log + biz_clean_room_uv_disinfect_log)"""
  2. from __future__ import annotations
  3. import math
  4. from datetime import datetime
  5. from typing import Any, Dict, List, Optional, Tuple
  6. from sqlalchemy import and_, func, or_
  7. from sqlalchemy.orm import Session
  8. from app.core.utils import generate_uuid
  9. from app.models.models import BizCleanRoom, BizCleanRoomAccessLog, BizCleanRoomUvDisinfectLog
  10. # ---------------------------------------------------------------------------
  11. # 洁净室
  12. # ---------------------------------------------------------------------------
  13. def _row_to_dict(c: BizCleanRoom) -> Dict[str, Any]:
  14. return {
  15. "id": c.id,
  16. "roomCode": c.room_code,
  17. "roomName": c.room_name,
  18. "roomGrade": c.room_grade,
  19. "roomStatus": c.room_status,
  20. "areaSqm": float(c.area_sqm) if c.area_sqm is not None else 0.0,
  21. "roomImage": c.room_image or "",
  22. "tenantNo": c.tenant_no,
  23. "orderNum": c.order_num if c.order_num is not None else 0,
  24. "remark": c.remark or "",
  25. "createTime": c.create_time.strftime("%Y-%m-%d %H:%M:%S") if c.create_time else "",
  26. "updateTime": c.update_time.strftime("%Y-%m-%d %H:%M:%S") if c.update_time else "",
  27. }
  28. def list_clean_rooms(
  29. db: Session,
  30. *,
  31. page: int = 1,
  32. page_size: int = 10,
  33. keyword: Optional[str] = None,
  34. tenant_no: Optional[str] = None,
  35. ) -> Tuple[List[Dict[str, Any]], int]:
  36. def _filtered():
  37. q0 = db.query(BizCleanRoom).filter(BizCleanRoom.del_flag == 0)
  38. if tenant_no:
  39. q0 = q0.filter(BizCleanRoom.tenant_no == tenant_no)
  40. kw = (keyword or "").strip()
  41. if kw:
  42. like = f"%{kw}%"
  43. q0 = q0.filter(or_(BizCleanRoom.room_code.like(like), BizCleanRoom.room_name.like(like)))
  44. return q0
  45. total = _filtered().order_by(None).with_entities(func.count(BizCleanRoom.id)).scalar() or 0
  46. page = max(1, page)
  47. page_size = min(max(1, page_size), 100)
  48. rows = (
  49. _filtered()
  50. .order_by(BizCleanRoom.create_time.desc())
  51. .offset((page - 1) * page_size)
  52. .limit(page_size)
  53. .all()
  54. )
  55. return [_row_to_dict(x) for x in rows], int(total)
  56. def get_clean_room(db: Session, room_id: str, tenant_no: Optional[str] = None) -> Optional[BizCleanRoom]:
  57. q = db.query(BizCleanRoom).filter(and_(BizCleanRoom.id == room_id, BizCleanRoom.del_flag == 0))
  58. if tenant_no:
  59. q = q.filter(BizCleanRoom.tenant_no == tenant_no)
  60. return q.first()
  61. def room_code_exists(db: Session, code: str, tenant_no: str, exclude_id: Optional[str] = None) -> bool:
  62. q = db.query(BizCleanRoom).filter(
  63. and_(BizCleanRoom.room_code == code.strip(), BizCleanRoom.tenant_no == tenant_no, BizCleanRoom.del_flag == 0)
  64. )
  65. if exclude_id:
  66. q = q.filter(BizCleanRoom.id != exclude_id)
  67. return q.first() is not None
  68. def create_clean_room(
  69. db: Session,
  70. *,
  71. room_code: str,
  72. room_name: str,
  73. room_grade: Optional[str] = None,
  74. room_status: Optional[str] = None,
  75. area_sqm: Optional[Any] = None,
  76. room_image: Optional[str] = None,
  77. tenant_no: str = "zkxw",
  78. order_num: Optional[int] = None,
  79. remark: Optional[str] = None,
  80. user_id: Optional[str] = None,
  81. ) -> BizCleanRoom:
  82. rc = (room_code or "").strip()
  83. if not rc:
  84. raise ValueError("洁净室编码不能为空")
  85. if room_code_exists(db, rc, tenant_no):
  86. raise ValueError("洁净室编码已存在")
  87. obj = BizCleanRoom(
  88. id=generate_uuid(),
  89. room_code=rc,
  90. room_name=(room_name or "").strip() or rc,
  91. room_grade=(room_grade or "").strip() or None,
  92. room_status=(room_status or "").strip() or None,
  93. area_sqm=area_sqm if area_sqm is not None else 0,
  94. room_image=(room_image or "").strip() or None,
  95. tenant_no=tenant_no,
  96. order_num=order_num if order_num is not None else 0,
  97. remark=(remark or "").strip() or None,
  98. create_by=user_id,
  99. create_dept_id="",
  100. create_time=datetime.now(),
  101. update_by=user_id,
  102. update_time=datetime.now(),
  103. del_flag=0,
  104. )
  105. db.add(obj)
  106. db.commit()
  107. db.refresh(obj)
  108. return obj
  109. def update_clean_room(
  110. db: Session,
  111. room_id: str,
  112. *,
  113. room_code: Optional[str] = None,
  114. room_name: Optional[str] = None,
  115. room_grade: Optional[str] = None,
  116. room_status: Optional[str] = None,
  117. area_sqm: Optional[Any] = None,
  118. room_image: Optional[str] = None,
  119. order_num: Optional[int] = None,
  120. remark: Optional[str] = None,
  121. user_id: Optional[str] = None,
  122. tenant_no: Optional[str] = None,
  123. ) -> BizCleanRoom:
  124. obj = get_clean_room(db, room_id, tenant_no=tenant_no)
  125. if not obj:
  126. raise ValueError("洁净室不存在")
  127. if room_code is not None:
  128. new_code = room_code.strip()
  129. if not new_code:
  130. raise ValueError("洁净室编码不能为空")
  131. if new_code != obj.room_code and room_code_exists(db, new_code, obj.tenant_no, exclude_id=room_id):
  132. raise ValueError("洁净室编码已存在")
  133. obj.room_code = new_code
  134. if room_name is not None:
  135. obj.room_name = (room_name or "").strip() or obj.room_name
  136. if room_grade is not None:
  137. obj.room_grade = (room_grade or "").strip() or None
  138. if room_status is not None:
  139. obj.room_status = (room_status or "").strip() or None
  140. if area_sqm is not None:
  141. obj.area_sqm = area_sqm
  142. if room_image is not None:
  143. obj.room_image = (room_image or "").strip() or None
  144. if order_num is not None:
  145. obj.order_num = order_num
  146. if remark is not None:
  147. obj.remark = (remark or "").strip() or None
  148. obj.update_by = user_id
  149. obj.update_time = datetime.now()
  150. db.add(obj)
  151. db.commit()
  152. db.refresh(obj)
  153. return obj
  154. def soft_delete_clean_room(db: Session, room_id: str, tenant_no: Optional[str] = None) -> None:
  155. obj = get_clean_room(db, room_id, tenant_no=tenant_no)
  156. if not obj:
  157. raise ValueError("洁净室不存在")
  158. obj.del_flag = 1
  159. obj.update_time = datetime.now()
  160. db.add(obj)
  161. db.commit()
  162. # ---------------------------------------------------------------------------
  163. # 进出记录
  164. # ---------------------------------------------------------------------------
  165. def _access_log_to_dict(log: BizCleanRoomAccessLog) -> Dict[str, Any]:
  166. return {
  167. "id": log.id,
  168. "cleanRoomId": log.clean_room_id,
  169. "userId": log.user_id,
  170. "personName": log.person_name,
  171. "deptName": log.dept_name,
  172. "enterTime": log.enter_time.strftime("%Y-%m-%d %H:%M:%S") if log.enter_time else "",
  173. "purpose": log.purpose or "",
  174. "leaveTime": log.leave_time.strftime("%Y-%m-%d %H:%M:%S") if log.leave_time else "",
  175. "bodyTemp": log.body_temp or "",
  176. "tenantNo": log.tenant_no,
  177. "orderNum": log.order_num if log.order_num is not None else 0,
  178. "remark": log.remark or "",
  179. "createTime": log.create_time.strftime("%Y-%m-%d %H:%M:%S") if log.create_time else "",
  180. "updateTime": log.update_time.strftime("%Y-%m-%d %H:%M:%S") if log.update_time else "",
  181. }
  182. def list_access_logs(
  183. db: Session,
  184. *,
  185. page: int = 1,
  186. page_size: int = 10,
  187. clean_room_id: Optional[str] = None,
  188. keyword: Optional[str] = None,
  189. tenant_no: Optional[str] = None,
  190. ) -> Tuple[List[Dict[str, Any]], int]:
  191. def _filtered():
  192. q0 = db.query(BizCleanRoomAccessLog).filter(BizCleanRoomAccessLog.del_flag == 0)
  193. if clean_room_id:
  194. q0 = q0.filter(BizCleanRoomAccessLog.clean_room_id == clean_room_id)
  195. if tenant_no:
  196. q0 = q0.filter(BizCleanRoomAccessLog.tenant_no == tenant_no)
  197. kw = (keyword or "").strip()
  198. if kw:
  199. like = f"%{kw}%"
  200. q0 = q0.filter(or_(
  201. BizCleanRoomAccessLog.person_name.like(like),
  202. BizCleanRoomAccessLog.dept_name.like(like),
  203. ))
  204. return q0
  205. total = _filtered().order_by(None).with_entities(func.count(BizCleanRoomAccessLog.id)).scalar() or 0
  206. page = max(1, page)
  207. page_size = min(max(1, page_size), 100)
  208. rows = (
  209. _filtered()
  210. .order_by(BizCleanRoomAccessLog.create_time.desc())
  211. .offset((page - 1) * page_size)
  212. .limit(page_size)
  213. .all()
  214. )
  215. return [_access_log_to_dict(x) for x in rows], int(total)
  216. def get_access_log(db: Session, log_id: str, tenant_no: Optional[str] = None) -> Optional[BizCleanRoomAccessLog]:
  217. q = db.query(BizCleanRoomAccessLog).filter(and_(BizCleanRoomAccessLog.id == log_id, BizCleanRoomAccessLog.del_flag == 0))
  218. if tenant_no:
  219. q = q.filter(BizCleanRoomAccessLog.tenant_no == tenant_no)
  220. return q.first()
  221. def create_access_log(
  222. db: Session,
  223. *,
  224. clean_room_id: str,
  225. user_id: Optional[str] = None,
  226. person_name: Optional[str] = None,
  227. dept_name: Optional[str] = None,
  228. enter_time: Optional[datetime] = None,
  229. purpose: Optional[str] = None,
  230. leave_time: Optional[datetime] = None,
  231. body_temp: Optional[str] = None,
  232. tenant_no: str = "zkxw",
  233. order_num: Optional[int] = None,
  234. remark: Optional[str] = None,
  235. user_id_param: Optional[str] = None,
  236. ) -> BizCleanRoomAccessLog:
  237. cr_id = (clean_room_id or "").strip()
  238. if not cr_id:
  239. raise ValueError("洁净室ID不能为空")
  240. obj = BizCleanRoomAccessLog(
  241. id=generate_uuid(),
  242. clean_room_id=cr_id,
  243. user_id=(user_id or "").strip() or None,
  244. person_name=(person_name or "").strip() or None,
  245. dept_name=(dept_name or "").strip() or None,
  246. enter_time=enter_time or datetime.now(),
  247. purpose=(purpose or "").strip() or None,
  248. leave_time=leave_time,
  249. body_temp=(body_temp or "").strip() or None,
  250. tenant_no=tenant_no,
  251. order_num=order_num if order_num is not None else 0,
  252. remark=(remark or "").strip() or None,
  253. create_by=user_id_param,
  254. create_dept_id="",
  255. create_time=datetime.now(),
  256. update_by=user_id_param,
  257. update_time=datetime.now(),
  258. del_flag=0,
  259. )
  260. db.add(obj)
  261. db.commit()
  262. db.refresh(obj)
  263. return obj
  264. def update_access_log(
  265. db: Session,
  266. log_id: str,
  267. *,
  268. person_name: Optional[str] = None,
  269. dept_name: Optional[str] = None,
  270. enter_time: Optional[datetime] = None,
  271. purpose: Optional[str] = None,
  272. leave_time: Optional[datetime] = None,
  273. body_temp: Optional[str] = None,
  274. order_num: Optional[int] = None,
  275. remark: Optional[str] = None,
  276. user_id_param: Optional[str] = None,
  277. tenant_no: Optional[str] = None,
  278. ) -> BizCleanRoomAccessLog:
  279. obj = get_access_log(db, log_id, tenant_no=tenant_no)
  280. if not obj:
  281. raise ValueError("进出记录不存在")
  282. if person_name is not None:
  283. obj.person_name = (person_name or "").strip() or None
  284. if dept_name is not None:
  285. obj.dept_name = (dept_name or "").strip() or None
  286. if enter_time is not None:
  287. obj.enter_time = enter_time
  288. if purpose is not None:
  289. obj.purpose = (purpose or "").strip() or None
  290. if leave_time is not None:
  291. obj.leave_time = leave_time
  292. if body_temp is not None:
  293. obj.body_temp = (body_temp or "").strip() or None
  294. if order_num is not None:
  295. obj.order_num = order_num
  296. if remark is not None:
  297. obj.remark = (remark or "").strip() or None
  298. obj.update_by = user_id_param
  299. obj.update_time = datetime.now()
  300. db.add(obj)
  301. db.commit()
  302. db.refresh(obj)
  303. return obj
  304. def soft_delete_access_log(db: Session, log_id: str, tenant_no: Optional[str] = None) -> None:
  305. obj = get_access_log(db, log_id, tenant_no=tenant_no)
  306. if not obj:
  307. raise ValueError("进出记录不存在")
  308. obj.del_flag = 1
  309. obj.update_time = datetime.now()
  310. db.add(obj)
  311. db.commit()
  312. def total_pages(total: int, page_size: int) -> int:
  313. if total <= 0:
  314. return 0
  315. return int(math.ceil(total / float(page_size)))
  316. # ---------------------------------------------------------------------------
  317. # 紫外线消毒记录
  318. # ---------------------------------------------------------------------------
  319. def _calc_duration_minutes(start_time: Optional[datetime], end_time: Optional[datetime]) -> Optional[int]:
  320. if not start_time or not end_time:
  321. return None
  322. delta = end_time - start_time
  323. minutes = int(delta.total_seconds() // 60)
  324. return max(minutes, 0)
  325. def _uv_disinfect_log_to_dict(log: BizCleanRoomUvDisinfectLog) -> Dict[str, Any]:
  326. duration = log.duration_minutes
  327. if duration is None:
  328. duration = _calc_duration_minutes(log.start_time, log.end_time)
  329. return {
  330. "id": log.id,
  331. "cleanRoomId": log.clean_room_id,
  332. "userId": log.user_id,
  333. "operatorName": log.operator_name,
  334. "startTime": log.start_time.strftime("%Y-%m-%d %H:%M:%S") if log.start_time else "",
  335. "endTime": log.end_time.strftime("%Y-%m-%d %H:%M:%S") if log.end_time else "",
  336. "durationMinutes": duration if duration is not None else None,
  337. "tenantNo": log.tenant_no,
  338. "orderNum": log.order_num if log.order_num is not None else 0,
  339. "remark": log.remark or "",
  340. "createTime": log.create_time.strftime("%Y-%m-%d %H:%M:%S") if log.create_time else "",
  341. "updateTime": log.update_time.strftime("%Y-%m-%d %H:%M:%S") if log.update_time else "",
  342. }
  343. def list_uv_disinfect_logs(
  344. db: Session,
  345. *,
  346. page: int = 1,
  347. page_size: int = 10,
  348. clean_room_id: Optional[str] = None,
  349. keyword: Optional[str] = None,
  350. tenant_no: Optional[str] = None,
  351. ) -> Tuple[List[Dict[str, Any]], int]:
  352. def _filtered():
  353. q0 = db.query(BizCleanRoomUvDisinfectLog).filter(BizCleanRoomUvDisinfectLog.del_flag == 0)
  354. if clean_room_id:
  355. q0 = q0.filter(BizCleanRoomUvDisinfectLog.clean_room_id == clean_room_id)
  356. if tenant_no:
  357. q0 = q0.filter(BizCleanRoomUvDisinfectLog.tenant_no == tenant_no)
  358. kw = (keyword or "").strip()
  359. if kw:
  360. like = f"%{kw}%"
  361. q0 = q0.filter(BizCleanRoomUvDisinfectLog.operator_name.like(like))
  362. return q0
  363. total = _filtered().order_by(None).with_entities(func.count(BizCleanRoomUvDisinfectLog.id)).scalar() or 0
  364. page = max(1, page)
  365. page_size = min(max(1, page_size), 100)
  366. rows = (
  367. _filtered()
  368. .order_by(BizCleanRoomUvDisinfectLog.create_time.desc())
  369. .offset((page - 1) * page_size)
  370. .limit(page_size)
  371. .all()
  372. )
  373. return [_uv_disinfect_log_to_dict(x) for x in rows], int(total)
  374. def get_uv_disinfect_log(
  375. db: Session, log_id: str, tenant_no: Optional[str] = None
  376. ) -> Optional[BizCleanRoomUvDisinfectLog]:
  377. q = db.query(BizCleanRoomUvDisinfectLog).filter(
  378. and_(BizCleanRoomUvDisinfectLog.id == log_id, BizCleanRoomUvDisinfectLog.del_flag == 0)
  379. )
  380. if tenant_no:
  381. q = q.filter(BizCleanRoomUvDisinfectLog.tenant_no == tenant_no)
  382. return q.first()
  383. def create_uv_disinfect_log(
  384. db: Session,
  385. *,
  386. clean_room_id: str,
  387. user_id: Optional[str] = None,
  388. operator_name: Optional[str] = None,
  389. start_time: Optional[datetime] = None,
  390. end_time: Optional[datetime] = None,
  391. duration_minutes: Optional[int] = None,
  392. tenant_no: str = "zkxw",
  393. order_num: Optional[int] = None,
  394. remark: Optional[str] = None,
  395. user_id_param: Optional[str] = None,
  396. ) -> BizCleanRoomUvDisinfectLog:
  397. cr_id = (clean_room_id or "").strip()
  398. if not cr_id:
  399. raise ValueError("洁净室ID不能为空")
  400. op_name = (operator_name or "").strip()
  401. if not op_name:
  402. raise ValueError("操作人不能为空")
  403. st = start_time or datetime.now()
  404. if end_time and end_time < st:
  405. raise ValueError("结束时间不能早于开始时间")
  406. dur = duration_minutes
  407. if dur is None:
  408. dur = _calc_duration_minutes(st, end_time)
  409. obj = BizCleanRoomUvDisinfectLog(
  410. id=generate_uuid(),
  411. clean_room_id=cr_id,
  412. user_id=(user_id or "").strip() or None,
  413. operator_name=op_name,
  414. start_time=st,
  415. end_time=end_time,
  416. duration_minutes=dur,
  417. tenant_no=tenant_no,
  418. order_num=order_num if order_num is not None else 0,
  419. remark=(remark or "").strip() or None,
  420. create_by=user_id_param,
  421. create_dept_id="",
  422. create_time=datetime.now(),
  423. update_by=user_id_param,
  424. update_time=datetime.now(),
  425. del_flag=0,
  426. )
  427. db.add(obj)
  428. db.commit()
  429. db.refresh(obj)
  430. return obj
  431. def update_uv_disinfect_log(
  432. db: Session,
  433. log_id: str,
  434. *,
  435. user_id: Optional[str] = None,
  436. operator_name: Optional[str] = None,
  437. start_time: Optional[datetime] = None,
  438. end_time: Optional[datetime] = None,
  439. duration_minutes: Optional[int] = None,
  440. order_num: Optional[int] = None,
  441. remark: Optional[str] = None,
  442. user_id_param: Optional[str] = None,
  443. tenant_no: Optional[str] = None,
  444. clear_end_time: bool = False,
  445. ) -> BizCleanRoomUvDisinfectLog:
  446. obj = get_uv_disinfect_log(db, log_id, tenant_no=tenant_no)
  447. if not obj:
  448. raise ValueError("紫外线消毒记录不存在")
  449. if operator_name is not None:
  450. op_name = (operator_name or "").strip()
  451. if not op_name:
  452. raise ValueError("操作人不能为空")
  453. obj.operator_name = op_name
  454. if user_id is not None:
  455. obj.user_id = (user_id or "").strip() or None
  456. if start_time is not None:
  457. obj.start_time = start_time
  458. if clear_end_time:
  459. obj.end_time = None
  460. elif end_time is not None:
  461. obj.end_time = end_time
  462. st = obj.start_time
  463. et = obj.end_time
  464. if et and st and et < st:
  465. raise ValueError("结束时间不能早于开始时间")
  466. if duration_minutes is not None:
  467. obj.duration_minutes = duration_minutes
  468. else:
  469. obj.duration_minutes = _calc_duration_minutes(st, et)
  470. if order_num is not None:
  471. obj.order_num = order_num
  472. if remark is not None:
  473. obj.remark = (remark or "").strip() or None
  474. obj.update_by = user_id_param
  475. obj.update_time = datetime.now()
  476. db.add(obj)
  477. db.commit()
  478. db.refresh(obj)
  479. return obj
  480. def soft_delete_uv_disinfect_log(db: Session, log_id: str, tenant_no: Optional[str] = None) -> None:
  481. obj = get_uv_disinfect_log(db, log_id, tenant_no=tenant_no)
  482. if not obj:
  483. raise ValueError("紫外线消毒记录不存在")
  484. obj.del_flag = 1
  485. obj.update_time = datetime.now()
  486. db.add(obj)
  487. db.commit()