# api/task_api.py from typing import Dict, Any, List, Optional from fastapi import APIRouter, HTTPException, Depends, Query from pydantic import BaseModel, Field from services.task_service import TaskService from core.exceptions import TianfengTaskError from config.task_config import ( get_all_task_types, get_task_type_name, get_task_type_description, TaskTypeConfig, TaskStatusConfig, DefaultConfig, TASK_TYPE_CONFIG, TASK_TYPE_NORMAL, TASK_TYPE_SCHEDULED, DEFAULT_TASK_DESCRIPTION, DEFAULT_TEMPLATE_DESCRIPTION, TaskType, TaskStatus ) from api.models import ApiResponse, TaskInput, TaskBatchInput, TaskIdList, TaskTypeInfo, SortField, SortOrder, TaskUpdateInput, TaskEditInput # 创建路由器 router = APIRouter(tags=["任务管理"]) # 创建服务实例 task_service = TaskService() @router.get("/tasks", response_model=ApiResponse) async def get_tasks( status: Optional[TaskStatus] = Query(None, description="任务状态"), task_type: Optional[TaskType] = Query(None, description="任务类型"), name: Optional[str] = Query(None, description="任务名称(模糊查询)"), is_scheduled: Optional[bool] = Query(None, description="是否为定时任务"), created_start: Optional[int] = Query(None, description="创建时间起始(毫秒时间戳)"), created_end: Optional[int] = Query(None, description="创建时间结束(毫秒时间戳)"), sort_by: SortField = Query(default=SortField.CREATED_AT, description="排序字段"), sort_order: SortOrder = Query(default=SortOrder.DESC, description="排序方式"), page: int = Query(1, ge=1, description="页码"), page_size: int = Query(10, ge=1, le=100, description="每页数量") ): """ 获取任务列表 支持多种筛选条件、排序和分页 """ try: # 获取任务列表 tasks, total = task_service.get_all_tasks( status=status, task_type=task_type, name=name, is_scheduled=is_scheduled, created_start=created_start, created_end=created_end, sort_by=sort_by, sort_order=sort_order, page=page, page_size=page_size ) # 构建分页信息 pagination = { "page": page, "page_size": page_size, "total": total, "total_pages": (total + page_size - 1) // page_size } return { "code": 200, "message": "获取任务列表成功", "data": { "tasks": tasks, "pagination": pagination } } except Exception as e: raise HTTPException(status_code=500, detail=f"获取任务列表失败: {str(e)}") @router.get("/task/types", response_model=ApiResponse) async def get_task_types(): """获取任务类型列表""" try: # 从配置文件中获取任务类型列表 task_types = get_all_task_types() # 添加value字段(枚举值) for task_type in task_types: # 从TaskType枚举中获取value try: # 尝试通过名称获取枚举值 task_type["value"] = TaskType[task_type["key"]].value except (KeyError, AttributeError): # 如果枚举中没有对应的值,则使用key的小写作为value task_type["value"] = task_type["key"] return { "code": 200, "message": "获取任务类型列表成功", "data": task_types } except Exception as e: raise HTTPException(status_code=500, detail=f"获取任务类型列表失败: {str(e)}") @router.post("/task/create", response_model=ApiResponse) async def create_task(task_input: TaskInput): """创建任务""" try: # 获取任务类型值(处理枚举) task_type = task_input.task_type if hasattr(task_type, "value"): task_type_value = task_type.value else: task_type_value = task_type # 从配置文件中获取任务类型信息 task_type_info = TaskTypeConfig.DETAILS.get(task_type_value, {}) # 根据任务类型决定是否为定时任务 is_scheduled = task_type_value == TASK_TYPE_SCHEDULED # 创建任务,设置固定参数 task = task_service.create_task( name=task_input.name, task_type=task_type_value, # 使用key作为task_type description=DEFAULT_TASK_DESCRIPTION, # 使用配置文件中的默认备注 template_desc=DEFAULT_TEMPLATE_DESCRIPTION, # 使用配置文件中的默认模板描述 is_scheduled=is_scheduled # 根据任务类型决定 ) # 在返回结果中添加任务类型的中文名称 if "task_type_name" not in task and task_type_info: task["task_type_name"] = task_type_info.get("name", task_type_value) return { "code": 200, "message": "创建任务成功", "data": task } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"创建任务失败: {str(e)}") @router.get("/task/{task_id}", response_model=ApiResponse) async def get_task(task_id: str): """获取任务详情""" try: # 获取任务 task = task_service.get_task_by_id(task_id) if not task: return { "code": 404, "message": f"任务不存在: {task_id}", "data": None } return { "code": 200, "message": "获取任务详情成功", "data": task } except Exception as e: raise HTTPException(status_code=500, detail=f"获取任务详情失败: {str(e)}") @router.delete("/task/{task_id}", response_model=ApiResponse) async def delete_task(task_id: str): """删除任务""" try: # 删除任务 success = task_service.delete_task(task_id) # 如果任务不存在,返回成功但提示任务不存在 if not success: return { "code": 200, "message": f"任务不存在或已被删除: {task_id}", "data": None } return { "code": 200, "message": "删除任务成功", "data": None } except Exception as e: raise HTTPException(status_code=500, detail=f"删除任务失败: {str(e)}") @router.post("/task/{task_id}/execute", response_model=ApiResponse) async def execute_task(task_id: str): """执行任务""" try: # 执行任务 task = task_service.execute_task(task_id) return { "code": 200, "message": "执行任务成功", "data": task } except ValueError as e: # 任务不存在 return { "code": 404, "message": str(e), "data": None } except TianfengTaskError as e: # 业务逻辑错误 return { "code": 400, "message": str(e), "data": None } except Exception as e: raise HTTPException(status_code=500, detail=f"执行任务失败: {str(e)}") @router.post("/task/{task_id}/cancel", response_model=ApiResponse) async def cancel_task(task_id: str): """取消任务""" try: # 取消任务 task = task_service.cancel_task(task_id) return { "code": 200, "message": "取消任务成功", "data": task } except ValueError as e: # 任务不存在或状态不允许取消 return { "code": 400, "message": str(e), "data": None } except Exception as e: raise HTTPException(status_code=500, detail=f"取消任务失败: {str(e)}") @router.delete("/task/batch", response_model=ApiResponse) async def batch_delete_tasks(id_list: TaskIdList): """批量删除任务""" try: # 批量删除任务 deleted_count = 0 not_found_ids = [] for task_id in id_list.task_ids: if task_service.delete_task(task_id): deleted_count += 1 else: not_found_ids.append(task_id) # 构建消息 message = f"批量删除任务成功,共删除 {deleted_count} 个任务" if not_found_ids: message += f",有 {len(not_found_ids)} 个任务不存在或已被删除" return { "code": 200, "message": message, "data": { "deleted_count": deleted_count, "total_count": len(id_list.task_ids), "not_found_ids": not_found_ids } } except Exception as e: raise HTTPException(status_code=500, detail=f"批量删除任务失败: {str(e)}") @router.put("/task/{task_id}", response_model=ApiResponse) async def update_task(task_id: str, task_update: TaskUpdateInput): """更新任务基本信息""" try: # 检查任务是否存在 task = task_service.get_task_by_id(task_id) if not task: return { "code": 404, "message": f"任务 {task_id} 不存在", "data": None } # 更新任务信息 updated_task = task_service.update_task( task_id=task_id, name=task_update.name, description=task_update.description, task_type=task_update.task_type, blocks=task_update.blocks, variables=task_update.variables, schedule=task_update.schedule ) return { "code": 200, "message": "更新任务成功", "data": {"task": updated_task} } except Exception as e: raise HTTPException(status_code=500, detail=f"更新任务失败: {str(e)}") @router.post("/task/edit", response_model=ApiResponse) async def edit_task(task_edit: TaskEditInput): """编辑任务流程和变量""" try: # 检查任务是否存在 task = task_service.get_task_by_id(task_edit.task_id) if not task: return { "code": 404, "message": f"任务 {task_edit.task_id} 不存在", "data": None } # 更新任务流程和变量 updated_task = task_service.update_task_workflow( task_id=task_edit.task_id, blocks=task_edit.blocks, variables=task_edit.variables ) return { "code": 200, "message": "编辑任务成功", "data": {"task": updated_task} } except Exception as e: raise HTTPException(status_code=500, detail=f"编辑任务失败: {str(e)}") @router.get("/task/{task_id}/edit", response_model=ApiResponse) async def get_task_edit_info(task_id: str): """获取任务编辑信息""" try: # 获取任务信息 task = task_service.get_task_by_id(task_id) if not task: return { "code": 404, "message": f"任务 {task_id} 不存在", "data": None } # 获取可用的子任务列表(排除当前任务自身) available_subtasks = [] try: all_tasks, _ = task_service.get_all_tasks(page=1, page_size=1000) # 获取所有任务 for t in all_tasks: if t["task_id"] != task_id: # 排除当前任务 available_subtasks.append({ "task_id": t["task_id"], "name": t["name"] }) except Exception as e: # 如果获取任务列表失败,记录错误但继续执行 print(f"获取可用子任务列表失败: {str(e)}") # 获取组件详细信息 from config.component_detail_config import ComponentDetailConfig component_details = ComponentDetailConfig.get_all_components() # 获取组件类型中文名称映射 component_type_names = ComponentDetailConfig.get_component_type_names() # 按组件类型分组并添加中文名称 component_types = {} for component in component_details: component_type = component["type"] # 如果该类型还未添加到结果中,先创建类型信息 if component_type not in component_types: # 获取组件类型的中文名称,如果没有则使用英文标识 type_name = component_type_names.get(component_type, component_type) component_types[component_type] = { "type": component_type, # 英文标识 "name": type_name, # 中文名称 "components": [] # 该类型下的组件列表 } # 添加组件到对应类型下 component_types[component_type]["components"].append(component) # 不再将子任务列表嵌入到组件类型对象中 if "subtask" in component_types: component_types["subtask"]["available_subtasks"] = available_subtasks return { "code": 200, "message": "获取任务编辑信息成功", "data": { "task": task, "component_types": component_types, "available_subtasks": available_subtasks # 作为单独的字段返回 } } except Exception as e: raise HTTPException(status_code=500, detail=f"获取任务编辑信息失败: {str(e)}") @router.get("/tasks/available-subtasks", response_model=ApiResponse) async def get_available_subtasks(current_task_id: Optional[str] = None): """获取可用的子任务列表""" try: # 获取所有任务 all_tasks, _ = task_service.get_all_tasks(page=1, page_size=1000) # 过滤出可用的子任务(如果提供了当前任务ID,则排除当前任务) available_subtasks = [] for task in all_tasks: if not current_task_id or task["task_id"] != current_task_id: available_subtasks.append({ "task_id": task["task_id"], "name": task["name"] }) return { "code": 200, "message": "获取可用子任务列表成功", "data": { "subtasks": available_subtasks } } except Exception as e: raise HTTPException(status_code=500, detail=f"获取可用子任务列表失败: {str(e)}")