""" 维修管理业务服务层 """ from typing import List, Optional, Dict, Any from sqlalchemy.orm import Session, selectinload from app.crud.maintenance import maintenance_record from app.crud.asset import asset from app.schemas.maintenance import ( MaintenanceRecordCreate, MaintenanceRecordUpdate, MaintenanceRecordStart, MaintenanceRecordComplete ) from app.core.exceptions import NotFoundException, BusinessException class MaintenanceService: """维修管理服务类""" async def get_record( self, db: Session, record_id: int ) -> Dict[str, Any]: """获取维修记录详情""" # 使用selectinload预加载关联数据,避免N+1查询 from app.models.maintenance import MaintenanceRecord from app.models.asset import Asset from app.models.user import User from app.models.brand_supplier import Supplier obj = db.query( MaintenanceRecord ).options( selectinload(MaintenanceRecord.asset.of_type(Asset)), selectinload(MaintenanceRecord.report_user.of_type(User)), selectinload(MaintenanceRecord.maintenance_user.of_type(User)), selectinload(MaintenanceRecord.vendor.of_type(Supplier)) ).filter( MaintenanceRecord.id == record_id ).first() if not obj: raise NotFoundException("维修记录") return self._load_relations(db, obj) def get_records( self, db: Session, skip: int = 0, limit: int = 20, asset_id: Optional[int] = None, status: Optional[str] = None, fault_type: Optional[str] = None, priority: Optional[str] = None, maintenance_type: Optional[str] = None, keyword: Optional[str] = None ) -> tuple: """获取维修记录列表""" items, total = maintenance_record.get_multi( db=db, skip=skip, limit=limit, asset_id=asset_id, status=status, fault_type=fault_type, priority=priority, maintenance_type=maintenance_type, keyword=keyword ) # 加载关联信息 items_with_relations = [self._load_relations(db, item) for item in items] return items_with_relations, total async def create_record( self, db: Session, obj_in: MaintenanceRecordCreate, report_user_id: int, creator_id: int ): """创建维修记录""" # 验证资产存在 asset_obj = asset.get(db, obj_in.asset_id) if not asset_obj: raise NotFoundException("资产") # 生成维修单号 record_code = await self._generate_record_code(db) # 创建维修记录 db_obj = maintenance_record.create( db=db, obj_in=obj_in, record_code=record_code, asset_code=asset_obj.asset_code, report_user_id=report_user_id, creator_id=creator_id ) # 如果资产状态不是维修中,则更新状态 if asset_obj.status != "maintenance": from app.services.asset_service import asset_service from app.schemas.asset import AssetStatusTransition try: await asset_service.change_asset_status( db=db, asset_id=asset_obj.id, status_transition=AssetStatusTransition( new_status="maintenance", remark=f"报修: {record_code}" ), operator_id=report_user_id ) except Exception as e: # 状态更新失败不影响维修记录创建 pass return self._load_relations(db, db_obj) def update_record( self, db: Session, record_id: int, obj_in: MaintenanceRecordUpdate, updater_id: int ): """更新维修记录""" db_obj = maintenance_record.get(db, record_id) if not db_obj: raise NotFoundException("维修记录") # 已完成的维修记录不能更新 if db_obj.status == "completed": raise BusinessException("已完成的维修记录不能更新") return maintenance_record.update(db, db_obj, obj_in, updater_id) async def start_maintenance( self, db: Session, record_id: int, start_in: MaintenanceRecordStart, maintenance_user_id: int ): """开始维修""" db_obj = maintenance_record.get(db, record_id) if not db_obj: raise NotFoundException("维修记录") # 检查状态 if db_obj.status != "pending": raise BusinessException("只有待处理状态的维修记录可以开始维修") # 验证维修类型 if start_in.maintenance_type == "vendor_repair" and not start_in.vendor_id: raise BusinessException("外部维修必须指定维修供应商") # 开始维修 db_obj = maintenance_record.start_maintenance( db=db, db_obj=db_obj, maintenance_type=start_in.maintenance_type, maintenance_user_id=maintenance_user_id, vendor_id=start_in.vendor_id ) return self._load_relations(db, db_obj) async def complete_maintenance( self, db: Session, record_id: int, complete_in: MaintenanceRecordComplete, maintenance_user_id: int ): """完成维修""" db_obj = maintenance_record.get(db, record_id) if not db_obj: raise NotFoundException("维修记录") # 检查状态 if db_obj.status != "in_progress": raise BusinessException("只有维修中的记录可以完成") # 完成维修 db_obj = maintenance_record.complete_maintenance( db=db, db_obj=db_obj, maintenance_result=complete_in.maintenance_result, maintenance_cost=complete_in.maintenance_cost, replaced_parts=complete_in.replaced_parts, images=complete_in.images ) # 恢复资产状态 from app.services.asset_service import asset_service from app.schemas.asset import AssetStatusTransition try: await asset_service.change_asset_status( db=db, asset_id=db_obj.asset_id, status_transition=AssetStatusTransition( new_status=complete_in.asset_status, remark=f"维修完成: {db_obj.record_code}" ), operator_id=maintenance_user_id ) except Exception as e: # 状态更新失败不影响维修记录完成 pass return self._load_relations(db, db_obj) def cancel_maintenance( self, db: Session, record_id: int ): """取消维修""" db_obj = maintenance_record.get(db, record_id) if not db_obj: raise NotFoundException("维修记录") # 检查状态 if db_obj.status == "completed": raise BusinessException("已完成的维修记录不能取消") # 取消维修 db_obj = maintenance_record.cancel_maintenance(db, db_obj) # 恢复资产状态 asset_obj = asset.get(db, db_obj.asset_id) if asset_obj and asset_obj.status == "maintenance": from app.services.asset_service import asset_service from app.schemas.asset import AssetStatusTransition try: # 根据维修前的状态恢复 target_status = "in_stock" # 默认恢复为库存中 asset_service.change_asset_status( db=db, asset_id=asset_obj.id, status_transition=AssetStatusTransition( new_status=target_status, remark=f"取消维修: {db_obj.record_code}" ), operator_id=db_obj.report_user_id or 0 ) except Exception as e: # 状态更新失败不影响维修记录取消 pass return self._load_relations(db, db_obj) def delete_record( self, db: Session, record_id: int ) -> bool: """删除维修记录""" db_obj = maintenance_record.get(db, record_id) if not db_obj: raise NotFoundException("维修记录") # 只能删除待处理或已取消的记录 if db_obj.status not in ["pending", "cancelled"]: raise BusinessException("只能删除待处理或已取消的维修记录") return maintenance_record.delete(db, record_id) def get_asset_records( self, db: Session, asset_id: int, skip: int = 0, limit: int = 50 ) -> List: """获取资产的维修记录""" # 验证资产存在 if not asset.get(db, asset_id): raise NotFoundException("资产") records = maintenance_record.get_by_asset(db, asset_id, skip, limit) return [self._load_relations(db, record) for record in records] def get_statistics( self, db: Session, asset_id: Optional[int] = None ) -> Dict[str, Any]: """获取维修统计信息""" return maintenance_record.get_statistics(db, asset_id) def _load_relations( self, db: Session, obj ) -> Dict[str, Any]: """加载维修记录关联信息""" from app.models.asset import Asset from app.models.user import User from app.models.brand_supplier import Supplier result = { "id": obj.id, "record_code": obj.record_code, "asset_id": obj.asset_id, "asset_code": obj.asset_code, "fault_description": obj.fault_description, "fault_type": obj.fault_type, "report_user_id": obj.report_user_id, "report_time": obj.report_time, "priority": obj.priority, "maintenance_type": obj.maintenance_type, "vendor_id": obj.vendor_id, "maintenance_cost": float(obj.maintenance_cost) if obj.maintenance_cost else None, "start_time": obj.start_time, "complete_time": obj.complete_time, "maintenance_user_id": obj.maintenance_user_id, "maintenance_result": obj.maintenance_result, "replaced_parts": obj.replaced_parts, "status": obj.status, "images": obj.images, "remark": obj.remark, "created_at": obj.created_at, "updated_at": obj.updated_at } # 加载资产信息 if obj.asset_id: asset_obj = db.query(Asset).filter(Asset.id == obj.asset_id).first() if asset_obj: result["asset"] = { "id": asset_obj.id, "asset_code": asset_obj.asset_code, "asset_name": asset_obj.asset_name, "status": asset_obj.status } # 加载报修人信息 if obj.report_user_id: report_user = db.query(User).filter(User.id == obj.report_user_id).first() if report_user: result["report_user"] = { "id": report_user.id, "real_name": report_user.real_name, "username": report_user.username } # 加载维修人员信息 if obj.maintenance_user_id: maintenance_user = db.query(User).filter(User.id == obj.maintenance_user_id).first() if maintenance_user: result["maintenance_user"] = { "id": maintenance_user.id, "real_name": maintenance_user.real_name, "username": maintenance_user.username } # 加载供应商信息 if obj.vendor_id: vendor = db.query(Supplier).filter(Supplier.id == obj.vendor_id).first() if vendor: result["vendor"] = { "id": vendor.id, "supplier_name": vendor.supplier_name, "contact_person": vendor.contact_person, "contact_phone": vendor.contact_phone } return result async def _generate_record_code(self, db: Session) -> str: """生成维修单号""" from datetime import datetime import random import string # 日期部分 date_str = datetime.now().strftime("%Y%m%d") # 序号部分(4位随机数) sequence = "".join(random.choices(string.digits, k=4)) # 组合单号: MT202501240001 record_code = f"MT{date_str}{sequence}" # 检查是否重复,如果重复则重新生成 while maintenance_record.get_by_code(db, record_code): sequence = "".join(random.choices(string.digits, k=4)) record_code = f"MT{date_str}{sequence}" return record_code # 创建全局实例 maintenance_service = MaintenanceService()