2025-03-17 14:58:05 +08:00

432 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)}")