VWED_server/services/task_service.py

440 lines
16 KiB
Python
Raw Normal View History

2025-04-30 16:57:46 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
天风任务服务层
处理天风任务相关的业务逻辑
"""
import json
import uuid
import datetime
from sqlalchemy.orm import Session
from typing import List, Dict, Any, Optional
from sqlalchemy import and_, or_, desc, asc
from data.models.taskdef import VWEDTaskDef
from data.models.taskrecord import VWEDTaskRecord
from data.models.tasktemplate import VWEDTaskTemplate
from data.enum.task_def_enum import TaskTypeEnum, TaskStatusEnum, EnableStatus, PeriodicTaskStatus
from utils.logger import get_logger
# 设置日志
logger = get_logger("services.task_service")
# 自定义异常类
class TaskNameExistsError(Exception):
"""任务名称已存在异常"""
pass
class TaskService:
"""
天风任务服务类
提供天风任务的相关业务操作
"""
@staticmethod
def get_task_list(
db: Session,
page_num: int = 1,
page_size: int = 10,
keyword: Optional[str] = None,
status: Optional[int] = None,
sort_field: Optional[str] = None,
sort_order: Optional[str] = "desc"
) -> Dict[str, Any]:
"""
获取任务列表
Args:
db: 数据库会话
page_num: 页码从1开始
page_size: 每页记录数
keyword: 关键字(任务名称)模糊匹配
status: 状态过滤
sort_field: 排序字段
sort_order: 排序方式asc升序desc降序
Returns:
Dict[str, Any]: 包含任务列表和分页信息
"""
try:
# 构建查询条件
query = db.query(VWEDTaskDef)
# 添加筛选条件
if keyword:
query = query.filter(VWEDTaskDef.label.like(f'%{keyword}%'))
if status is not None:
query = query.filter(VWEDTaskDef.status == status)
# 计算总数
total = query.count()
# 添加排序
if sort_field and sort_field in VWEDTaskDef.__table__.columns:
direction = desc if sort_order == "desc" else asc
query = query.order_by(direction(getattr(VWEDTaskDef, sort_field)))
else:
# 默认按创建时间倒序
query = query.order_by(desc(VWEDTaskDef.created_at))
# 分页处理
offset = (page_num - 1) * page_size
tasks = query.offset(offset).limit(page_size).all()
# 格式化返回结果
task_list = []
for task in tasks:
task_list.append({
"id": task.id,
"label": task.label,
"version": task.version,
"detail": task.detail,
"templateName": task.template_name,
"periodicTask": bool(task.periodic_task),
"ifEnable": task.if_enable,
"status": task.status,
"createDate": task.created_at,
"remark": task.remark,
"tenant_id": task.tenant_id,
"updated_at": task.updated_at
})
return {
"total": total,
"list": task_list,
"pageNum": page_num,
"pageSize": page_size,
"totalPages": (total + page_size - 1) // page_size
}
except Exception as e:
logger.error(f"获取任务列表失败: {str(e)}")
raise Exception(f"获取任务列表失败: {str(e)}")
@staticmethod
def create_task(
db: Session,
label: str,
task_type: int,
remark: Optional[str] = None,
period: Optional[int] = None,
delay: Optional[int] = 3000,
release_sites: Optional[bool] = True,
token: Optional[str] = None,
tenant_id: str = "default",
map_id: Optional[str] = None
) -> Dict[str, Any]:
"""
创建任务
Args:
db: 数据库会话
label: 任务名称
task_type: 任务类型1-普通任务2-定时任务
remark: 任务备注
period: 周期时间毫秒定时任务必填
delay: 延迟时间毫秒默认3000
release_sites: 是否释放站点默认true
token: 用户token值用于认证
tenant_id: 租户ID用于多租户隔离默认为"default"
map_id: 相关地图ID
Returns:
Dict[str, Any]: 新建任务信息
Raises:
ValueError: 参数验证失败时
TaskNameExistsError: 任务名称已存在时
"""
try:
# 验证任务名称
if not label or label.strip() == "":
raise ValueError("任务名称不能为空")
# 验证任务名称长度
if len(label.strip()) < 2:
raise ValueError("任务名称至少需要2个字符")
# 检查任务名称是否已存在
existing_task = db.query(VWEDTaskDef).filter(VWEDTaskDef.label == label.strip()).first()
if existing_task:
raise TaskNameExistsError(f"任务名称 '{label}' 已存在,请使用其他名称")
# 验证任务类型
if task_type not in [TaskTypeEnum.NORMAL, TaskTypeEnum.PERIODIC]:
raise ValueError("任务类型无效应为1(普通任务)或2(定时任务)")
# 验证定时任务参数
if task_type == TaskTypeEnum.PERIODIC and not period:
raise ValueError("定时任务必须指定周期时间")
# 获取当前启用的模板
template = db.query(VWEDTaskTemplate).filter(
VWEDTaskTemplate.template_if_enable == 1
).first()
if not template:
raise ValueError("没有可用的任务模板,请先启用一个模板")
# 创建任务定义
task_id = str(uuid.uuid4())
new_task = VWEDTaskDef(
id=task_id,
label=label.strip(), # 去除首尾空白字符
version=1,
detail=json.dumps({
"inputParams": [],
"outputParams": [],
"rootBlock": None
}, ensure_ascii=False),
template_name=template.template_name,
period=period if period else 3000,
periodic_task=PeriodicTaskStatus.PERIODIC if task_type == TaskTypeEnum.PERIODIC else PeriodicTaskStatus.NON_PERIODIC,
status=TaskStatusEnum.PENDING,
if_enable=EnableStatus.DISABLED,
delay=delay,
release_sites=release_sites,
remark=remark,
tenant_id=tenant_id, # 使用传入的租户ID
user_token=token, # 保存用户token
map_id=map_id # 保存地图ID
)
db.add(new_task)
db.commit()
db.refresh(new_task)
# 返回任务信息
return {
"id": new_task.id,
"label": new_task.label,
"version": new_task.version,
"detail": new_task.detail,
"templateName": new_task.template_name,
"periodicTask": bool(new_task.periodic_task),
"status": new_task.status,
"createDate": new_task.created_at,
"mapId": new_task.map_id # 返回地图ID
}
except TaskNameExistsError as e:
db.rollback()
logger.warning(f"创建任务失败: {str(e)}")
raise # 重新抛出异常,不包装
except Exception as e:
db.rollback()
logger.error(f"创建任务失败: {str(e)}")
raise Exception(f"创建任务失败: {str(e)}")
@staticmethod
def delete_tasks(db: Session, ids: List[str]) -> bool:
"""
删除任务
Args:
db: 数据库会话
ids: 要删除的任务ID列表
Returns:
bool: 是否删除成功
"""
try:
# 检查任务是否存在
tasks = db.query(VWEDTaskDef).filter(VWEDTaskDef.id.in_(ids)).all()
if len(tasks) != len(ids):
missing_ids = set(ids) - {task.id for task in tasks}
raise ValueError(f"以下任务不存在: {', '.join(missing_ids)}")
# 删除任务
for task in tasks:
db.delete(task)
db.commit()
return True
except Exception as e:
db.rollback()
logger.error(f"删除任务失败: {str(e)}")
raise Exception(f"删除任务失败: {str(e)}")
@staticmethod
def get_task_by_id(db: Session, task_id: str) -> Optional[Dict[str, Any]]:
"""
根据ID获取任务
Args:
db: 数据库会话
task_id: 任务ID
Returns:
Optional[Dict[str, Any]]: 任务信息如果不存在则返回None
"""
try:
task = db.query(VWEDTaskDef).filter(VWEDTaskDef.id == task_id).first()
if not task:
return None
# 格式化任务信息
task_info = {
"id": task.id,
"label": task.label,
"version": task.version,
"detail": json.loads(task.detail) if task.detail else {},
"templateName": task.template_name,
"period": task.period,
"periodicTask": bool(task.periodic_task),
"status": task.status,
"ifEnable": task.if_enable,
"delay": task.delay,
"releaseSites": bool(task.release_sites) if task.release_sites is not None else None,
"createDate": task.created_at,
"remark": task.remark,
}
return task_info
except Exception as e:
logger.error(f"获取任务失败: {str(e)}")
raise Exception(f"获取任务失败: {str(e)}")
@staticmethod
def export_tasks(db: Session, ids: List[str]) -> List[Dict[str, Any]]:
"""
导出任务
Args:
db: 数据库会话
ids: 任务ID列表
Returns:
List[Dict[str, Any]]: 任务配置列表
"""
try:
tasks = db.query(VWEDTaskDef).filter(VWEDTaskDef.id.in_(ids)).all()
if not tasks:
raise ValueError(f"未找到指定的任务")
# 格式化任务信息
tasks_export = []
for task in tasks:
task_export = {
"id": task.id,
"label": task.label,
"version": task.version,
"detail": task.detail,
"templateName": task.template_name,
"templateDescription": db.query(VWEDTaskTemplate.template_description)
.filter(VWEDTaskTemplate.template_name == task.template_name)
.scalar() or "",
"period": task.period,
"periodicTask": task.periodic_task,
"status": task.status,
"ifEnable": task.if_enable,
"delay": task.delay,
"remark": task.remark,
"windcategoryId": 0 # 默认分类ID
}
tasks_export.append(task_export)
return tasks_export
except Exception as e:
logger.error(f"导出任务失败: {str(e)}")
raise Exception(f"导出任务失败: {str(e)}")
@staticmethod
def import_task(
db: Session,
task_data: Dict[str, Any],
task_name: Optional[str] = None,
token: Optional[str] = None,
tenant_id: str = "default"
) -> Dict[str, Any]:
"""
导入任务
Args:
db: 数据库会话
task_data: 任务配置数据
task_name: 导入后的任务名称不填则使用原名称
token: 用户token用于认证
tenant_id: 租户ID默认为"default"
Returns:
Dict[str, Any]: 导入的任务信息
Raises:
ValueError: 参数验证失败时
TaskNameExistsError: 任务名称已存在时
"""
try:
# 创建新ID避免冲突
new_id = str(uuid.uuid4())
# 设置任务名称
label = task_name or task_data.get("label", "导入的任务")
# 获取detail字段这是唯一必需的字段
detail = task_data.get("detail")
if not detail:
raise ValueError("任务数据缺少detail字段无法导入")
# 检查任务名称是否已存在
existing_task = db.query(VWEDTaskDef).filter(
VWEDTaskDef.label == label.strip(),
VWEDTaskDef.tenant_id == tenant_id
).first()
if existing_task:
raise TaskNameExistsError(f"任务名称 '{label}' 已存在,请使用其他名称")
# 获取当前启用的模板
template = db.query(VWEDTaskTemplate).filter(
VWEDTaskTemplate.template_if_enable == 1
).first()
if not template:
template_name = task_data.get("templateName", "默认模板")
else:
template_name = template.template_name
# 创建新任务
new_task = VWEDTaskDef(
id=new_id,
label=label,
version=1, # 新任务从版本1开始
detail=detail,
template_name=template_name,
period=task_data.get("period", 3000),
periodic_task=task_data.get("periodicTask", PeriodicTaskStatus.NON_PERIODIC),
status=TaskStatusEnum.PENDING, # 初始状态为0
if_enable=EnableStatus.DISABLED, # 初始未启用
delay=task_data.get("delay", 3000),
release_sites=task_data.get("releaseSites", True), # 默认释放站点
remark=task_data.get("remark", ""),
tenant_id=tenant_id,
user_token=token
)
db.add(new_task)
db.commit()
db.refresh(new_task)
# 返回任务信息
return {
"id": new_task.id,
"label": new_task.label,
"version": new_task.version,
"status": new_task.status,
"createDate": new_task.created_at
}
except TaskNameExistsError as e:
db.rollback()
logger.warning(f"导入任务失败: {str(e)}")
raise # 重新抛出异常,不包装
except Exception as e:
db.rollback()
logger.error(f"导入任务失败: {str(e)}")
raise Exception(f"导入任务失败: {str(e)}")