"""洁净服管理 API""" 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.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.crud import cleanroom_garment as garment_svc from app.models.models import 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 class GarmentCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_code: str = Field(..., alias="garmentCode") garment_name: str = Field(..., alias="garmentName") wash_discard_limit: int = Field(..., alias="washDiscardLimit") sterilize_discard_limit: int = Field(..., alias="sterilizeDiscardLimit") inbound_date: str = Field(..., alias="inboundDate") order_num: Optional[int] = Field(None, alias="orderNum") remark: Optional[str] = None class GarmentUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_code: Optional[str] = Field(None, alias="garmentCode") garment_name: Optional[str] = Field(None, alias="garmentName") wash_discard_limit: Optional[int] = Field(None, alias="washDiscardLimit") sterilize_discard_limit: Optional[int] = Field(None, alias="sterilizeDiscardLimit") inbound_date: Optional[str] = Field(None, alias="inboundDate") order_num: Optional[int] = Field(None, alias="orderNum") remark: Optional[str] = None class GarmentBatchDeleteBody(BaseModel): model_config = ConfigDict(populate_by_name=True) ids: List[str] = Field(..., alias="ids") class GarmentDiscardBody(BaseModel): model_config = ConfigDict(populate_by_name=True) discard_date: Optional[str] = Field(None, alias="discardDate") remark: Optional[str] = None class GarmentBatchDiscardBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_ids: List[str] = Field(..., alias="garmentIds") discard_date: Optional[str] = Field(None, alias="discardDate") remark: Optional[str] = None class WashLogCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_id: str = Field(..., alias="garmentId") wash_time: str = Field(..., alias="washTime") operator_user_id: Optional[str] = Field(None, alias="operatorUserId") operator_name: Optional[str] = Field(None, alias="operatorName") order_num: Optional[int] = Field(0, alias="orderNum") remark: Optional[str] = None class WashLogUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) wash_time: Optional[str] = Field(None, alias="washTime") operator_user_id: Optional[str] = Field(None, alias="operatorUserId") operator_name: Optional[str] = Field(None, alias="operatorName") order_num: Optional[int] = Field(None, alias="orderNum") remark: Optional[str] = None class WashLogBatchBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_ids: List[str] = Field(..., alias="garmentIds") wash_time: str = Field(..., alias="washTime") operator_user_id: Optional[str] = Field(None, alias="operatorUserId") operator_name: Optional[str] = Field(None, alias="operatorName") remark: Optional[str] = None class SterilizeLogCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_id: str = Field(..., alias="garmentId") sterilize_time: str = Field(..., alias="sterilizeTime") operator_user_id: Optional[str] = Field(None, alias="operatorUserId") operator_name: Optional[str] = Field(None, alias="operatorName") order_num: Optional[int] = Field(0, alias="orderNum") remark: Optional[str] = None class SterilizeLogUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) sterilize_time: Optional[str] = Field(None, alias="sterilizeTime") operator_user_id: Optional[str] = Field(None, alias="operatorUserId") operator_name: Optional[str] = Field(None, alias="operatorName") order_num: Optional[int] = Field(None, alias="orderNum") remark: Optional[str] = None class SterilizeLogBatchBody(BaseModel): model_config = ConfigDict(populate_by_name=True) garment_ids: List[str] = Field(..., alias="garmentIds") sterilize_time: str = Field(..., alias="sterilizeTime") operator_user_id: Optional[str] = Field(None, alias="operatorUserId") operator_name: Optional[str] = Field(None, alias="operatorName") remark: Optional[str] = None @router.get("/cleanroom-garments") async def list_cleanroom_garments( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), keyword: Optional[str] = None, garmentStatus: Optional[str] = Query(None, alias="garmentStatus"), needAttention: Optional[str] = Query(None, alias="needAttention"), user: dict = Depends(require_menu_code("production:cleanroom-garment:list")), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) items, total = garment_svc.list_garments( db, page=page, page_size=pageSize, keyword=keyword, garment_status=garmentStatus, need_attention=needAttention in ("1", "true", "True"), tenant_no=tenant_no, user_info=user, ) tp = garment_svc.total_pages(total, pageSize) return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}) @router.get("/cleanroom-garments/{garment_id}") async def get_cleanroom_garment( garment_id: str, user: dict = Depends(require_menu_code("production:cleanroom-garment:list")), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) detail = garment_svc.get_garment_detail(db, garment_id, tenant_no=tenant_no) if not detail: return error_response(404, "洁净服不存在") return success_response(detail) @router.post("/cleanroom-garments") async def create_cleanroom_garment( body: GarmentCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) obj = garment_svc.create_garment( db, garment_code=body.garment_code, garment_name=body.garment_name, wash_discard_limit=body.wash_discard_limit, sterilize_discard_limit=body.sterilize_discard_limit, inbound_date=body.inbound_date, tenant_no=tenant_no, order_num=body.order_num, remark=body.remark, user_id=user.get("user_id"), ) return success_response(garment_svc.get_garment_detail(db, obj.id, tenant_no=tenant_no), message="创建成功") except ValueError as e: return error_response(400, str(e)) @router.put("/cleanroom-garments/{garment_id}") async def update_cleanroom_garment( garment_id: str, body: GarmentUpdateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) payload = body.model_dump(exclude_unset=True, by_alias=False) obj = garment_svc.update_garment( db, garment_id, garment_code=payload.get("garment_code"), garment_name=payload.get("garment_name"), wash_discard_limit=payload.get("wash_discard_limit"), sterilize_discard_limit=payload.get("sterilize_discard_limit"), inbound_date=payload.get("inbound_date"), order_num=payload.get("order_num"), remark=payload.get("remark"), user_id=user.get("user_id"), tenant_no=tenant_no, ) return success_response(garment_svc.get_garment_detail(db, obj.id, tenant_no=tenant_no), message="更新成功") except ValueError as e: return error_response(400, str(e)) @router.post("/cleanroom-garments/batch-delete") async def batch_delete_cleanroom_garments( body: GarmentBatchDeleteBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) count = garment_svc.batch_soft_delete_garments(db, body.ids, tenant_no=tenant_no) return success_response({"deletedCount": count}, message="删除成功") except ValueError as e: return error_response(400, str(e)) @router.delete("/cleanroom-garments/{garment_id}") async def delete_cleanroom_garment( garment_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) garment_svc.soft_delete_garment(db, garment_id, tenant_no=tenant_no) return success_response(None, message="删除成功") except ValueError as e: return error_response(400, str(e)) @router.post("/cleanroom-garments/batch-discard") async def batch_discard_cleanroom_garments( body: GarmentBatchDiscardBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = garment_svc.batch_discard_garments( db, body.garment_ids, discard_date=body.discard_date, remark=body.remark, user_id=user.get("user_id"), tenant_no=tenant_no, ) return success_response(result, message="废弃成功") except ValueError as e: return error_response(400, str(e)) @router.post("/cleanroom-garments/{garment_id}/discard") async def discard_cleanroom_garment( garment_id: str, body: GarmentDiscardBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) obj = garment_svc.discard_garment( db, garment_id, discard_date=body.discard_date, remark=body.remark, user_id=user.get("user_id"), tenant_no=tenant_no, ) return success_response(garment_svc.get_garment_detail(db, obj.id, tenant_no=tenant_no), message="废弃成功") except ValueError as e: return error_response(400, str(e)) @router.get("/cleanroom-garment-wash-logs") async def list_wash_logs( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), garmentId: str = Query(..., alias="garmentId"), user: dict = Depends(require_menu_code("production:cleanroom-garment:list")), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) items, total = garment_svc.list_wash_logs( db, garment_id=garmentId, page=page, page_size=pageSize, tenant_no=tenant_no ) tp = garment_svc.total_pages(total, pageSize) return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}) @router.post("/cleanroom-garment-wash-logs") async def create_wash_log( body: WashLogCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) obj = garment_svc.create_wash_log( db, garment_id=body.garment_id, wash_time=body.wash_time, operator_user_id=body.operator_user_id, operator_name=body.operator_name, tenant_no=tenant_no, order_num=body.order_num, remark=body.remark, user_id=user.get("user_id"), ) return success_response(garment_svc._wash_log_to_dict(obj), message="创建成功") except ValueError as e: return error_response(400, str(e)) @router.post("/cleanroom-garment-wash-logs/batch") async def batch_create_wash_logs( body: WashLogBatchBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = garment_svc.batch_create_wash_logs( db, garment_ids=body.garment_ids, wash_time=body.wash_time, operator_user_id=body.operator_user_id, operator_name=body.operator_name, remark=body.remark, tenant_no=tenant_no, user_id=user.get("user_id"), ) return success_response(result, message="批量清洗登记成功") except ValueError as e: return error_response(400, str(e)) @router.put("/cleanroom-garment-wash-logs/{log_id}") async def update_wash_log( log_id: str, body: WashLogUpdateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) payload = body.model_dump(exclude_unset=True, by_alias=False) obj = garment_svc.update_wash_log( db, log_id, wash_time=payload.get("wash_time"), operator_user_id=payload.get("operator_user_id"), operator_name=payload.get("operator_name"), order_num=payload.get("order_num"), remark=payload.get("remark"), user_id=user.get("user_id"), tenant_no=tenant_no, ) return success_response(garment_svc._wash_log_to_dict(obj), message="更新成功") except ValueError as e: return error_response(400, str(e)) @router.delete("/cleanroom-garment-wash-logs/{log_id}") async def delete_wash_log( log_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) garment_svc.soft_delete_wash_log(db, log_id, tenant_no=tenant_no) return success_response(None, message="删除成功") except ValueError as e: return error_response(400, str(e)) @router.get("/cleanroom-garment-sterilize-logs") async def list_sterilize_logs( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), garmentId: str = Query(..., alias="garmentId"), user: dict = Depends(require_menu_code("production:cleanroom-garment:list")), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) items, total = garment_svc.list_sterilize_logs( db, garment_id=garmentId, page=page, page_size=pageSize, tenant_no=tenant_no ) tp = garment_svc.total_pages(total, pageSize) return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp}) @router.post("/cleanroom-garment-sterilize-logs") async def create_sterilize_log( body: SterilizeLogCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) obj = garment_svc.create_sterilize_log( db, garment_id=body.garment_id, sterilize_time=body.sterilize_time, operator_user_id=body.operator_user_id, operator_name=body.operator_name, tenant_no=tenant_no, order_num=body.order_num, remark=body.remark, user_id=user.get("user_id"), ) return success_response(garment_svc._sterilize_log_to_dict(obj), message="创建成功") except ValueError as e: return error_response(400, str(e)) @router.post("/cleanroom-garment-sterilize-logs/batch") async def batch_create_sterilize_logs( body: SterilizeLogBatchBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = garment_svc.batch_create_sterilize_logs( db, garment_ids=body.garment_ids, sterilize_time=body.sterilize_time, operator_user_id=body.operator_user_id, operator_name=body.operator_name, remark=body.remark, tenant_no=tenant_no, user_id=user.get("user_id"), ) return success_response(result, message="批量灭菌登记成功") except ValueError as e: return error_response(400, str(e)) @router.put("/cleanroom-garment-sterilize-logs/{log_id}") async def update_sterilize_log( log_id: str, body: SterilizeLogUpdateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) payload = body.model_dump(exclude_unset=True, by_alias=False) obj = garment_svc.update_sterilize_log( db, log_id, sterilize_time=payload.get("sterilize_time"), operator_user_id=payload.get("operator_user_id"), operator_name=payload.get("operator_name"), order_num=payload.get("order_num"), remark=payload.get("remark"), user_id=user.get("user_id"), tenant_no=tenant_no, ) return success_response(garment_svc._sterilize_log_to_dict(obj), message="更新成功") except ValueError as e: return error_response(400, str(e)) @router.delete("/cleanroom-garment-sterilize-logs/{log_id}") async def delete_sterilize_log( log_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) garment_svc.soft_delete_sterilize_log(db, log_id, tenant_no=tenant_no) return success_response(None, message="删除成功") except ValueError as e: return error_response(400, str(e))