"""环境检测 — 检测项维护 + 登记管理 API""" from datetime import datetime, time from typing import List, Optional from fastapi import APIRouter, Depends, Query from pydantic import BaseModel, ConfigDict, Field from sqlalchemy.orm import Session from app.core.data_scope import apply_data_scope from app.core.database import get_db from app.core.menu_guard import require_admin_or_super_admin, require_menu_code from app.core.response import error_response, success_response from app.core.utils import parse_datetime from app.crud import env_detect_item as item_svc from app.crud import env_detect_record as record_svc from app.crud.collection_protocol import total_pages from app.models.models import BizEnvDetectItem, BizEnvDetectRecord, SysUser router = APIRouter(prefix="/web/admin", tags=["环境检测"]) def _get_tenant_no(db: Session, user: dict) -> str: user_id = user.get("user_id") if not user_id: raise PermissionError("用户身份信息缺失,无法确定租户") sys_user = db.query(SysUser).filter(SysUser.id == user_id).first() if not sys_user or not sys_user.tenant_no: raise PermissionError("用户租户信息缺失") return sys_user.tenant_no def _get_current_user_id(user: dict) -> str: user_id = user.get("user_id") if not user_id: raise PermissionError("用户身份信息缺失") return str(user_id) def _page_payload(items: list, total: int, page: int, page_size: int) -> dict: return { "list": items, "total": total, "page": page, "pageSize": page_size, "totalPages": total_pages(total, page_size), } # ---------- 环境检测项 ---------- class EnvDetectItemBody(BaseModel): model_config = ConfigDict(populate_by_name=True) item_code: str = Field(..., alias="itemCode") item_name: str = Field(..., alias="itemName") status: str = Field("1", alias="status") order_num: Optional[int] = Field(None, alias="orderNum") remark: Optional[str] = None class EnvDetectItemUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) item_code: Optional[str] = Field(None, alias="itemCode") item_name: Optional[str] = Field(None, alias="itemName") status: Optional[str] = None order_num: Optional[int] = Field(None, alias="orderNum") remark: Optional[str] = None @router.get("/env-detect-items") async def list_env_detect_items( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100), keyword: Optional[str] = None, status: Optional[str] = None, db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:envDetectItem:list")), ): try: tenant_no = _get_tenant_no(db, user) query = db.query(BizEnvDetectItem).filter( BizEnvDetectItem.del_flag == 0, BizEnvDetectItem.tenant_no == tenant_no, ) kw = (keyword or "").strip() if kw: from sqlalchemy import or_ like = f"%{kw}%" query = query.filter( or_( BizEnvDetectItem.item_code.like(like), BizEnvDetectItem.item_name.like(like), ) ) if status: query = query.filter(BizEnvDetectItem.status == status) query = apply_data_scope(query, BizEnvDetectItem, user, db, tenant_no) total = query.count() items = ( query.order_by(BizEnvDetectItem.create_time.desc()) .offset((page - 1) * pageSize) .limit(pageSize) .all() ) result_list = [item_svc._row_to_dict(r) for r in items] return success_response(_page_payload(result_list, total, page, pageSize)) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.get("/env-detect-items/active") async def list_active_env_detect_items( db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:envDetectRecord:list")), ): try: tenant_no = _get_tenant_no(db, user) items = item_svc.list_active_env_detect_items(db, tenant_no) return success_response(items) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.get("/env-detect-items/{row_id}") async def get_env_detect_item( row_id: str, db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:envDetectItem:list")), ): try: tenant_no = _get_tenant_no(db, user) obj = item_svc.get_env_detect_item(db, row_id, tenant_no=tenant_no) if not obj: return error_response(404, "检测项不存在") return success_response(item_svc._row_to_dict(obj)) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.post("/env-detect-items") async def create_env_detect_item( body: EnvDetectItemBody, db: Session = Depends(get_db), user: dict = Depends(require_admin_or_super_admin), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) sys_user = db.query(SysUser).filter(SysUser.id == user_id).first() dept_id = (sys_user.dept_id or "") if sys_user else "" obj = item_svc.create_env_detect_item( db, item_code=body.item_code, item_name=body.item_name, status=body.status, tenant_no=tenant_no, order_num=body.order_num, remark=body.remark, user_id=user_id, create_dept_id=dept_id, ) return success_response(item_svc._row_to_dict(obj)) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(400, str(e)) @router.put("/env-detect-items/{row_id}") async def update_env_detect_item( row_id: str, body: EnvDetectItemUpdateBody, db: Session = Depends(get_db), user: dict = Depends(require_admin_or_super_admin), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) obj = item_svc.update_env_detect_item( db, row_id, item_code=body.item_code, item_name=body.item_name, status=body.status, order_num=body.order_num, remark=body.remark, user_id=user_id, tenant_no=tenant_no, ) return success_response(item_svc._row_to_dict(obj)) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(400, str(e)) @router.delete("/env-detect-items/{row_id}") async def delete_env_detect_item( row_id: str, db: Session = Depends(get_db), user: dict = Depends(require_admin_or_super_admin), ): try: tenant_no = _get_tenant_no(db, user) item_svc.soft_delete_env_detect_item(db, row_id, tenant_no=tenant_no) return success_response(None) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(400, str(e)) # ---------- 环境检测登记 ---------- def _parse_detect_date(value: Optional[str]) -> Optional[datetime]: s = (value or "").strip() if not s: return None dt = parse_datetime(s) if not dt and len(s) >= 10: dt = parse_datetime(f"{s[:10]} 00:00:00") return dt def _parse_detect_date_bound(value: Optional[str], *, end_of_day: bool = False) -> Optional[datetime]: dt = _parse_detect_date(value) if not dt: return None s = (value or "").strip() if end_of_day and " " not in s: return datetime.combine(dt.date(), time(23, 59, 59)) return dt class EnvDetectRecordBody(BaseModel): model_config = ConfigDict(populate_by_name=True) detect_time: str = Field(..., alias="detectTime") result_time: Optional[str] = Field(None, alias="resultTime") inspector_ids: List[str] = Field(..., alias="inspectorIds") env_detect_item_id: str = Field(..., alias="envDetectItemId") result_desc: str = Field(..., alias="resultDesc") attachment_urls: Optional[str] = Field(None, alias="attachmentUrls") remark: Optional[str] = None class EnvDetectRecordUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) detect_time: Optional[str] = Field(None, alias="detectTime") result_time: Optional[str] = Field(None, alias="resultTime") inspector_ids: Optional[List[str]] = Field(None, alias="inspectorIds") env_detect_item_id: Optional[str] = Field(None, alias="envDetectItemId") result_desc: Optional[str] = Field(None, alias="resultDesc") attachment_urls: Optional[str] = Field(None, alias="attachmentUrls") remark: Optional[str] = None @router.get("/env-detect-records") async def list_env_detect_records( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100), keyword: Optional[str] = None, envDetectItemId: Optional[str] = None, inspectorId: Optional[str] = None, detectTimeStart: Optional[str] = None, detectTimeEnd: Optional[str] = None, db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:envDetectRecord:list")), ): try: tenant_no = _get_tenant_no(db, user) query = db.query(BizEnvDetectRecord).filter( BizEnvDetectRecord.del_flag == 0, BizEnvDetectRecord.tenant_no == tenant_no, ) kw = (keyword or "").strip() if kw: from sqlalchemy import or_ like = f"%{kw}%" query = query.filter( or_( BizEnvDetectRecord.record_no.like(like), BizEnvDetectRecord.inspector_names.like(like), BizEnvDetectRecord.result_desc.like(like), ) ) item_id = (envDetectItemId or "").strip() if item_id: query = query.filter(BizEnvDetectRecord.env_detect_item_id == item_id) insp_id = (inspectorId or "").strip() if insp_id: from sqlalchemy import func query = query.filter(func.find_in_set(insp_id, BizEnvDetectRecord.inspector_ids) > 0) dt_start = _parse_detect_date_bound(detectTimeStart, end_of_day=False) dt_end = _parse_detect_date_bound(detectTimeEnd, end_of_day=True) if dt_start: query = query.filter(BizEnvDetectRecord.detect_time >= dt_start) if dt_end: query = query.filter(BizEnvDetectRecord.detect_time <= dt_end) query = apply_data_scope(query, BizEnvDetectRecord, user, db, tenant_no) total = query.count() items = ( query.order_by(BizEnvDetectRecord.create_time.desc()) .offset((page - 1) * pageSize) .limit(pageSize) .all() ) result_list = [record_svc._row_to_dict(r) for r in items] return success_response(_page_payload(result_list, total, page, pageSize)) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.get("/env-detect-records/{row_id}") async def get_env_detect_record( row_id: str, db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:envDetectRecord:list")), ): try: tenant_no = _get_tenant_no(db, user) obj = record_svc.get_env_detect_record(db, row_id, tenant_no=tenant_no) if not obj: return error_response(404, "登记记录不存在") return success_response(record_svc._row_to_dict(obj)) except PermissionError as e: return error_response(403, str(e)) except Exception as e: return error_response(400, str(e)) @router.post("/env-detect-records") async def create_env_detect_record( body: EnvDetectRecordBody, db: Session = Depends(get_db), user: dict = Depends(require_admin_or_super_admin), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) sys_user = db.query(SysUser).filter(SysUser.id == user_id).first() dept_id = (sys_user.dept_id or "") if sys_user else "" detect_time = _parse_detect_date(body.detect_time) if not detect_time: return error_response(400, "检测日期格式无效") result_time = None if body.result_time: result_time = _parse_detect_date(body.result_time) if not result_time: return error_response(400, "结果日期格式无效") obj = record_svc.create_env_detect_record( db, detect_time=detect_time, result_time=result_time, inspector_ids=body.inspector_ids, env_detect_item_id=body.env_detect_item_id, result_desc=body.result_desc, attachment_urls=body.attachment_urls, tenant_no=tenant_no, remark=body.remark, user_id=user_id, create_dept_id=dept_id, ) return success_response(record_svc._row_to_dict(obj)) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(400, str(e)) @router.put("/env-detect-records/{row_id}") async def update_env_detect_record( row_id: str, body: EnvDetectRecordUpdateBody, db: Session = Depends(get_db), user: dict = Depends(require_admin_or_super_admin), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) detect_time = _parse_detect_date(body.detect_time) if body.detect_time else None if body.detect_time and not detect_time: return error_response(400, "检测日期格式无效") update_result_time = "result_time" in body.model_fields_set result_time = None if update_result_time and body.result_time: result_time = _parse_detect_date(body.result_time) if not result_time: return error_response(400, "结果日期格式无效") obj = record_svc.update_env_detect_record( db, row_id, detect_time=detect_time, result_time=result_time, update_result_time=update_result_time, inspector_ids=body.inspector_ids, env_detect_item_id=body.env_detect_item_id, result_desc=body.result_desc, attachment_urls=body.attachment_urls, remark=body.remark, user_id=user_id, tenant_no=tenant_no, ) return success_response(record_svc._row_to_dict(obj)) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(400, str(e)) @router.delete("/env-detect-records/{row_id}") async def delete_env_detect_record( row_id: str, db: Session = Depends(get_db), user: dict = Depends(require_admin_or_super_admin), ): try: tenant_no = _get_tenant_no(db, user) record_svc.soft_delete_env_detect_record(db, row_id, tenant_no=tenant_no) return success_response(None) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(400, str(e))