Files
FastapiAdmin/backend/app/plugin/module_task/workflow/flows/service.py
T
zhangtao f57d4f8cfb refactor: 清理冗余代码与初始化数据,优化配置与调度器
本次提交包含多项优化和清理工作:
1.  新增订单类型枚举BUY,调整定时任务状态显示逻辑
2.  优化Redis连接配置,重构认证逻辑减少重复代码
3.  移除大量初始化演示数据、过期的租户与角色配置
4.  简化调度器状态存储,不再写入配置表而是内存缓存
5.  修复插件开关状态存储逻辑,调整字典数据标签与值
6.  优化WebSocket聊天处理,完善错误处理流程
7.  清理冗余导入与废弃的操作日志过滤逻辑
2026-06-24 01:41:04 +08:00

198 lines
7.1 KiB
Python

import asyncio
from typing import Any
from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from ..handlers.workflow_engine import run_workflow_sync, utc_now_iso, validate_workflow_graph
from ..nodes.crud import WorkflowNodeTypeCRUD
from .crud import WorkflowCRUD
from .schema import (
WorkflowCreateSchema,
WorkflowExecuteResultSchema,
WorkflowExecuteSchema,
WorkflowOutSchema,
WorkflowQueryParam,
WorkflowUpdateSchema,
)
# 工作流状态常量(与 WorkflowModel.status 保持一致:0:草稿 1:已发布 2:已归档)
WORKFLOW_STATUS_DRAFT = 0
WORKFLOW_STATUS_PUBLISHED = 1
WORKFLOW_STATUS_ARCHIVED = 2
# 工作流执行结果状态(0:失败 1:已完成)
WORKFLOW_EXEC_STATUS_FAILED = 0
WORKFLOW_EXEC_STATUS_COMPLETED = 1
class WorkflowService:
"""工作流:画布存储 + 发布校验 + 分层并行执行"""
def __init__(self, auth: AuthSchema) -> None:
self.auth = auth
def _out(self, obj: Any) -> WorkflowOutSchema:
return WorkflowOutSchema.model_validate(obj)
async def get_workflow_detail(self, id: int) -> WorkflowOutSchema:
obj = await WorkflowCRUD(self.auth).get_obj_by_id_crud(id=id)
if not obj:
raise CustomException(msg="工作流不存在")
return self._out(obj)
async def get_workflow_list(
self,
search: WorkflowQueryParam | None = None,
order_by: list[dict[str, str]] | None = None,
) -> list[WorkflowOutSchema]:
if order_by is None:
order_by = [{"updated_time": "desc"}]
obj_list = await WorkflowCRUD(self.auth).get_obj_list_crud(
search=vars(search) if search else None,
order_by=order_by,
)
return [self._out(o) for o in obj_list]
async def get_workflow_page(
self,
page_no: int,
page_size: int,
search: WorkflowQueryParam | None = None,
order_by: list[dict[str, str]] | None = None,
) -> dict:
offset = (page_no - 1) * page_size
order = order_by or [{"updated_time": "desc"}]
result = await WorkflowCRUD(self.auth).page(
offset=offset,
limit=page_size,
order_by=order,
search=vars(search) if search else None,
out_schema=WorkflowOutSchema,
)
result.items = [WorkflowOutSchema.model_validate(item).model_dump(mode="json") for item in result.items]
return result
async def create_workflow(self, data: WorkflowCreateSchema) -> WorkflowOutSchema:
exist = await WorkflowCRUD(self.auth).get(code=data.code)
if exist:
raise CustomException(msg="流程编码已存在")
obj = await WorkflowCRUD(self.auth).create_obj_crud(data=data)
if not obj:
raise CustomException(msg="创建工作流失败")
return self._out(obj)
async def update_workflow(self, id: int, data: WorkflowUpdateSchema) -> WorkflowOutSchema:
exist = await WorkflowCRUD(self.auth).get_obj_by_id_crud(id=id)
if not exist:
raise CustomException(msg="工作流不存在")
if exist.code != data.code:
other = await WorkflowCRUD(self.auth).get(code=data.code)
if other:
raise CustomException(msg="流程编码已存在")
obj = await WorkflowCRUD(self.auth).update_obj_crud(id=id, data=data)
if not obj:
raise CustomException(msg="更新工作流失败")
return self._out(obj)
async def delete_workflow(self, ids: list[int]) -> None:
if not ids:
raise CustomException(msg="删除ID不能为空")
await WorkflowCRUD(self.auth).delete_obj_crud(ids=ids)
async def publish_workflow(self, id: int) -> WorkflowOutSchema:
obj = await WorkflowCRUD(self.auth).get_obj_by_id_crud(id=id)
if not obj:
raise CustomException(msg="工作流不存在")
nodes = obj.nodes or []
edges = obj.edges or []
try:
validate_workflow_graph(nodes, edges)
except ValueError as e:
raise CustomException(msg=str(e)) from e
data = WorkflowUpdateSchema(
name=obj.name,
code=obj.code,
description=obj.description,
nodes=obj.nodes,
edges=obj.edges,
workflow_status=WORKFLOW_STATUS_PUBLISHED,
)
updated = await WorkflowCRUD(self.auth).update_obj_crud(id=id, data=data)
if not updated:
raise CustomException(msg="发布失败")
return self._out(updated)
async def execute_workflow(self, body: WorkflowExecuteSchema) -> WorkflowExecuteResultSchema:
obj = await WorkflowCRUD(self.auth).get_obj_by_id_crud(id=body.workflow_id)
if not obj:
raise CustomException(msg="工作流不存在")
if obj.workflow_status != WORKFLOW_STATUS_PUBLISHED:
raise CustomException(msg="仅已发布的工作流可执行")
nodes = obj.nodes or []
edges = obj.edges or []
if not nodes:
raise CustomException(msg="工作流没有节点")
codes_set = {n.get("type") for n in nodes if n.get("type")}
code_list = list(codes_set)
templates: dict[str, dict[str, Any]] = {}
type_objs = await WorkflowNodeTypeCRUD(self.auth).get_obj_list_crud(search={"code": ("in", code_list)})
type_map = {t.code: t for t in type_objs}
for code in codes_set:
node_type = type_map.get(code)
if not node_type:
raise CustomException(msg=f"节点类型未注册(请在「工作流节点类型」中维护,非定时任务节点): {code}")
if not node_type.func or not str(node_type.func).strip():
raise CustomException(msg=f"节点类型未配置 func 代码块: {code}")
templates[code] = {
"func": node_type.func,
"args": node_type.args,
"kwargs": node_type.kwargs,
}
variables = body.variables or {}
start = utc_now_iso()
try:
raw = await asyncio.to_thread(
run_workflow_sync,
nodes,
edges,
templates,
variables,
)
except ValueError as e:
raise CustomException(msg=str(e)) from e
except CustomException:
raise
except Exception as e:
end = utc_now_iso()
err = WorkflowExecuteResultSchema(
workflow_id=obj.id,
workflow_name=obj.name,
status=WORKFLOW_EXEC_STATUS_FAILED,
start_time=start,
end_time=end,
variables=variables,
node_results=None,
error=str(e),
)
return err
end = utc_now_iso()
ok = WorkflowExecuteResultSchema(
workflow_id=obj.id,
workflow_name=obj.name,
status=WORKFLOW_EXEC_STATUS_COMPLETED,
start_time=start,
end_time=end,
variables=variables,
node_results=raw.get("node_results"),
error=None,
)
return ok