""" 资产分配业务服务层 """ from typing import List, Optional, Dict, Any from sqlalchemy.orm import Session, selectinload from app.crud.allocation import allocation_order, allocation_item from app.crud.asset import asset from app.schemas.allocation import ( AllocationOrderCreate, AllocationOrderUpdate, AllocationOrderApproval ) from app.core.exceptions import NotFoundException, BusinessException class AllocationService: """资产分配服务类""" async def get_order( self, db: Session, order_id: int ) -> Dict[str, Any]: """获取分配单详情""" # 使用selectinload预加载关联数据,避免N+1查询 from app.models.allocation import AllocationOrder from app.models.organization import Organization from app.models.user import User from app.models.allocation import AllocationItem obj = db.query( AllocationOrder ).options( selectinload(AllocationOrder.items), selectinload(AllocationOrder.source_organization.of_type(Organization)), selectinload(AllocationOrder.target_organization.of_type(Organization)), selectinload(AllocationOrder.applicant.of_type(User)), selectinload(AllocationOrder.approver.of_type(User)), selectinload(AllocationOrder.executor.of_type(User)) ).filter( AllocationOrder.id == order_id ).first() if not obj: raise NotFoundException("分配单") # 加载关联信息 return self._load_order_relations(db, obj) def get_orders( self, db: Session, skip: int = 0, limit: int = 20, order_type: Optional[str] = None, approval_status: Optional[str] = None, execute_status: Optional[str] = None, applicant_id: Optional[int] = None, target_organization_id: Optional[int] = None, keyword: Optional[str] = None ) -> tuple: """获取分配单列表""" items, total = allocation_order.get_multi( db=db, skip=skip, limit=limit, order_type=order_type, approval_status=approval_status, execute_status=execute_status, applicant_id=applicant_id, target_organization_id=target_organization_id, keyword=keyword ) # 加载关联信息 items_with_relations = [self._load_order_relations(db, item) for item in items] return items_with_relations, total async def create_order( self, db: Session, obj_in: AllocationOrderCreate, applicant_id: int ): """创建分配单""" # 验证资产存在性和状态 assets = [] for asset_id in obj_in.asset_ids: asset_obj = asset.get(db, asset_id) if not asset_obj: raise NotFoundException(f"资产ID {asset_id}") assets.append(asset_obj) # 验证资产状态是否允许分配 for asset_obj in assets: if not self._can_allocate(asset_obj.status, obj_in.order_type): raise BusinessException( f"资产 {asset_obj.asset_code} 当前状态为 {asset_obj.status},不允许{self._get_order_type_name(obj_in.order_type)}操作" ) # 生成分配单号 order_code = await self._generate_order_code(db, obj_in.order_type) # 创建分配单 db_obj = allocation_order.create( db=db, obj_in=obj_in, order_code=order_code, applicant_id=applicant_id ) return self._load_order_relations(db, db_obj) def update_order( self, db: Session, order_id: int, obj_in: AllocationOrderUpdate, updater_id: int ): """更新分配单""" db_obj = allocation_order.get(db, order_id) if not db_obj: raise NotFoundException("分配单") # 只有草稿或待审批状态可以更新 if db_obj.approval_status not in ["pending", "draft"]: raise BusinessException("只有待审批状态的分配单可以更新") return allocation_order.update(db, db_obj, obj_in, updater_id) async def approve_order( self, db: Session, order_id: int, approval_in: AllocationOrderApproval, approver_id: int ): """审批分配单""" db_obj = allocation_order.get(db, order_id) if not db_obj: raise NotFoundException("分配单") # 检查状态 if db_obj.approval_status != "pending": raise BusinessException("该分配单已审批,无法重复审批") # 审批 db_obj = allocation_order.approve( db=db, db_obj=db_obj, approval_status=approval_in.approval_status, approver_id=approver_id, approval_remark=approval_in.approval_remark ) # 如果审批通过,执行分配逻辑 if approval_in.approval_status == "approved": await self._execute_allocation_logic(db, db_obj) return self._load_order_relations(db, db_obj) async def execute_order( self, db: Session, order_id: int, executor_id: int ): """执行分配单""" db_obj = allocation_order.get(db, order_id) if not db_obj: raise NotFoundException("分配单") # 检查状态 if db_obj.approval_status != "approved": raise BusinessException("该分配单未审批通过,无法执行") if db_obj.execute_status == "completed": raise BusinessException("该分配单已执行完成") # 执行分配单 db_obj = allocation_order.execute(db, db_obj, executor_id) return self._load_order_relations(db, db_obj) def cancel_order( self, db: Session, order_id: int ) -> bool: """取消分配单""" db_obj = allocation_order.get(db, order_id) if not db_obj: raise NotFoundException("分配单") # 检查状态 if db_obj.execute_status == "completed": raise BusinessException("已完成的分配单无法取消") allocation_order.cancel(db, db_obj) return True def delete_order( self, db: Session, order_id: int ) -> bool: """删除分配单""" db_obj = allocation_order.get(db, order_id) if not db_obj: raise NotFoundException("分配单") # 只有草稿或已取消的可以删除 if db_obj.approval_status not in ["draft", "rejected", "cancelled"]: raise BusinessException("只能删除草稿、已拒绝或已取消的分配单") return allocation_order.delete(db, order_id) def get_order_items( self, db: Session, order_id: int ) -> List: """获取分配单明细""" # 验证分配单存在 if not allocation_order.get(db, order_id): raise NotFoundException("分配单") return allocation_item.get_by_order(db, order_id) def get_statistics( self, db: Session, applicant_id: Optional[int] = None ) -> Dict[str, int]: """获取分配单统计信息""" return allocation_order.get_statistics(db, applicant_id) async def _execute_allocation_logic( self, db: Session, order_obj ): """执行分配逻辑(审批通过后自动执行)""" # 根据单据类型执行不同的逻辑 if order_obj.order_type == "allocation": await self._execute_allocation(db, order_obj) elif order_obj.order_type == "transfer": await self._execute_transfer(db, order_obj) elif order_obj.order_type == "recovery": await self._execute_recovery(db, order_obj) elif order_obj.order_type == "maintenance": await self._execute_maintenance_allocation(db, order_obj) elif order_obj.order_type == "scrap": await self._execute_scrap_allocation(db, order_obj) async def _execute_allocation(self, db: Session, order_obj): """执行资产分配""" # 更新明细状态为执行中 allocation_item.batch_update_execute_status(db, order_obj.id, "executing") # 获取明细 items = allocation_item.get_by_order(db, order_obj.id) # 更新资产状态 from app.services.asset_service import asset_service from app.schemas.asset import AssetStatusTransition for item in items: try: # 变更资产状态 await asset_service.change_asset_status( db=db, asset_id=item.asset_id, status_transition=AssetStatusTransition( new_status=item.to_status, remark=f"分配单: {order_obj.order_code}" ), operator_id=order_obj.applicant_id ) # 更新明细状态为完成 allocation_item.update_execute_status(db, item.id, "completed") except Exception as e: # 更新明细状态为失败 allocation_item.update_execute_status( db, item.id, "failed", failure_reason=str(e) ) async def _execute_transfer(self, db: Session, order_obj): """执行资产调拨""" # 调拨逻辑与分配类似,但需要记录调出和调入网点 await self._execute_allocation(db, order_obj) async def _execute_recovery(self, db: Session, order_obj): """执行资产回收""" # 回收逻辑 await self._execute_allocation(db, order_obj) async def _execute_maintenance_allocation(self, db: Session, order_obj): """执行维修分配""" # 维修分配逻辑 await self._execute_allocation(db, order_obj) async def _execute_scrap_allocation(self, db: Session, order_obj): """执行报废分配""" # 报废分配逻辑 await self._execute_allocation(db, order_obj) def _load_order_relations( self, db: Session, obj ) -> Dict[str, Any]: """加载分配单关联信息""" from app.models.user import User from app.models.organization import Organization result = { "id": obj.id, "order_code": obj.order_code, "order_type": obj.order_type, "title": obj.title, "source_organization_id": obj.source_organization_id, "target_organization_id": obj.target_organization_id, "applicant_id": obj.applicant_id, "approver_id": obj.approver_id, "approval_status": obj.approval_status, "approval_time": obj.approval_time, "approval_remark": obj.approval_remark, "expect_execute_date": obj.expect_execute_date, "actual_execute_date": obj.actual_execute_date, "executor_id": obj.executor_id, "execute_status": obj.execute_status, "remark": obj.remark, "created_at": obj.created_at, "updated_at": obj.updated_at } # 加载关联信息 if obj.source_organization_id: source_org = db.query(Organization).filter( Organization.id == obj.source_organization_id ).first() if source_org: result["source_organization"] = { "id": source_org.id, "org_name": source_org.org_name, "org_type": source_org.org_type } if obj.target_organization_id: target_org = db.query(Organization).filter( Organization.id == obj.target_organization_id ).first() if target_org: result["target_organization"] = { "id": target_org.id, "org_name": target_org.org_name, "org_type": target_org.org_type } if obj.applicant_id: applicant = db.query(User).filter(User.id == obj.applicant_id).first() if applicant: result["applicant"] = { "id": applicant.id, "real_name": applicant.real_name, "username": applicant.username } if obj.approver_id: approver = db.query(User).filter(User.id == obj.approver_id).first() if approver: result["approver"] = { "id": approver.id, "real_name": approver.real_name, "username": approver.username } if obj.executor_id: executor = db.query(User).filter(User.id == obj.executor_id).first() if executor: result["executor"] = { "id": executor.id, "real_name": executor.real_name, "username": executor.username } # 加载明细 items = allocation_item.get_by_order(db, obj.id) result["items"] = [ { "id": item.id, "asset_id": item.asset_id, "asset_code": item.asset_code, "asset_name": item.asset_name, "from_status": item.from_status, "to_status": item.to_status, "execute_status": item.execute_status, "failure_reason": item.failure_reason } for item in items ] return result def _can_allocate(self, asset_status: str, order_type: str) -> bool: """判断资产是否可以分配""" # 库存中或使用中的资产可以分配 if order_type in ["allocation", "transfer"]: return asset_status in ["in_stock", "in_use"] elif order_type == "recovery": return asset_status == "in_use" elif order_type == "maintenance": return asset_status in ["in_stock", "in_use"] elif order_type == "scrap": return asset_status in ["in_stock", "in_use", "maintenance"] return False def _get_order_type_name(self, order_type: str) -> str: """获取单据类型中文名""" type_names = { "allocation": "分配", "transfer": "调拨", "recovery": "回收", "maintenance": "维修", "scrap": "报废" } return type_names.get(order_type, "操作") async def _generate_order_code(self, db: Session, order_type: str) -> str: """生成分配单号""" from datetime import datetime import random import string # 单据类型前缀 prefix_map = { "allocation": "AL", "transfer": "TF", "recovery": "RC", "maintenance": "MT", "scrap": "SC" } prefix = prefix_map.get(order_type, "AL") # 日期部分 date_str = datetime.now().strftime("%Y%m%d") # 序号部分(4位随机数) sequence = "".join(random.choices(string.digits, k=4)) # 组合单号: AL202501240001 order_code = f"{prefix}{date_str}{sequence}" # 检查是否重复,如果重复则重新生成 while allocation_order.get_by_code(db, order_code): sequence = "".join(random.choices(string.digits, k=4)) order_code = f"{prefix}{date_str}{sequence}" return order_code # 创建全局实例 allocation_service = AllocationService()