""" 异常处理 API 提供异常的查询、更新、批量操作等接口 """ from typing import Any, Dict, List, Optional from fastapi import APIRouter, Depends, HTTPException, Query, Body from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.core.tenant import get_current_company_id from app.models.exception_item import ExceptionItem from app.services.exception_service import ( get_exceptions, update_exception, batch_resolve_exceptions, ExceptionService, ) router = APIRouter(prefix="/api/exceptions", tags=["异常处理"]) @router.get("/") async def list_exceptions( task_id: Optional[int] = Query(None, description="任务ID"), status: Optional[str] = Query(None, description="状态"), severity: Optional[str] = Query(None, description="严重程度"), exception_type: Optional[str] = Query(None, description="异常类型"), page: int = Query(1, ge=1, description="页码"), page_size: int = Query(20, ge=1, le=100, description="每页数量"), db: AsyncSession = Depends(get_db), company_id: int = Depends(get_current_company_id), ) -> Dict[str, Any]: """ 查询异常列表 支持按任务、状态、严重程度、类型筛选 """ exceptions, total = await get_exceptions( db=db, task_id=task_id, company_id=company_id, status=status, severity=severity, exception_type=exception_type, page=page, page_size=page_size, ) return { "items": [ { "id": e.id, "task_id": e.task_id, "exception_type": e.exception_type, "severity": e.severity.lower() if e.severity else None, "status": e.status.lower() if e.status else None, "employee_id": e.employee_id, "employee_name": e.employee_name, "description": e.description, "detail": e.detail, "salary_amount": e.salary_amount, "social_security_amount": e.social_security_amount, "tax_amount": e.tax_amount, "bank_amount": e.bank_amount, "difference_amount": e.difference_amount, "suggested_action": e.suggested_action, "ai_suggestion": e.ai_suggestion, "handling_note": e.handling_note, "handled_by": e.handled_by, "handler_name": e.handler.full_name if e.handler else None, "handled_at": e.handled_at.isoformat() if e.handled_at else None, "created_at": e.created_at.isoformat() if e.created_at else None, "updated_at": e.updated_at.isoformat() if e.updated_at else None, } for e in exceptions ], "total": total, "page": page, "page_size": page_size, } @router.get("/summary") async def get_exception_summary( task_id: Optional[int] = Query(None, description="任务ID"), db: AsyncSession = Depends(get_db), company_id: int = Depends(get_current_company_id), ) -> Dict[str, Any]: """获取异常汇总统计""" service = ExceptionService(db) summary = await service.get_exception_summary( task_id=task_id, company_id=company_id, ) return summary @router.get("/{exception_id}") async def get_exception_detail( exception_id: int, db: AsyncSession = Depends(get_db), company_id: int = Depends(get_current_company_id), ) -> Dict[str, Any]: """获取异常详情""" service = ExceptionService(db) exception = await service.get_exception(exception_id) if not exception: raise HTTPException(status_code=404, detail="异常不存在") if exception.company_id != company_id: raise HTTPException(status_code=403, detail="无权访问该异常") return { "id": exception.id, "task_id": exception.task_id, "exception_type": exception.exception_type, "severity": exception.severity, "status": exception.status, "employee_id": exception.employee_id, "employee_name": exception.employee_name, "description": exception.description, "detail": exception.detail, "salary_amount": exception.salary_amount, "social_security_amount": exception.social_security_amount, "tax_amount": exception.tax_amount, "bank_amount": exception.bank_amount, "difference_amount": exception.difference_amount, "suggested_action": exception.suggested_action, "ai_suggestion": exception.ai_suggestion, "handling_note": exception.handling_note, "handled_by": exception.handled_by, "handler_name": exception.handler.full_name if exception.handler else None, "handled_at": exception.handled_at.isoformat() if exception.handled_at else None, "created_at": exception.created_at.isoformat() if exception.created_at else None, "updated_at": exception.updated_at.isoformat() if exception.updated_at else None, } @router.patch("/{exception_id}") async def update_exception_status( exception_id: int, status: Optional[str] = Body(None), handling_note: Optional[str] = Body(None), db: AsyncSession = Depends(get_db), company_id: int = Depends(get_current_company_id), ): """更新异常状态""" service = ExceptionService(db) # 检查权限 exception = await service.get_exception(exception_id) if not exception: raise HTTPException(status_code=404, detail="异常不存在") if exception.company_id != company_id: raise HTTPException(status_code=403, detail="无权访问该异常") updated = await service.update_exception( exception_id=exception_id, status=status, handling_note=handling_note, ) return {"success": True, "data": updated} @router.post("/batch-update") async def batch_update_exceptions( exception_ids: List[int] = Body(..., description="异常ID列表"), status: str = Body(..., description="新状态"), db: AsyncSession = Depends(get_db), company_id: int = Depends(get_current_company_id), ) -> Dict[str, Any]: """批量更新异常状态""" service = ExceptionService(db) # 检查权限 query = select(ExceptionItem).where(ExceptionItem.id.in_(exception_ids)) result = await db.execute(query) exceptions = result.scalars().all() # 验证权限 for e in exceptions: if e.company_id != company_id: raise HTTPException(status_code=403, detail="存在无权访问的异常") count = await service.batch_update_status(exception_ids, status) return {"success": True, "updated_count": count} @router.post("/batch-resolve") async def batch_resolve( exception_ids: List[int] = Body(..., description="异常ID列表"), handling_note: str = Body(..., description="处理备注"), db: AsyncSession = Depends(get_db), company_id: int = Depends(get_current_company_id), ) -> Dict[str, Any]: """批量处理异常(标记为已解决)""" service = ExceptionService(db) # 检查权限 query = select(ExceptionItem).where(ExceptionItem.id.in_(exception_ids)) result = await db.execute(query) exceptions = result.scalars().all() # 验证权限 for e in exceptions: if e.company_id != company_id: raise HTTPException(status_code=403, detail="存在无权访问的异常") count = await service.batch_resolve(exception_ids, handling_note) return {"success": True, "resolved_count": count}