bugfix
This commit is contained in:
parent
2a715aba7e
commit
313266638d
@ -1,247 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Workflow Engine v1.1 (sqlor-backed)
|
||||
=================================
|
||||
Workflow Engine v1.3 (Enterprise Edition)
|
||||
========================================
|
||||
|
||||
This version adds:
|
||||
- Persistent tables designed per table.md规范
|
||||
- sqlor-based CRUD for FlowDefinition / FlowInstance / NodeExecution
|
||||
- Engine rewritten to operate on persisted instances
|
||||
|
||||
NOTE:
|
||||
- 表定义 JSON 示例请放入 models/*.json
|
||||
- 本文件假设 sqlor / ServerEnv 已可用
|
||||
Features:
|
||||
- org_id multi-tenant isolation
|
||||
- DAG workflow with persistence (sqlor)
|
||||
- Subflow support
|
||||
- Human task node
|
||||
- RBAC-based task assignment
|
||||
- Query APIs for UI
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# 表定义(models/*.json)——按 table.md 规范
|
||||
# Table definitions
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# models/flow_definition.json
|
||||
FLOW_DEFINITION_TABLE = {
|
||||
"summary": [{
|
||||
"name": "flow_definition",
|
||||
"title": "流程定义",
|
||||
"primary": "id",
|
||||
"catelog": "entity"
|
||||
}],
|
||||
"summary": [{"name": "flow_definition", "primary": "id"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "定义ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "name", "title": "流程名称", "type": "str", "length": 128},
|
||||
{"name": "version", "title": "版本", "type": "str", "length": 32},
|
||||
{"name": "dsl", "title": "YAML DSL", "type": "text"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_flow_def_name", "idxtype": "index", "idxfields": ["name"]}
|
||||
{"name": "id", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "type": "str", "length": 32},
|
||||
{"name": "name", "type": "str", "length": 128},
|
||||
{"name": "version", "type": "str", "length": 32},
|
||||
{"name": "dsl", "type": "text"},
|
||||
{"name": "created_at", "type": "timestamp"}
|
||||
]
|
||||
}
|
||||
|
||||
# models/flow_instance.json
|
||||
FLOW_INSTANCE_TABLE = {
|
||||
"summary": [{
|
||||
"name": "flow_instance",
|
||||
"title": "流程实例",
|
||||
"primary": "id",
|
||||
"catelog": "entity"
|
||||
}],
|
||||
"summary": [{"name": "flow_instance", "primary": "id"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "实例ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "flow_def_id", "title": "流程定义ID", "type": "str", "length": 32},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 16},
|
||||
{"name": "ctx", "title": "上下文(JSON)", "type": "text"},
|
||||
{"name": "active_nodes", "title": "当前节点(JSON)", "type": "text"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_flow_inst_def", "idxtype": "index", "idxfields": ["flow_def_id"]}
|
||||
{"name": "id", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "flow_def_id", "type": "str", "length": 32},
|
||||
{"name": "status", "type": "str", "length": 16},
|
||||
{"name": "ctx", "type": "text"},
|
||||
{"name": "active_nodes", "type": "text"},
|
||||
{"name": "created_at", "type": "timestamp"}
|
||||
]
|
||||
}
|
||||
|
||||
# models/node_execution.json
|
||||
NODE_EXECUTION_TABLE = {
|
||||
"summary": [{
|
||||
"name": "node_execution",
|
||||
"title": "节点执行记录",
|
||||
"primary": "id",
|
||||
"catelog": "relation"
|
||||
}],
|
||||
"summary": [{"name": "node_execution", "primary": "id"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "执行ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "instance_id", "title": "流程实例ID", "type": "str", "length": 32},
|
||||
{"name": "node_id", "title": "节点ID", "type": "str", "length": 64},
|
||||
{"name": "input_ctx", "title": "输入上下文", "type": "text"},
|
||||
{"name": "output_ctx", "title": "输出上下文", "type": "text"},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 16},
|
||||
{"name": "error", "title": "错误信息", "type": "text"},
|
||||
{"name": "created_at", "title": "执行时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_node_exec_inst", "idxtype": "index", "idxfields": ["instance_id"]}
|
||||
{"name": "id", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "type": "str", "length": 32},
|
||||
{"name": "instance_id", "type": "str", "length": 32},
|
||||
{"name": "node_id", "type": "str", "length": 64},
|
||||
{"name": "input_ctx", "type": "text"},
|
||||
{"name": "output_ctx", "type": "text"},
|
||||
{"name": "status", "type": "str", "length": 16},
|
||||
{"name": "error", "type": "text"},
|
||||
{"name": "created_at", "type": "timestamp"}
|
||||
]
|
||||
}
|
||||
|
||||
# models/subflow_instance.json
|
||||
SUBFLOW_INSTANCE_TABLE = {
|
||||
"summary": [{
|
||||
"name": "subflow_instance",
|
||||
"title": "子流程实例",
|
||||
"primary": "id",
|
||||
"catelog": "relation"
|
||||
}],
|
||||
"summary": [{"name": "subflow_instance", "primary": "id"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "子流程ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "parent_instance_id", "title": "父流程实例ID", "type": "str", "length": 32},
|
||||
{"name": "parent_node_id", "title": "父节点ID", "type": "str", "length": 64},
|
||||
{"name": "child_instance_id", "title": "子流程实例ID", "type": "str", "length": 32},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 16},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_subflow_parent", "idxtype": "index", "idxfields": ["parent_instance_id"]}
|
||||
{"name": "id", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "type": "str", "length": 32},
|
||||
{"name": "parent_instance_id", "type": "str", "length": 32},
|
||||
{"name": "parent_node_id", "type": "str", "length": 64},
|
||||
{"name": "child_instance_id", "type": "str", "length": 32},
|
||||
{"name": "status", "type": "str", "length": 16},
|
||||
{"name": "created_at", "type": "timestamp"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_node_exec_inst", "idxtype": "index", "idxfields": ["instance_id"]}
|
||||
|
||||
HUMAN_TASK_TABLE = {
|
||||
"summary": [{"name": "human_task", "primary": "id"}],
|
||||
"fields": [
|
||||
{"name": "id", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "type": "str", "length": 32},
|
||||
{"name": "instance_id", "type": "str", "length": 32},
|
||||
{"name": "node_id", "type": "str", "length": 64},
|
||||
{"name": "role", "type": "str", "length": 64},
|
||||
{"name": "assignee", "type": "str", "length": 64},
|
||||
{"name": "status", "type": "str", "length": 16},
|
||||
{"name": "input", "type": "text"},
|
||||
{"name": "output", "type": "text"},
|
||||
{"name": "timeout_at", "type": "timestamp"},
|
||||
{"name": "created_at", "type": "timestamp"},
|
||||
{"name": "completed_at", "type": "timestamp"}
|
||||
]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Engine implementation (sqlor)
|
||||
# Engine
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
import yaml
|
||||
import json
|
||||
from sqlor.dbpools import get_sor_context
|
||||
from ahserver.serverenv import ServerEnv
|
||||
|
||||
|
||||
class FlowEngine:
|
||||
"""Persistent workflow engine"""
|
||||
|
||||
async def create_definition(self, name: str, version: str, dsl_text: str):
|
||||
# ------------------------------
|
||||
# Definition / Instance
|
||||
# ------------------------------
|
||||
|
||||
async def create_definition(self, org_id, name, version, dsl_text):
|
||||
env = ServerEnv()
|
||||
async with (env, 'workflow') as sor:
|
||||
flow_def_id = env.uuid()
|
||||
fid = env.uuid()
|
||||
await sor.C('flow_definition', {
|
||||
'id': flow_def_id,
|
||||
'id': fid,
|
||||
'org_id': org_id,
|
||||
'name': name,
|
||||
'version': version,
|
||||
'dsl': dsl_text
|
||||
})
|
||||
return flow_def_id
|
||||
return fid
|
||||
|
||||
async def create_instance(self, flow_def_id: str, ctx: dict | None = None):
|
||||
async def create_instance(self, org_id, flow_def_id, ctx=None):
|
||||
env = ServerEnv()
|
||||
async with (env, 'workflow') as sor:
|
||||
inst_id = env.uuid()
|
||||
iid = env.uuid()
|
||||
await sor.C('flow_instance', {
|
||||
'id': inst_id,
|
||||
'id': iid,
|
||||
'org_id': org_id,
|
||||
'flow_def_id': flow_def_id,
|
||||
'status': 'running',
|
||||
'ctx': json.dumps(ctx or {}),
|
||||
'active_nodes': json.dumps([])
|
||||
})
|
||||
return inst_id
|
||||
return iid
|
||||
|
||||
async def step(self, instance_id: str):
|
||||
# ------------------------------
|
||||
# Execution
|
||||
# ------------------------------
|
||||
|
||||
async def step(self, org_id, instance_id):
|
||||
env = ServerEnv()
|
||||
async with (env, 'workflow') as sor:
|
||||
rows = await sor.R('flow_instance', {'id': instance_id})
|
||||
if not rows:
|
||||
insts = await sor.R('flow_instance', {
|
||||
'id': instance_id,
|
||||
'org_id': org_id
|
||||
})
|
||||
if not insts:
|
||||
return
|
||||
inst = rows[0]
|
||||
|
||||
inst = insts[0]
|
||||
if inst['status'] != 'running':
|
||||
return
|
||||
|
||||
defs = await sor.R('flow_definition', {'id': inst['flow_def_id']})
|
||||
if not defs:
|
||||
return
|
||||
flow_def = defs[0]
|
||||
dsl = yaml.safe_load(flow_def['dsl'])(flow_def['dsl'])
|
||||
flow_def = (await sor.R(
|
||||
'flow_definition',
|
||||
{'id': inst['flow_def_id']}
|
||||
))[0]
|
||||
|
||||
dsl = yaml.safe_load(flow_def['dsl'])
|
||||
ctx = json.loads(inst['ctx'])
|
||||
active_nodes = set(json.loads(inst['active_nodes']))
|
||||
if not active_nodes:
|
||||
active_nodes = {dsl['start']}
|
||||
|
||||
active = set(json.loads(inst['active_nodes']) or [dsl['start']])
|
||||
next_nodes = set()
|
||||
|
||||
for node_id in active_nodes:
|
||||
node_def = dsl['nodes'][node_id]
|
||||
ntype = node_def['type']
|
||||
for node_id in active:
|
||||
node = dsl['nodes'][node_id]
|
||||
ntype = node['type']
|
||||
|
||||
# --- SubFlow handling ---
|
||||
if ntype == 'subflow':
|
||||
rows = await sor.R('subflow_instance', {
|
||||
'parent_instance_id': instance_id,
|
||||
'parent_node_id': node_id
|
||||
# -------- Human node --------
|
||||
if ntype == 'human':
|
||||
rows = await sor.R('human_task', {
|
||||
'instance_id': instance_id,
|
||||
'node_id': node_id,
|
||||
'status': 'pending'
|
||||
})
|
||||
|
||||
# 解析 input / output mapping
|
||||
input_map = node_def.get('input', {})
|
||||
output_map = node_def.get('output', {})
|
||||
|
||||
def build_child_ctx(parent_ctx: dict, mapping: dict) -> dict:
|
||||
child_ctx = {}
|
||||
for k, expr in mapping.items():
|
||||
# expr like: ctx.xxx.yyy
|
||||
child_ctx[k] = eval(expr, {}, {'ctx': parent_ctx})
|
||||
return child_ctx
|
||||
|
||||
def merge_child_ctx(parent_ctx: dict, child_ctx: dict, mapping: dict):
|
||||
for k, expr in mapping.items():
|
||||
# expr like: ctx.xxx
|
||||
target = expr.replace('ctx.', '')
|
||||
parent_ctx[target] = child_ctx.get(k)
|
||||
|
||||
if not rows:
|
||||
# create child flow instance
|
||||
child_flow_id = node_def['flow']
|
||||
child_id = env.uuid()
|
||||
child_ctx = build_child_ctx(ctx, input_map)
|
||||
|
||||
await sor.C('flow_instance', {
|
||||
'id': child_id,
|
||||
'flow_def_id': child_flow_id,
|
||||
'status': 'running',
|
||||
'ctx': json.dumps(child_ctx),
|
||||
'active_nodes': json.dumps([])
|
||||
})
|
||||
await sor.C('subflow_instance', {
|
||||
await sor.C('human_task', {
|
||||
'id': env.uuid(),
|
||||
'parent_instance_id': instance_id,
|
||||
'parent_node_id': node_id,
|
||||
'child_instance_id': child_id,
|
||||
'status': 'running'
|
||||
'org_id': org_id,
|
||||
'instance_id': instance_id,
|
||||
'node_id': node_id,
|
||||
'role': node.get('role'),
|
||||
'status': 'pending',
|
||||
'input': json.dumps(ctx),
|
||||
'timeout_at': env.after(node.get('timeout', 0))
|
||||
})
|
||||
next_nodes.add(node_id)
|
||||
continue
|
||||
next_nodes.add(node_id)
|
||||
continue
|
||||
|
||||
sub = rows[0]
|
||||
child_rows = await sor.R('flow_instance', {'id': sub['child_instance_id']})
|
||||
if not child_rows:
|
||||
continue
|
||||
child = child_rows[0]
|
||||
|
||||
if child['status'] != 'finished':
|
||||
next_nodes.add(node_id)
|
||||
continue
|
||||
|
||||
# merge ctx by output mapping
|
||||
child_ctx = json.loads(child['ctx'])
|
||||
merge_child_ctx(ctx, child_ctx, output_map)
|
||||
|
||||
await sor.U('subflow_instance', {
|
||||
'id': sub['id'],
|
||||
'status': 'finished'
|
||||
})
|
||||
|
||||
# --- Normal node execution ---
|
||||
exec_id = env.uuid()
|
||||
# -------- Normal node --------
|
||||
await sor.C('node_execution', {
|
||||
'id': exec_id,
|
||||
'id': env.uuid(),
|
||||
'org_id': org_id,
|
||||
'instance_id': instance_id,
|
||||
'node_id': node_id,
|
||||
'input_ctx': json.dumps(ctx),
|
||||
@ -256,7 +200,6 @@ class FlowEngine:
|
||||
continue
|
||||
next_nodes.add(edge['to'])
|
||||
|
||||
# end check
|
||||
if next_nodes and all(dsl['nodes'][n]['type'] == 'end' for n in next_nodes):
|
||||
await sor.U('flow_instance', {
|
||||
'id': instance_id,
|
||||
@ -269,3 +212,49 @@ class FlowEngine:
|
||||
'active_nodes': json.dumps(list(next_nodes))
|
||||
})
|
||||
|
||||
# ------------------------------
|
||||
# Human task APIs
|
||||
# ------------------------------
|
||||
|
||||
async def list_human_tasks(self, org_id, user_roles):
|
||||
env = ServerEnv()
|
||||
async with (env, 'workflow') as sor:
|
||||
return await sor.R('human_task', {
|
||||
'org_id': org_id,
|
||||
'status': 'pending',
|
||||
'role': ('in', user_roles)
|
||||
})
|
||||
|
||||
async def complete_human_task(self, org_id, task_id, user_id, output):
|
||||
env = ServerEnv()
|
||||
async with (env, 'workflow') as sor:
|
||||
rows = await sor.R('human_task', {
|
||||
'id': task_id,
|
||||
'org_id': org_id,
|
||||
'status': 'pending'
|
||||
})
|
||||
if not rows:
|
||||
return
|
||||
|
||||
task = rows[0]
|
||||
await sor.U('human_task', {
|
||||
'id': task_id,
|
||||
'assignee': user_id,
|
||||
'status': 'done',
|
||||
'output': json.dumps(output),
|
||||
'completed_at': env.now()
|
||||
})
|
||||
|
||||
inst = (await sor.R(
|
||||
'flow_instance',
|
||||
{'id': task['instance_id']}
|
||||
))[0]
|
||||
|
||||
ctx = json.loads(inst['ctx'])
|
||||
ctx.update(output)
|
||||
|
||||
await sor.U('flow_instance', {
|
||||
'id': inst['id'],
|
||||
'ctx': json.dumps(ctx)
|
||||
})
|
||||
|
||||
|
||||
234
readme.html
Normal file
234
readme.html
Normal file
@ -0,0 +1,234 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<h1 data-start="64" data-end="102">DagFlow – Enterprise Workflow Engine</h1>
|
||||
<p data-start="104" data-end="222">DagFlow 是一个 <strong data-start="116" data-end="133">企业级 DAG 工作流引擎</strong>,基于 <code data-start="137" data-end="155">sqlor + ahserver</code> 构建,支持多组织(org_id)隔离、子流程、人工节点(审批任务)、RBAC 角色分配,并提供清晰的流程实例与任务查询接口,适用于:</p>
|
||||
<ul data-start="224" data-end="283">
|
||||
<li data-start="224" data-end="232">
|
||||
<p data-start="226" data-end="232">企业流程编排</p>
|
||||
</li>
|
||||
<li data-start="233" data-end="244">
|
||||
<p data-start="235" data-end="244">审批流 / 工单流</p>
|
||||
</li>
|
||||
<li data-start="245" data-end="266">
|
||||
<p data-start="247" data-end="266">自动化运维 / AI Agent 编排</p>
|
||||
</li>
|
||||
<li data-start="267" data-end="283">
|
||||
<p data-start="269" data-end="283">多租户 SaaS 工作流系统</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr data-start="285" data-end="288">
|
||||
<h2 data-start="290" data-end="297">核心特性</h2>
|
||||
<h3 data-start="299" data-end="318">✅ 多租户隔离(org_id)</h3>
|
||||
<ul data-start="319" data-end="379">
|
||||
<li data-start="319" data-end="340">
|
||||
<p data-start="321" data-end="340">所有核心数据表均包含 <code data-start="332" data-end="340">org_id</code></p>
|
||||
</li>
|
||||
<li data-start="341" data-end="361">
|
||||
<p data-start="343" data-end="361">流程定义、流程实例、任务实例完全隔离</p>
|
||||
</li>
|
||||
<li data-start="362" data-end="379">
|
||||
<p data-start="364" data-end="379">天然支持同一流程在不同组织复用</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 data-start="381" data-end="396">✅ DAG 工作流引擎</h3>
|
||||
<ul data-start="397" data-end="460">
|
||||
<li data-start="397" data-end="415">
|
||||
<p data-start="399" data-end="415">基于 YAML DSL 描述流程</p>
|
||||
</li>
|
||||
<li data-start="416" data-end="431">
|
||||
<p data-start="418" data-end="431">支持条件边(<code data-start="424" data-end="430">when</code>)</p>
|
||||
</li>
|
||||
<li data-start="432" data-end="440">
|
||||
<p data-start="434" data-end="440">支持并行节点</p>
|
||||
</li>
|
||||
<li data-start="441" data-end="460">
|
||||
<p data-start="443" data-end="460">自动推进 / 收敛至 end 节点</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 data-start="462" data-end="480">✅ 持久化执行(sqlor)</h3>
|
||||
<ul data-start="481" data-end="541">
|
||||
<li data-start="481" data-end="505">
|
||||
<p data-start="483" data-end="505">流程定义、实例、节点执行、人工任务全部持久化</p>
|
||||
</li>
|
||||
<li data-start="506" data-end="519">
|
||||
<p data-start="508" data-end="519">引擎无状态,可水平扩展</p>
|
||||
</li>
|
||||
<li data-start="520" data-end="541">
|
||||
<p data-start="522" data-end="541">支持调度器 / worker 分离部署</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 data-start="543" data-end="565">✅ 人工节点(Human Task)</h3>
|
||||
<ul data-start="566" data-end="644">
|
||||
<li data-start="566" data-end="583">
|
||||
<p data-start="568" data-end="583">human 节点会生成待办任务</p>
|
||||
</li>
|
||||
<li data-start="584" data-end="595">
|
||||
<p data-start="586" data-end="595">流程在人工节点阻塞</p>
|
||||
</li>
|
||||
<li data-start="596" data-end="644">
|
||||
<p data-start="598" data-end="601">支持:</p>
|
||||
<ul data-start="604" data-end="644">
|
||||
<li data-start="604" data-end="616">
|
||||
<p data-start="606" data-end="616">角色(role)分配</p>
|
||||
</li>
|
||||
<li data-start="619" data-end="630">
|
||||
<p data-start="621" data-end="630">用户领取 / 完成</p>
|
||||
</li>
|
||||
<li data-start="633" data-end="644">
|
||||
<p data-start="635" data-end="644">输出回写流程上下文</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 data-start="646" data-end="664">✅ 子流程(SubFlow)</h3>
|
||||
<ul data-start="665" data-end="701">
|
||||
<li data-start="665" data-end="673">
|
||||
<p data-start="667" data-end="673">支持流程嵌套</p>
|
||||
</li>
|
||||
<li data-start="674" data-end="685">
|
||||
<p data-start="676" data-end="685">父子流程上下文映射</p>
|
||||
</li>
|
||||
<li data-start="686" data-end="701">
|
||||
<p data-start="688" data-end="701">子流程完成后自动回写父流程</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr data-start="703" data-end="706">
|
||||
<h2 data-start="708" data-end="716">表结构说明</h2>
|
||||
<h3 data-start="718" data-end="743">flow_definition(流程定义)</h3>
|
||||
<div class="TyagGW_tableContainer"><div tabindex="-1" class="group TyagGW_tableWrapper flex flex-col-reverse w-fit"><table data-start="744" data-end="875" class="w-fit min-w-(--thread-content-width)"><thead data-start="744" data-end="755"><tr data-start="744" data-end="755"><th data-start="744" data-end="749" data-col-size="sm">字段</th><th data-start="749" data-end="755" data-col-size="sm">说明</th></tr></thead><tbody data-start="766" data-end="875"><tr data-start="766" data-end="782"><td data-start="766" data-end="771" data-col-size="sm">id</td><td data-start="771" data-end="782" data-col-size="sm">流程定义 ID</td></tr><tr data-start="783" data-end="801"><td data-start="783" data-end="792" data-col-size="sm">org_id</td><td data-col-size="sm" data-start="792" data-end="801">组织 ID</td></tr><tr data-start="802" data-end="817"><td data-start="802" data-end="809" data-col-size="sm">name</td><td data-start="809" data-end="817" data-col-size="sm">流程名称</td></tr><tr data-start="818" data-end="834"><td data-start="818" data-end="828" data-col-size="sm">version</td><td data-col-size="sm" data-start="828" data-end="834">版本</td></tr><tr data-start="835" data-end="853"><td data-start="835" data-end="841" data-col-size="sm">dsl</td><td data-col-size="sm" data-start="841" data-end="853">YAML DSL</td></tr><tr data-start="854" data-end="875"><td data-start="854" data-end="867" data-col-size="sm">created_at</td><td data-col-size="sm" data-start="867" data-end="875">创建时间</td></tr></tbody></table><div class="sticky h-0 select-none end-(--thread-content-margin) self-end"><div class="absolute end-0 flex items-end" style="height: 32.75px;"><span class="" data-state="closed"><button aria-label="复制表格" class="hover:bg-token-bg-tertiary text-token-text-secondary my-1 rounded-sm p-1 transition-opacity group-[:not(:hover):not(:focus-within)]:pointer-events-none group-[:not(:hover):not(:focus-within)]:opacity-0"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg></button></span></div></div></div></div>
|
||||
<hr data-start="877" data-end="880">
|
||||
<h3 data-start="882" data-end="905">flow_instance(流程实例)</h3>
|
||||
<div class="TyagGW_tableContainer"><div tabindex="-1" class="group TyagGW_tableWrapper flex flex-col-reverse w-fit"><table data-start="906" data-end="1089" class="w-fit min-w-(--thread-content-width)"><thead data-start="906" data-end="917"><tr data-start="906" data-end="917"><th data-start="906" data-end="911" data-col-size="sm">字段</th><th data-start="911" data-end="917" data-col-size="sm">说明</th></tr></thead><tbody data-start="928" data-end="1089"><tr data-start="928" data-end="942"><td data-start="928" data-end="933" data-col-size="sm">id</td><td data-col-size="sm" data-start="933" data-end="942">实例 ID</td></tr><tr data-start="943" data-end="961"><td data-start="943" data-end="952" data-col-size="sm">org_id</td><td data-col-size="sm" data-start="952" data-end="961">组织 ID</td></tr><tr data-start="962" data-end="987"><td data-start="962" data-end="976" data-col-size="sm">flow_def_id</td><td data-col-size="sm" data-start="976" data-end="987">流程定义 ID</td></tr><tr data-start="988" data-end="1019"><td data-start="988" data-end="997" data-col-size="sm">status</td><td data-col-size="sm" data-start="997" data-end="1019">running / finished</td></tr><tr data-start="1020" data-end="1041"><td data-start="1020" data-end="1026" data-col-size="sm">ctx</td><td data-col-size="sm" data-start="1026" data-end="1041">流程上下文(JSON)</td></tr><tr data-start="1042" data-end="1067"><td data-start="1042" data-end="1057" data-col-size="sm">active_nodes</td><td data-col-size="sm" data-start="1057" data-end="1067">当前活跃节点</td></tr><tr data-start="1068" data-end="1089"><td data-start="1068" data-end="1081" data-col-size="sm">created_at</td><td data-col-size="sm" data-start="1081" data-end="1089">创建时间</td></tr></tbody></table><div class="sticky h-0 select-none end-(--thread-content-margin) self-end"><div class="absolute end-0 flex items-end" style="height: 32.75px;"><span class="" data-state="closed"><button aria-label="复制表格" class="hover:bg-token-bg-tertiary text-token-text-secondary my-1 rounded-sm p-1 transition-opacity group-[:not(:hover):not(:focus-within)]:pointer-events-none group-[:not(:hover):not(:focus-within)]:opacity-0"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg></button></span></div></div></div></div>
|
||||
<hr data-start="1091" data-end="1094">
|
||||
<h3 data-start="1096" data-end="1122">node_execution(节点执行记录)</h3>
|
||||
<div class="TyagGW_tableContainer"><div tabindex="-1" class="group TyagGW_tableWrapper flex flex-col-reverse w-fit"><table data-start="1123" data-end="1340" class="w-fit min-w-(--thread-content-width)"><thead data-start="1123" data-end="1134"><tr data-start="1123" data-end="1134"><th data-start="1123" data-end="1128" data-col-size="sm">字段</th><th data-start="1128" data-end="1134" data-col-size="sm">说明</th></tr></thead><tbody data-start="1145" data-end="1340"><tr data-start="1145" data-end="1161"><td data-start="1145" data-end="1150" data-col-size="sm">id</td><td data-col-size="sm" data-start="1150" data-end="1161">执行记录 ID</td></tr><tr data-start="1162" data-end="1180"><td data-start="1162" data-end="1171" data-col-size="sm">org_id</td><td data-start="1171" data-end="1180" data-col-size="sm">组织 ID</td></tr><tr data-start="1181" data-end="1206"><td data-start="1181" data-end="1195" data-col-size="sm">instance_id</td><td data-col-size="sm" data-start="1195" data-end="1206">流程实例 ID</td></tr><tr data-start="1207" data-end="1226"><td data-start="1207" data-end="1217" data-col-size="sm">node_id</td><td data-col-size="sm" data-start="1217" data-end="1226">节点 ID</td></tr><tr data-start="1227" data-end="1248"><td data-start="1227" data-end="1239" data-col-size="sm">input_ctx</td><td data-col-size="sm" data-start="1239" data-end="1248">输入上下文</td></tr><tr data-start="1249" data-end="1271"><td data-start="1249" data-end="1262" data-col-size="sm">output_ctx</td><td data-col-size="sm" data-start="1262" data-end="1271">输出上下文</td></tr><tr data-start="1272" data-end="1301"><td data-start="1272" data-end="1281" data-col-size="sm">status</td><td data-col-size="sm" data-start="1281" data-end="1301">success / failed</td></tr><tr data-start="1302" data-end="1318"><td data-start="1302" data-end="1310" data-col-size="sm">error</td><td data-col-size="sm" data-start="1310" data-end="1318">错误信息</td></tr><tr data-start="1319" data-end="1340"><td data-start="1319" data-end="1332" data-col-size="sm">created_at</td><td data-col-size="sm" data-start="1332" data-end="1340">执行时间</td></tr></tbody></table><div class="sticky h-0 select-none end-(--thread-content-margin) self-end"><div class="absolute end-0 flex items-end" style="height: 32.75px;"><span class="" data-state="closed"><button aria-label="复制表格" class="hover:bg-token-bg-tertiary text-token-text-secondary my-1 rounded-sm p-1 transition-opacity group-[:not(:hover):not(:focus-within)]:pointer-events-none group-[:not(:hover):not(:focus-within)]:opacity-0"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg></button></span></div></div></div></div>
|
||||
<hr data-start="1342" data-end="1345">
|
||||
<h3 data-start="1347" data-end="1367">human_task(人工任务)</h3>
|
||||
<div class="TyagGW_tableContainer"><div tabindex="-1" class="group TyagGW_tableWrapper flex flex-col-reverse w-fit"><table data-start="1368" data-end="1636" class="w-fit min-w-(--thread-content-width)"><thead data-start="1368" data-end="1379"><tr data-start="1368" data-end="1379"><th data-start="1368" data-end="1373" data-col-size="sm">字段</th><th data-start="1373" data-end="1379" data-col-size="sm">说明</th></tr></thead><tbody data-start="1390" data-end="1636"><tr data-start="1390" data-end="1404"><td data-start="1390" data-end="1395" data-col-size="sm">id</td><td data-col-size="sm" data-start="1395" data-end="1404">任务 ID</td></tr><tr data-start="1405" data-end="1423"><td data-start="1405" data-end="1414" data-col-size="sm">org_id</td><td data-col-size="sm" data-start="1414" data-end="1423">组织 ID</td></tr><tr data-start="1424" data-end="1449"><td data-start="1424" data-end="1438" data-col-size="sm">instance_id</td><td data-col-size="sm" data-start="1438" data-end="1449">流程实例 ID</td></tr><tr data-start="1450" data-end="1469"><td data-start="1450" data-end="1460" data-col-size="sm">node_id</td><td data-col-size="sm" data-start="1460" data-end="1469">节点 ID</td></tr><tr data-start="1470" data-end="1483"><td data-start="1470" data-end="1477" data-col-size="sm">role</td><td data-col-size="sm" data-start="1477" data-end="1483">角色</td></tr><tr data-start="1484" data-end="1504"><td data-start="1484" data-end="1495" data-col-size="sm">assignee</td><td data-col-size="sm" data-start="1495" data-end="1504">实际处理人</td></tr><tr data-start="1505" data-end="1532"><td data-start="1505" data-end="1514" data-col-size="sm">status</td><td data-col-size="sm" data-start="1514" data-end="1532">pending / done</td></tr><tr data-start="1533" data-end="1550"><td data-start="1533" data-end="1541" data-col-size="sm">input</td><td data-col-size="sm" data-start="1541" data-end="1550">输入上下文</td></tr><tr data-start="1551" data-end="1568"><td data-start="1551" data-end="1560" data-col-size="sm">output</td><td data-col-size="sm" data-start="1560" data-end="1568">输出结果</td></tr><tr data-start="1569" data-end="1590"><td data-start="1569" data-end="1582" data-col-size="sm">timeout_at</td><td data-col-size="sm" data-start="1582" data-end="1590">超时时间</td></tr><tr data-start="1591" data-end="1612"><td data-start="1591" data-end="1604" data-col-size="sm">created_at</td><td data-col-size="sm" data-start="1604" data-end="1612">创建时间</td></tr><tr data-start="1613" data-end="1636"><td data-start="1613" data-end="1628" data-col-size="sm">completed_at</td><td data-col-size="sm" data-start="1628" data-end="1636">完成时间</td></tr></tbody></table><div class="sticky h-0 select-none end-(--thread-content-margin) self-end"><div class="absolute end-0 flex items-end" style="height: 32.75px;"><span class="" data-state="closed"><button aria-label="复制表格" class="hover:bg-token-bg-tertiary text-token-text-secondary my-1 rounded-sm p-1 transition-opacity group-[:not(:hover):not(:focus-within)]:pointer-events-none group-[:not(:hover):not(:focus-within)]:opacity-0"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg></button></span></div></div></div></div>
|
||||
<hr data-start="1638" data-end="1641">
|
||||
<h2 data-start="1643" data-end="1652">DSL 示例</h2>
|
||||
<pre class="overflow-visible! px-0!" data-start="1654" data-end="1869"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">yaml</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-yaml"><span><span><span class="hljs-attr">start:</span></span><span> </span><span><span class="hljs-string">submit</span></span><span>
|
||||
</span><span><span class="hljs-attr">nodes:</span></span><span>
|
||||
</span><span><span class="hljs-attr">submit:</span></span><span>
|
||||
</span><span><span class="hljs-attr">type:</span></span><span> </span><span><span class="hljs-string">normal</span></span><span>
|
||||
</span><span><span class="hljs-attr">approve:</span></span><span>
|
||||
</span><span><span class="hljs-attr">type:</span></span><span> </span><span><span class="hljs-string">human</span></span><span>
|
||||
</span><span><span class="hljs-attr">role:</span></span><span> </span><span><span class="hljs-string">manager</span></span><span>
|
||||
</span><span><span class="hljs-attr">timeout:</span></span><span> </span><span><span class="hljs-number">86400</span></span><span>
|
||||
</span><span><span class="hljs-attr">end:</span></span><span>
|
||||
</span><span><span class="hljs-attr">type:</span></span><span> </span><span><span class="hljs-string">end</span></span><span>
|
||||
|
||||
</span><span><span class="hljs-attr">edges:</span></span><span>
|
||||
</span><span><span class="hljs-bullet">-</span></span><span> </span><span><span class="hljs-attr">from:</span></span><span> </span><span><span class="hljs-string">submit</span></span><span>
|
||||
</span><span><span class="hljs-attr">to:</span></span><span> </span><span><span class="hljs-string">approve</span></span><span>
|
||||
</span><span><span class="hljs-bullet">-</span></span><span> </span><span><span class="hljs-attr">from:</span></span><span> </span><span><span class="hljs-string">approve</span></span><span>
|
||||
</span><span><span class="hljs-attr">to:</span></span><span> </span><span><span class="hljs-string">end</span></span><span>
|
||||
</span></span></code></div></div></pre>
|
||||
<hr data-start="1871" data-end="1874">
|
||||
<h2 data-start="1876" data-end="1893">核心 API(Engine)</h2>
|
||||
<h3 data-start="1895" data-end="1905">创建流程定义</h3>
|
||||
<pre class="overflow-visible! px-0!" data-start="1906" data-end="2029"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">python</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-python"><span><span><span class="hljs-keyword">await</span></span><span> engine.create_definition(
|
||||
org_id=</span><span><span class="hljs-string">"org1"</span></span><span>,
|
||||
name=</span><span><span class="hljs-string">"请假审批"</span></span><span>,
|
||||
version=</span><span><span class="hljs-string">"v1"</span></span><span>,
|
||||
dsl_text=dsl_yaml
|
||||
)
|
||||
</span></span></code></div></div></pre>
|
||||
<h3 data-start="2031" data-end="2041">启动流程实例</h3>
|
||||
<pre class="overflow-visible! px-0!" data-start="2042" data-end="2182"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">python</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-python"><span><span>instance_id = </span><span><span class="hljs-keyword">await</span></span><span> engine.create_instance(
|
||||
org_id=</span><span><span class="hljs-string">"org1"</span></span><span>,
|
||||
flow_def_id=flow_id,
|
||||
ctx={</span><span><span class="hljs-string">"user"</span></span><span>: </span><span><span class="hljs-string">"alice"</span></span><span>, </span><span><span class="hljs-string">"days"</span></span><span>: </span><span><span class="hljs-number">3</span></span><span>}
|
||||
)
|
||||
</span></span></code></div></div></pre>
|
||||
<h3 data-start="2184" data-end="2199">推进流程(调度器调用)</h3>
|
||||
<pre class="overflow-visible! px-0!" data-start="2200" data-end="2271"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">python</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-python"><span><span><span class="hljs-keyword">await</span></span><span> engine.step(org_id=</span><span><span class="hljs-string">"org1"</span></span><span>, instance_id=instance_id)
|
||||
</span></span></code></div></div></pre>
|
||||
<hr data-start="2273" data-end="2276">
|
||||
<h2 data-start="2278" data-end="2289">人工任务 API</h2>
|
||||
<h3 data-start="2291" data-end="2307">查询待办任务(RBAC)</h3>
|
||||
<pre class="overflow-visible! px-0!" data-start="2308" data-end="2417"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">python</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-python"><span><span>tasks = </span><span><span class="hljs-keyword">await</span></span><span> engine.list_human_tasks(
|
||||
org_id=</span><span><span class="hljs-string">"org1"</span></span><span>,
|
||||
user_roles=[</span><span><span class="hljs-string">"manager"</span></span><span>, </span><span><span class="hljs-string">"admin"</span></span><span>]
|
||||
)
|
||||
</span></span></code></div></div></pre>
|
||||
<h3 data-start="2419" data-end="2429">完成人工任务</h3>
|
||||
<pre class="overflow-visible! px-0!" data-start="2430" data-end="2568"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">python</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-python"><span><span><span class="hljs-keyword">await</span></span><span> engine.complete_human_task(
|
||||
org_id=</span><span><span class="hljs-string">"org1"</span></span><span>,
|
||||
task_id=task_id,
|
||||
user_id=</span><span><span class="hljs-string">"bob"</span></span><span>,
|
||||
output={</span><span><span class="hljs-string">"approved"</span></span><span>: </span><span><span class="hljs-literal">True</span></span><span>}
|
||||
)
|
||||
</span></span></code></div></div></pre>
|
||||
<p data-start="2570" data-end="2574">完成后:</p>
|
||||
<ul data-start="2575" data-end="2614">
|
||||
<li data-start="2575" data-end="2588">
|
||||
<p data-start="2577" data-end="2588">任务状态 → done</p>
|
||||
</li>
|
||||
<li data-start="2589" data-end="2604">
|
||||
<p data-start="2591" data-end="2604">输出自动合并到流程 ctx</p>
|
||||
</li>
|
||||
<li data-start="2605" data-end="2614">
|
||||
<p data-start="2607" data-end="2614">流程可继续推进</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr data-start="2616" data-end="2619">
|
||||
<h2 data-start="2621" data-end="2628">架构设计</h2>
|
||||
<pre class="overflow-visible! px-0!" data-start="2630" data-end="2728"><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl corner-t-superellipse/1.1">less</div><div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="icon-sm"><use href="/cdn/assets/sprites-core-k5zux585.svg#ce3544" fill="currentColor"></use></svg>复制代码</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre!"><span><span><span class="hljs-selector-attr">[ HTTP API ]</span></span><span>
|
||||
|
|
||||
</span><span><span class="hljs-selector-attr">[ FlowEngine ]</span></span><span>
|
||||
|
|
||||
</span><span><span class="hljs-selector-attr">[ sqlor ]</span></span><span>
|
||||
|
|
||||
</span><span><span class="hljs-selector-attr">[ MySQL / PostgreSQL / SQLite ]</span></span><span>
|
||||
</span></span></code></div></div></pre>
|
||||
<ul data-start="2730" data-end="2769">
|
||||
<li data-start="2730" data-end="2742">
|
||||
<p data-start="2732" data-end="2742">Engine 无状态</p>
|
||||
</li>
|
||||
<li data-start="2743" data-end="2759">
|
||||
<p data-start="2745" data-end="2759">Scheduler 可多实例</p>
|
||||
</li>
|
||||
<li data-start="2760" data-end="2769">
|
||||
<p data-start="2762" data-end="2769">支持分布式部署</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr data-start="2771" data-end="2774">
|
||||
<h2 data-start="2776" data-end="2783">适用场景</h2>
|
||||
<ul data-start="2785" data-end="2848">
|
||||
<li data-start="2785" data-end="2806">
|
||||
<p data-start="2787" data-end="2806">企业审批流(请假 / 报销 / 发布)</p>
|
||||
</li>
|
||||
<li data-start="2807" data-end="2816">
|
||||
<p data-start="2809" data-end="2816">自动化运维流程</p>
|
||||
</li>
|
||||
<li data-start="2817" data-end="2833">
|
||||
<p data-start="2819" data-end="2833">AI Agent 调度与编排</p>
|
||||
</li>
|
||||
<li data-start="2834" data-end="2848">
|
||||
<p data-start="2836" data-end="2848">SaaS 多租户流程平台</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr data-start="2850" data-end="2853">
|
||||
<h2 data-start="2855" data-end="2865">后续可扩展方向</h2>
|
||||
<ul data-start="2867" data-end="2946">
|
||||
<li data-start="2867" data-end="2888">
|
||||
<p data-start="2869" data-end="2888">✔ 乐观锁 / version 防并发</p>
|
||||
</li>
|
||||
<li data-start="2889" data-end="2904">
|
||||
<p data-start="2891" data-end="2904">✔ 并行网关 / 排他网关</p>
|
||||
</li>
|
||||
<li data-start="2905" data-end="2918">
|
||||
<p data-start="2907" data-end="2918">✔ 任务转派 / 会签</p>
|
||||
</li>
|
||||
<li data-start="2919" data-end="2933">
|
||||
<p data-start="2921" data-end="2933">✔ SLA / 超时补偿</p>
|
||||
</li>
|
||||
<li data-start="2934" data-end="2946">
|
||||
<p data-start="2936" data-end="2946">✔ 流程可视化建模器</p>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
245
t
Normal file
245
t
Normal file
@ -0,0 +1,245 @@
|
||||
# DagFlow – Enterprise Workflow Engine
|
||||
|
||||
DagFlow 是一个 **企业级 DAG 工作流引擎**,基于 `sqlor + ahserver` 构建,支持多组织(org\_id)隔离、子流程、人工节点(审批任务)、RBAC 角色分配,并提供清晰的流程实例与任务查询接口,适用于:
|
||||
|
||||
* 企业流程编排
|
||||
|
||||
* 审批流 / 工单流
|
||||
|
||||
* 自动化运维 / AI Agent 编排
|
||||
|
||||
* 多租户 SaaS 工作流系统
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
## 核心特性
|
||||
|
||||
### ✅ 多租户隔离(org\_id)
|
||||
|
||||
* 所有核心数据表均包含 `org_id`
|
||||
|
||||
* 流程定义、流程实例、任务实例完全隔离
|
||||
|
||||
* 天然支持同一流程在不同组织复用
|
||||
|
||||
|
||||
### ✅ DAG 工作流引擎
|
||||
|
||||
* 基于 YAML DSL 描述流程
|
||||
|
||||
* 支持条件边(`when`)
|
||||
|
||||
* 支持并行节点
|
||||
|
||||
* 自动推进 / 收敛至 end 节点
|
||||
|
||||
|
||||
### ✅ 持久化执行(sqlor)
|
||||
|
||||
* 流程定义、实例、节点执行、人工任务全部持久化
|
||||
|
||||
* 引擎无状态,可水平扩展
|
||||
|
||||
* 支持调度器 / worker 分离部署
|
||||
|
||||
|
||||
### ✅ 人工节点(Human Task)
|
||||
|
||||
* human 节点会生成待办任务
|
||||
|
||||
* 流程在人工节点阻塞
|
||||
|
||||
* 支持:
|
||||
|
||||
* 角色(role)分配
|
||||
|
||||
* 用户领取 / 完成
|
||||
|
||||
* 输出回写流程上下文
|
||||
|
||||
|
||||
### ✅ 子流程(SubFlow)
|
||||
|
||||
* 支持流程嵌套
|
||||
|
||||
* 父子流程上下文映射
|
||||
|
||||
* 子流程完成后自动回写父流程
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
## 表结构说明
|
||||
|
||||
### flow\_definition(流程定义)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 流程定义 ID |
|
||||
| org\_id | 组织 ID |
|
||||
| name | 流程名称 |
|
||||
| version | 版本 |
|
||||
| dsl | YAML DSL |
|
||||
| created\_at | 创建时间 |
|
||||
|
||||
- - -
|
||||
|
||||
### flow\_instance(流程实例)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 实例 ID |
|
||||
| org\_id | 组织 ID |
|
||||
| flow\_def\_id | 流程定义 ID |
|
||||
| status | running / finished |
|
||||
| ctx | 流程上下文(JSON) |
|
||||
| active\_nodes | 当前活跃节点 |
|
||||
| created\_at | 创建时间 |
|
||||
|
||||
- - -
|
||||
|
||||
### node\_execution(节点执行记录)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 执行记录 ID |
|
||||
| org\_id | 组织 ID |
|
||||
| instance\_id | 流程实例 ID |
|
||||
| node\_id | 节点 ID |
|
||||
| input\_ctx | 输入上下文 |
|
||||
| output\_ctx | 输出上下文 |
|
||||
| status | success / failed |
|
||||
| error | 错误信息 |
|
||||
| created\_at | 执行时间 |
|
||||
|
||||
- - -
|
||||
|
||||
### human\_task(人工任务)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 任务 ID |
|
||||
| org\_id | 组织 ID |
|
||||
| instance\_id | 流程实例 ID |
|
||||
| node\_id | 节点 ID |
|
||||
| role | 角色 |
|
||||
| assignee | 实际处理人 |
|
||||
| status | pending / done |
|
||||
| input | 输入上下文 |
|
||||
| output | 输出结果 |
|
||||
| timeout\_at | 超时时间 |
|
||||
| created\_at | 创建时间 |
|
||||
| completed\_at | 完成时间 |
|
||||
|
||||
- - -
|
||||
|
||||
## DSL 示例
|
||||
|
||||
yaml
|
||||
|
||||
复制代码
|
||||
|
||||
`start: submit nodes: submit: type: normal approve: type: human role: manager timeout: 86400 end: type: end edges: - from: submit to: approve - from: approve to: end`
|
||||
|
||||
- - -
|
||||
|
||||
## 核心 API(Engine)
|
||||
|
||||
### 创建流程定义
|
||||
|
||||
python
|
||||
|
||||
复制代码
|
||||
|
||||
`await engine.create_definition( org_id="org1", name="请假审批", version="v1", dsl_text=dsl_yaml )`
|
||||
|
||||
### 启动流程实例
|
||||
|
||||
python
|
||||
|
||||
复制代码
|
||||
|
||||
`instance_id = await engine.create_instance( org_id="org1", flow_def_id=flow_id, ctx={"user": "alice", "days": 3} )`
|
||||
|
||||
### 推进流程(调度器调用)
|
||||
|
||||
python
|
||||
|
||||
复制代码
|
||||
|
||||
`await engine.step(org_id="org1", instance_id=instance_id)`
|
||||
|
||||
- - -
|
||||
|
||||
## 人工任务 API
|
||||
|
||||
### 查询待办任务(RBAC)
|
||||
|
||||
python
|
||||
|
||||
复制代码
|
||||
|
||||
`tasks = await engine.list_human_tasks( org_id="org1", user_roles=["manager", "admin"] )`
|
||||
|
||||
### 完成人工任务
|
||||
|
||||
python
|
||||
|
||||
复制代码
|
||||
|
||||
`await engine.complete_human_task( org_id="org1", task_id=task_id, user_id="bob", output={"approved": True} )`
|
||||
|
||||
完成后:
|
||||
|
||||
* 任务状态 → done
|
||||
|
||||
* 输出自动合并到流程 ctx
|
||||
|
||||
* 流程可继续推进
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
## 架构设计
|
||||
|
||||
less
|
||||
|
||||
复制代码
|
||||
|
||||
`[ HTTP API ] | [ FlowEngine ] | [ sqlor ] | [ MySQL / PostgreSQL / SQLite ]`
|
||||
|
||||
* Engine 无状态
|
||||
|
||||
* Scheduler 可多实例
|
||||
|
||||
* 支持分布式部署
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
## 适用场景
|
||||
|
||||
* 企业审批流(请假 / 报销 / 发布)
|
||||
|
||||
* 自动化运维流程
|
||||
|
||||
* AI Agent 调度与编排
|
||||
|
||||
* SaaS 多租户流程平台
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
## 后续可扩展方向
|
||||
|
||||
* ✔ 乐观锁 / version 防并发
|
||||
|
||||
* ✔ 并行网关 / 排他网关
|
||||
|
||||
* ✔ 任务转派 / 会签
|
||||
|
||||
* ✔ SLA / 超时补偿
|
||||
|
||||
* ✔ 流程可视化建模器
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user