""" 任务管理API """ from typing import Optional from fastapi import APIRouter, Depends, HTTPException, status, Query, Request from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_db, get_current_user, require_admin_or_manager from app.schemas.base import ResponseModel, PaginatedResponse from app.schemas.task import TaskCreate, TaskUpdate, TaskResponse, TaskStatsResponse from app.services.task_service import task_service from app.services.system_log_service import system_log_service from app.schemas.system_log import SystemLogCreate from app.models.user import User router = APIRouter(prefix="/manager/tasks", tags=["Tasks"], redirect_slashes=False) @router.post("", response_model=ResponseModel[TaskResponse], summary="创建任务") async def create_task( task_in: TaskCreate, request: Request, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """创建新任务""" task = await task_service.create_task(db, task_in, current_user.id) # 记录任务创建日志 await system_log_service.create_log( db, SystemLogCreate( level="INFO", type="api", message=f"创建任务: {task.title}", user_id=current_user.id, user=current_user.username, ip=request.client.host if request.client else None, path="/api/v1/manager/tasks", method="POST", user_agent=request.headers.get("user-agent") ) ) # 构建响应 courses = [link.course.name for link in task.course_links] # 安全获取枚举值(兼容字符串和枚举类型) priority_val = task.priority.value if hasattr(task.priority, 'value') else task.priority status_val = task.status.value if hasattr(task.status, 'value') else task.status completed_count = sum( 1 for a in task.assignments if (a.status.value if hasattr(a.status, 'value') else a.status) == "completed" ) return ResponseModel( data=TaskResponse( id=task.id, title=task.title, description=task.description, priority=priority_val, status=status_val, creator_id=task.creator_id, deadline=task.deadline, requirements=task.requirements, progress=task.progress, created_at=task.created_at, updated_at=task.updated_at, courses=courses, assigned_count=len(task.assignments), completed_count=completed_count ) ) @router.get("", response_model=ResponseModel[PaginatedResponse[TaskResponse]], summary="获取任务列表") async def get_tasks( status: Optional[str] = Query(None, description="任务状态筛选"), page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=500), db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """获取任务列表""" tasks, total = await task_service.get_tasks(db, status, page, page_size) # 构建响应 items = [] for task in tasks: # 安全获取枚举值 priority_val = task.priority.value if hasattr(task.priority, 'value') else task.priority status_val = task.status.value if hasattr(task.status, 'value') else task.status # 使用已加载的关联数据(通过 selectinload) courses = [link.course.name for link in task.course_links] if task.course_links else [] completed_count = sum( 1 for a in task.assignments if (a.status.value if hasattr(a.status, 'value') else a.status) == "completed" ) if task.assignments else 0 items.append(TaskResponse( id=task.id, title=task.title, description=task.description, priority=priority_val, status=status_val, creator_id=task.creator_id, deadline=task.deadline, requirements=task.requirements, progress=task.progress, created_at=task.created_at, updated_at=task.updated_at, courses=courses, assigned_count=len(task.assignments) if task.assignments else 0, completed_count=completed_count )) return ResponseModel( data=PaginatedResponse.create( items=items, total=total, page=page, page_size=page_size ) ) @router.get("/stats", response_model=ResponseModel[TaskStatsResponse], summary="获取任务统计") async def get_task_stats( db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """获取任务统计数据""" stats = await task_service.get_task_stats(db) return ResponseModel(data=stats) @router.get("/{task_id}", response_model=ResponseModel[TaskResponse], summary="获取任务详情") async def get_task( task_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """获取任务详情""" task = await task_service.get_task_detail(db, task_id) if not task: raise HTTPException(status_code=404, detail="任务不存在") courses = [link.course.name for link in task.course_links] return ResponseModel( data=TaskResponse( id=task.id, title=task.title, description=task.description, priority=task.priority.value, status=task.status.value, creator_id=task.creator_id, deadline=task.deadline, requirements=task.requirements, progress=task.progress, created_at=task.created_at, updated_at=task.updated_at, courses=courses, assigned_count=len(task.assignments), completed_count=sum(1 for a in task.assignments if a.status.value == "completed") ) ) @router.put("/{task_id}", response_model=ResponseModel[TaskResponse], summary="更新任务") async def update_task( task_id: int, task_in: TaskUpdate, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """更新任务""" task = await task_service.update_task(db, task_id, task_in) if not task: raise HTTPException(status_code=404, detail="任务不存在") # 自动更新任务进度和状态 await task_service.update_task_status(db, task_id) # 重新加载详情 task_detail = await task_service.get_task_detail(db, task.id) courses = [link.course.name for link in task_detail.course_links] if task_detail else [] return ResponseModel( data=TaskResponse( id=task.id, title=task.title, description=task.description, priority=task.priority.value, status=task.status.value, creator_id=task.creator_id, deadline=task.deadline, requirements=task.requirements, progress=task.progress, created_at=task.created_at, updated_at=task.updated_at, courses=courses, assigned_count=len(task_detail.assignments) if task_detail else 0, completed_count=sum(1 for a in task_detail.assignments if a.status.value == "completed") if task_detail else 0 ) ) @router.delete("/{task_id}", response_model=ResponseModel, summary="删除任务") async def delete_task( task_id: int, request: Request, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """删除任务""" # 先获取任务信息用于日志 task_detail = await task_service.get_task_detail(db, task_id) task_title = task_detail.title if task_detail else f"ID:{task_id}" success = await task_service.delete_task(db, task_id) if not success: raise HTTPException(status_code=404, detail="任务不存在") # 记录任务删除日志 await system_log_service.create_log( db, SystemLogCreate( level="INFO", type="api", message=f"删除任务: {task_title}", user_id=current_user.id, user=current_user.username, ip=request.client.host if request.client else None, path=f"/api/v1/manager/tasks/{task_id}", method="DELETE", user_agent=request.headers.get("user-agent") ) ) return ResponseModel(message="任务已删除") @router.post("/{task_id}/remind", response_model=ResponseModel, summary="发送任务提醒") async def send_task_reminder( task_id: int, request: Request, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """向未完成任务的成员发送提醒""" task = await task_service.get_task_detail(db, task_id) if not task: raise HTTPException(status_code=404, detail="任务不存在") # 获取未完成的成员数量 incomplete_count = sum(1 for a in task.assignments if a.status.value != "completed") if incomplete_count == 0: return ResponseModel(message="所有成员已完成任务,无需发送提醒") # 记录提醒日志 await system_log_service.create_log( db, SystemLogCreate( level="INFO", type="notification", message=f"发送任务提醒: {task.title},提醒 {incomplete_count} 人", user_id=current_user.id, user=current_user.username, ip=request.client.host if request.client else None, path=f"/api/v1/manager/tasks/{task_id}/remind", method="POST", user_agent=request.headers.get("user-agent") ) ) # TODO: 实际发送通知逻辑(通过通知服务) # 可以调用 notification_service.send_task_reminder(task, incomplete_assignments) return ResponseModel(message=f"已向 {incomplete_count} 位未完成成员发送提醒")