env_detect.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. """环境检测 — 检测项维护 + 登记管理 API"""
  2. from datetime import datetime, time
  3. from typing import List, 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, require_menu_code
  10. from app.core.response import error_response, success_response
  11. from app.core.utils import parse_datetime
  12. from app.crud import env_detect_item as item_svc
  13. from app.crud import env_detect_record as record_svc
  14. from app.crud.collection_protocol import total_pages
  15. from app.models.models import BizEnvDetectItem, BizEnvDetectRecord, SysUser
  16. router = APIRouter(prefix="/web/admin", tags=["环境检测"])
  17. def _get_tenant_no(db: Session, user: dict) -> str:
  18. user_id = user.get("user_id")
  19. if not user_id:
  20. raise PermissionError("用户身份信息缺失,无法确定租户")
  21. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  22. if not sys_user or not sys_user.tenant_no:
  23. raise PermissionError("用户租户信息缺失")
  24. return sys_user.tenant_no
  25. def _get_current_user_id(user: dict) -> str:
  26. user_id = user.get("user_id")
  27. if not user_id:
  28. raise PermissionError("用户身份信息缺失")
  29. return str(user_id)
  30. def _page_payload(items: list, total: int, page: int, page_size: int) -> dict:
  31. return {
  32. "list": items,
  33. "total": total,
  34. "page": page,
  35. "pageSize": page_size,
  36. "totalPages": total_pages(total, page_size),
  37. }
  38. # ---------- 环境检测项 ----------
  39. class EnvDetectItemBody(BaseModel):
  40. model_config = ConfigDict(populate_by_name=True)
  41. item_code: str = Field(..., alias="itemCode")
  42. item_name: str = Field(..., alias="itemName")
  43. status: str = Field("1", alias="status")
  44. order_num: Optional[int] = Field(None, alias="orderNum")
  45. remark: Optional[str] = None
  46. class EnvDetectItemUpdateBody(BaseModel):
  47. model_config = ConfigDict(populate_by_name=True)
  48. item_code: Optional[str] = Field(None, alias="itemCode")
  49. item_name: Optional[str] = Field(None, alias="itemName")
  50. status: Optional[str] = None
  51. order_num: Optional[int] = Field(None, alias="orderNum")
  52. remark: Optional[str] = None
  53. @router.get("/env-detect-items")
  54. async def list_env_detect_items(
  55. page: int = Query(1, ge=1),
  56. pageSize: int = Query(10, ge=1, le=100),
  57. keyword: Optional[str] = None,
  58. status: Optional[str] = None,
  59. db: Session = Depends(get_db),
  60. user: dict = Depends(require_menu_code("biz:envDetectItem:list")),
  61. ):
  62. try:
  63. tenant_no = _get_tenant_no(db, user)
  64. query = db.query(BizEnvDetectItem).filter(
  65. BizEnvDetectItem.del_flag == 0,
  66. BizEnvDetectItem.tenant_no == tenant_no,
  67. )
  68. kw = (keyword or "").strip()
  69. if kw:
  70. from sqlalchemy import or_
  71. like = f"%{kw}%"
  72. query = query.filter(
  73. or_(
  74. BizEnvDetectItem.item_code.like(like),
  75. BizEnvDetectItem.item_name.like(like),
  76. )
  77. )
  78. if status:
  79. query = query.filter(BizEnvDetectItem.status == status)
  80. query = apply_data_scope(query, BizEnvDetectItem, user, db, tenant_no)
  81. total = query.count()
  82. items = (
  83. query.order_by(BizEnvDetectItem.create_time.desc())
  84. .offset((page - 1) * pageSize)
  85. .limit(pageSize)
  86. .all()
  87. )
  88. result_list = [item_svc._row_to_dict(r) for r in items]
  89. return success_response(_page_payload(result_list, total, page, pageSize))
  90. except PermissionError as e:
  91. return error_response(403, str(e))
  92. except Exception as e:
  93. return error_response(400, str(e))
  94. @router.get("/env-detect-items/active")
  95. async def list_active_env_detect_items(
  96. db: Session = Depends(get_db),
  97. user: dict = Depends(require_menu_code("biz:envDetectRecord:list")),
  98. ):
  99. try:
  100. tenant_no = _get_tenant_no(db, user)
  101. items = item_svc.list_active_env_detect_items(db, tenant_no)
  102. return success_response(items)
  103. except PermissionError as e:
  104. return error_response(403, str(e))
  105. except Exception as e:
  106. return error_response(400, str(e))
  107. @router.get("/env-detect-items/{row_id}")
  108. async def get_env_detect_item(
  109. row_id: str,
  110. db: Session = Depends(get_db),
  111. user: dict = Depends(require_menu_code("biz:envDetectItem:list")),
  112. ):
  113. try:
  114. tenant_no = _get_tenant_no(db, user)
  115. obj = item_svc.get_env_detect_item(db, row_id, tenant_no=tenant_no)
  116. if not obj:
  117. return error_response(404, "检测项不存在")
  118. return success_response(item_svc._row_to_dict(obj))
  119. except PermissionError as e:
  120. return error_response(403, str(e))
  121. except Exception as e:
  122. return error_response(400, str(e))
  123. @router.post("/env-detect-items")
  124. async def create_env_detect_item(
  125. body: EnvDetectItemBody,
  126. db: Session = Depends(get_db),
  127. user: dict = Depends(require_admin_or_super_admin),
  128. ):
  129. try:
  130. tenant_no = _get_tenant_no(db, user)
  131. user_id = _get_current_user_id(user)
  132. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  133. dept_id = (sys_user.dept_id or "") if sys_user else ""
  134. obj = item_svc.create_env_detect_item(
  135. db,
  136. item_code=body.item_code,
  137. item_name=body.item_name,
  138. status=body.status,
  139. tenant_no=tenant_no,
  140. order_num=body.order_num,
  141. remark=body.remark,
  142. user_id=user_id,
  143. create_dept_id=dept_id,
  144. )
  145. return success_response(item_svc._row_to_dict(obj))
  146. except PermissionError as e:
  147. return error_response(403, str(e))
  148. except ValueError as e:
  149. return error_response(400, str(e))
  150. except Exception as e:
  151. return error_response(400, str(e))
  152. @router.put("/env-detect-items/{row_id}")
  153. async def update_env_detect_item(
  154. row_id: str,
  155. body: EnvDetectItemUpdateBody,
  156. db: Session = Depends(get_db),
  157. user: dict = Depends(require_admin_or_super_admin),
  158. ):
  159. try:
  160. tenant_no = _get_tenant_no(db, user)
  161. user_id = _get_current_user_id(user)
  162. obj = item_svc.update_env_detect_item(
  163. db,
  164. row_id,
  165. item_code=body.item_code,
  166. item_name=body.item_name,
  167. status=body.status,
  168. order_num=body.order_num,
  169. remark=body.remark,
  170. user_id=user_id,
  171. tenant_no=tenant_no,
  172. )
  173. return success_response(item_svc._row_to_dict(obj))
  174. except PermissionError as e:
  175. return error_response(403, str(e))
  176. except ValueError as e:
  177. return error_response(400, str(e))
  178. except Exception as e:
  179. return error_response(400, str(e))
  180. @router.delete("/env-detect-items/{row_id}")
  181. async def delete_env_detect_item(
  182. row_id: str,
  183. db: Session = Depends(get_db),
  184. user: dict = Depends(require_admin_or_super_admin),
  185. ):
  186. try:
  187. tenant_no = _get_tenant_no(db, user)
  188. item_svc.soft_delete_env_detect_item(db, row_id, tenant_no=tenant_no)
  189. return success_response(None)
  190. except PermissionError as e:
  191. return error_response(403, str(e))
  192. except ValueError as e:
  193. return error_response(400, str(e))
  194. except Exception as e:
  195. return error_response(400, str(e))
  196. # ---------- 环境检测登记 ----------
  197. def _parse_detect_date(value: Optional[str]) -> Optional[datetime]:
  198. s = (value or "").strip()
  199. if not s:
  200. return None
  201. dt = parse_datetime(s)
  202. if not dt and len(s) >= 10:
  203. dt = parse_datetime(f"{s[:10]} 00:00:00")
  204. return dt
  205. def _parse_detect_date_bound(value: Optional[str], *, end_of_day: bool = False) -> Optional[datetime]:
  206. dt = _parse_detect_date(value)
  207. if not dt:
  208. return None
  209. s = (value or "").strip()
  210. if end_of_day and " " not in s:
  211. return datetime.combine(dt.date(), time(23, 59, 59))
  212. return dt
  213. class EnvDetectRecordBody(BaseModel):
  214. model_config = ConfigDict(populate_by_name=True)
  215. detect_time: str = Field(..., alias="detectTime")
  216. result_time: Optional[str] = Field(None, alias="resultTime")
  217. inspector_ids: List[str] = Field(..., alias="inspectorIds")
  218. env_detect_item_id: str = Field(..., alias="envDetectItemId")
  219. result_desc: str = Field(..., alias="resultDesc")
  220. attachment_urls: Optional[str] = Field(None, alias="attachmentUrls")
  221. remark: Optional[str] = None
  222. class EnvDetectRecordUpdateBody(BaseModel):
  223. model_config = ConfigDict(populate_by_name=True)
  224. detect_time: Optional[str] = Field(None, alias="detectTime")
  225. result_time: Optional[str] = Field(None, alias="resultTime")
  226. inspector_ids: Optional[List[str]] = Field(None, alias="inspectorIds")
  227. env_detect_item_id: Optional[str] = Field(None, alias="envDetectItemId")
  228. result_desc: Optional[str] = Field(None, alias="resultDesc")
  229. attachment_urls: Optional[str] = Field(None, alias="attachmentUrls")
  230. remark: Optional[str] = None
  231. @router.get("/env-detect-records")
  232. async def list_env_detect_records(
  233. page: int = Query(1, ge=1),
  234. pageSize: int = Query(10, ge=1, le=100),
  235. keyword: Optional[str] = None,
  236. envDetectItemId: Optional[str] = None,
  237. inspectorId: Optional[str] = None,
  238. detectTimeStart: Optional[str] = None,
  239. detectTimeEnd: Optional[str] = None,
  240. db: Session = Depends(get_db),
  241. user: dict = Depends(require_menu_code("biz:envDetectRecord:list")),
  242. ):
  243. try:
  244. tenant_no = _get_tenant_no(db, user)
  245. query = db.query(BizEnvDetectRecord).filter(
  246. BizEnvDetectRecord.del_flag == 0,
  247. BizEnvDetectRecord.tenant_no == tenant_no,
  248. )
  249. kw = (keyword or "").strip()
  250. if kw:
  251. from sqlalchemy import or_
  252. like = f"%{kw}%"
  253. query = query.filter(
  254. or_(
  255. BizEnvDetectRecord.record_no.like(like),
  256. BizEnvDetectRecord.inspector_names.like(like),
  257. BizEnvDetectRecord.result_desc.like(like),
  258. )
  259. )
  260. item_id = (envDetectItemId or "").strip()
  261. if item_id:
  262. query = query.filter(BizEnvDetectRecord.env_detect_item_id == item_id)
  263. insp_id = (inspectorId or "").strip()
  264. if insp_id:
  265. from sqlalchemy import func
  266. query = query.filter(func.find_in_set(insp_id, BizEnvDetectRecord.inspector_ids) > 0)
  267. dt_start = _parse_detect_date_bound(detectTimeStart, end_of_day=False)
  268. dt_end = _parse_detect_date_bound(detectTimeEnd, end_of_day=True)
  269. if dt_start:
  270. query = query.filter(BizEnvDetectRecord.detect_time >= dt_start)
  271. if dt_end:
  272. query = query.filter(BizEnvDetectRecord.detect_time <= dt_end)
  273. query = apply_data_scope(query, BizEnvDetectRecord, user, db, tenant_no)
  274. total = query.count()
  275. items = (
  276. query.order_by(BizEnvDetectRecord.create_time.desc())
  277. .offset((page - 1) * pageSize)
  278. .limit(pageSize)
  279. .all()
  280. )
  281. result_list = [record_svc._row_to_dict(r) for r in items]
  282. return success_response(_page_payload(result_list, total, page, pageSize))
  283. except PermissionError as e:
  284. return error_response(403, str(e))
  285. except Exception as e:
  286. return error_response(400, str(e))
  287. @router.get("/env-detect-records/{row_id}")
  288. async def get_env_detect_record(
  289. row_id: str,
  290. db: Session = Depends(get_db),
  291. user: dict = Depends(require_menu_code("biz:envDetectRecord:list")),
  292. ):
  293. try:
  294. tenant_no = _get_tenant_no(db, user)
  295. obj = record_svc.get_env_detect_record(db, row_id, tenant_no=tenant_no)
  296. if not obj:
  297. return error_response(404, "登记记录不存在")
  298. return success_response(record_svc._row_to_dict(obj))
  299. except PermissionError as e:
  300. return error_response(403, str(e))
  301. except Exception as e:
  302. return error_response(400, str(e))
  303. @router.post("/env-detect-records")
  304. async def create_env_detect_record(
  305. body: EnvDetectRecordBody,
  306. db: Session = Depends(get_db),
  307. user: dict = Depends(require_admin_or_super_admin),
  308. ):
  309. try:
  310. tenant_no = _get_tenant_no(db, user)
  311. user_id = _get_current_user_id(user)
  312. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  313. dept_id = (sys_user.dept_id or "") if sys_user else ""
  314. detect_time = _parse_detect_date(body.detect_time)
  315. if not detect_time:
  316. return error_response(400, "检测日期格式无效")
  317. result_time = None
  318. if body.result_time:
  319. result_time = _parse_detect_date(body.result_time)
  320. if not result_time:
  321. return error_response(400, "结果日期格式无效")
  322. obj = record_svc.create_env_detect_record(
  323. db,
  324. detect_time=detect_time,
  325. result_time=result_time,
  326. inspector_ids=body.inspector_ids,
  327. env_detect_item_id=body.env_detect_item_id,
  328. result_desc=body.result_desc,
  329. attachment_urls=body.attachment_urls,
  330. tenant_no=tenant_no,
  331. remark=body.remark,
  332. user_id=user_id,
  333. create_dept_id=dept_id,
  334. )
  335. return success_response(record_svc._row_to_dict(obj))
  336. except PermissionError as e:
  337. return error_response(403, str(e))
  338. except ValueError as e:
  339. return error_response(400, str(e))
  340. except Exception as e:
  341. return error_response(400, str(e))
  342. @router.put("/env-detect-records/{row_id}")
  343. async def update_env_detect_record(
  344. row_id: str,
  345. body: EnvDetectRecordUpdateBody,
  346. db: Session = Depends(get_db),
  347. user: dict = Depends(require_admin_or_super_admin),
  348. ):
  349. try:
  350. tenant_no = _get_tenant_no(db, user)
  351. user_id = _get_current_user_id(user)
  352. detect_time = _parse_detect_date(body.detect_time) if body.detect_time else None
  353. if body.detect_time and not detect_time:
  354. return error_response(400, "检测日期格式无效")
  355. update_result_time = "result_time" in body.model_fields_set
  356. result_time = None
  357. if update_result_time and body.result_time:
  358. result_time = _parse_detect_date(body.result_time)
  359. if not result_time:
  360. return error_response(400, "结果日期格式无效")
  361. obj = record_svc.update_env_detect_record(
  362. db,
  363. row_id,
  364. detect_time=detect_time,
  365. result_time=result_time,
  366. update_result_time=update_result_time,
  367. inspector_ids=body.inspector_ids,
  368. env_detect_item_id=body.env_detect_item_id,
  369. result_desc=body.result_desc,
  370. attachment_urls=body.attachment_urls,
  371. remark=body.remark,
  372. user_id=user_id,
  373. tenant_no=tenant_no,
  374. )
  375. return success_response(record_svc._row_to_dict(obj))
  376. except PermissionError as e:
  377. return error_response(403, str(e))
  378. except ValueError as e:
  379. return error_response(400, str(e))
  380. except Exception as e:
  381. return error_response(400, str(e))
  382. @router.delete("/env-detect-records/{row_id}")
  383. async def delete_env_detect_record(
  384. row_id: str,
  385. db: Session = Depends(get_db),
  386. user: dict = Depends(require_admin_or_super_admin),
  387. ):
  388. try:
  389. tenant_no = _get_tenant_no(db, user)
  390. record_svc.soft_delete_env_detect_record(db, row_id, tenant_no=tenant_no)
  391. return success_response(None)
  392. except PermissionError as e:
  393. return error_response(403, str(e))
  394. except ValueError as e:
  395. return error_response(400, str(e))
  396. except Exception as e:
  397. return error_response(400, str(e))