feat(flow): 新增分组执行与异步模式支持
refactor(executors): 将 Rhai 引擎评估逻辑迁移至 script_rhai 模块 docs: 添加 Flow 架构文档与示例 JSON feat(i18n): 新增前端多语言支持 perf(axios): 优化 token 刷新与 401 处理逻辑 style: 统一代码格式化与简化条件判断
This commit is contained in:
336
docs/flow/code_js1.json
Normal file
336
docs/flow/code_js1.json
Normal file
@ -0,0 +1,336 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start_0",
|
||||
"type": "start",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 189.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "Start",
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "end_0",
|
||||
"type": "end",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2940,
|
||||
"y": 189.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "End"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "variable_zOb3P",
|
||||
"type": "variable",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2020,
|
||||
"y": 179.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "Variable_1",
|
||||
"assign": [
|
||||
{
|
||||
"operator": "declare",
|
||||
"left": "jss",
|
||||
"right": {
|
||||
"type": "constant",
|
||||
"content": "${\"user_n\"}",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"jss": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "code_y71Sd",
|
||||
"type": "javascript",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 640,
|
||||
"y": 162.3
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "JS",
|
||||
"inputsValues": {
|
||||
"input": {
|
||||
"type": "constant",
|
||||
"content": "",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"extra": {
|
||||
"index": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"language": "javascript",
|
||||
"content": "ctx.vri=\"usertest\"; ctx[\"user_n\"]=\"user_nuser_n\" for (let i = 0; i < 5; i++) { //console.log(`第 ${i} 次循环`); ctx[i]=\"v\"+i; }"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key0": {
|
||||
"type": "string"
|
||||
},
|
||||
"key1": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"key2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key21": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "http_0IIt-",
|
||||
"type": "http",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1598.880308880309,
|
||||
"y": -317.18146718146716
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "HTTP_1",
|
||||
"api": {
|
||||
"method": "GET",
|
||||
"url": {
|
||||
"type": "template",
|
||||
"content": "https://account.aliyun.com"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"bodyType": "JSON"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object"
|
||||
},
|
||||
"statusCode": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"timeout": 10000,
|
||||
"retryTimes": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "variable_zy_Ae",
|
||||
"type": "variable",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2480,
|
||||
"y": 179.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "http响应",
|
||||
"assign": [
|
||||
{
|
||||
"operator": "declare",
|
||||
"left": "http_resp",
|
||||
"right": {
|
||||
"type": "ref",
|
||||
"content": [
|
||||
"http_0IIt-",
|
||||
"headers"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"http_resp": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "code_iyMNK",
|
||||
"type": "script",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1100,
|
||||
"y": 162.3
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "rhai",
|
||||
"inputsValues": {
|
||||
"input": {
|
||||
"type": "constant",
|
||||
"content": ""
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"language": "rhai",
|
||||
"content": "// 修改字段 ctx.addr_name = \"Alice\"; ctx.count = 10; // 添加新字段 ctx.city = \"Beijing\"; // 还可以用下标方式 ctx[\"extra\"] = 123;"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key0": {
|
||||
"type": "string"
|
||||
},
|
||||
"key1": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"key2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key21": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "db_LMgXg",
|
||||
"type": "db",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1606.042471042471,
|
||||
"y": 253.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "DB_1",
|
||||
"db": {
|
||||
"sql": {
|
||||
"type": "template",
|
||||
"content": "SELECT t.* FROM udmin.departments t"
|
||||
},
|
||||
"params": [],
|
||||
"outputKey": "db_response",
|
||||
"connection": {
|
||||
"driver": "mysql",
|
||||
"mode": "fields",
|
||||
"database": "udmin",
|
||||
"host": "127.0.0.1",
|
||||
"port": 3306,
|
||||
"username": "root",
|
||||
"password": "123456"
|
||||
},
|
||||
"output": {
|
||||
"mode": "rows"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"db_response": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"sourceNodeID": "start_0",
|
||||
"targetNodeID": "code_y71Sd"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "variable_zy_Ae",
|
||||
"targetNodeID": "end_0"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "http_0IIt-",
|
||||
"targetNodeID": "variable_zOb3P"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "db_LMgXg",
|
||||
"targetNodeID": "variable_zOb3P"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "variable_zOb3P",
|
||||
"targetNodeID": "variable_zy_Ae"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "code_y71Sd",
|
||||
"targetNodeID": "code_iyMNK"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "code_iyMNK",
|
||||
"targetNodeID": "http_0IIt-"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "code_iyMNK",
|
||||
"targetNodeID": "db_LMgXg"
|
||||
}
|
||||
]
|
||||
}
|
||||
355
docs/flow/code_js1_group.json
Normal file
355
docs/flow/code_js1_group.json
Normal file
@ -0,0 +1,355 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start_0",
|
||||
"type": "start",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 189.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "Start",
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "end_0",
|
||||
"type": "end",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2940,
|
||||
"y": 189.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "End"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "variable_zOb3P",
|
||||
"type": "variable",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2020,
|
||||
"y": 179.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "Variable_1",
|
||||
"assign": [
|
||||
{
|
||||
"operator": "declare",
|
||||
"left": "jss",
|
||||
"right": {
|
||||
"type": "constant",
|
||||
"content": "${\"user_n\"}",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"jss": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "code_y71Sd",
|
||||
"type": "javascript",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 640,
|
||||
"y": 162.3
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "JS",
|
||||
"inputsValues": {
|
||||
"input": {
|
||||
"type": "constant",
|
||||
"content": "",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"extra": {
|
||||
"index": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"language": "javascript",
|
||||
"content": "ctx.vri=\"usertest\"; ctx[\"user_n\"]=\"user_nuser_n\" for (let i = 0; i < 5; i++) { //console.log(`第 ${i} 次循环`); ctx[i]=\"v\"+i; }"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key0": {
|
||||
"type": "string"
|
||||
},
|
||||
"key1": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"key2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key21": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "variable_zy_Ae",
|
||||
"type": "variable",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2480,
|
||||
"y": 179.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "http响应",
|
||||
"assign": [
|
||||
{
|
||||
"operator": "declare",
|
||||
"left": "http_resp",
|
||||
"right": {
|
||||
"type": "ref",
|
||||
"content": [
|
||||
"http_0IIt-",
|
||||
"headers"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"http_resp": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "code_iyMNK",
|
||||
"type": "script",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1100,
|
||||
"y": 162.3
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "rhai",
|
||||
"inputsValues": {
|
||||
"input": {
|
||||
"type": "constant",
|
||||
"content": ""
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"language": "rhai",
|
||||
"content": "// 修改字段 ctx.addr_name = \"Alice\"; ctx.count = 10; // 添加新字段 ctx.city = \"Beijing\"; // 还可以用下标方式 ctx[\"extra\"] = 123;"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key0": {
|
||||
"type": "string"
|
||||
},
|
||||
"key1": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"key2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key21": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "group_o4dbz",
|
||||
"type": "group",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"color": "Green",
|
||||
"title": "Group_2",
|
||||
"parentID": "root",
|
||||
"blockIDs": [
|
||||
"http_0IIt-",
|
||||
"db_LMgXg"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "http_0IIt-",
|
||||
"type": "http",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1598.880308880309,
|
||||
"y": -317.18146718146716
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "HTTP_1",
|
||||
"api": {
|
||||
"method": "GET",
|
||||
"url": {
|
||||
"type": "template",
|
||||
"content": "https://account.aliyun.com"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"bodyType": "JSON"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object"
|
||||
},
|
||||
"statusCode": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"timeout": 10000,
|
||||
"retryTimes": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "db_LMgXg",
|
||||
"type": "db",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1598.880308880309,
|
||||
"y": 179.8
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"title": "DB_1",
|
||||
"db": {
|
||||
"sql": {
|
||||
"type": "template",
|
||||
"content": "SELECT t.* FROM udmin.departments t"
|
||||
},
|
||||
"params": [],
|
||||
"outputKey": "db_response",
|
||||
"connection": {
|
||||
"driver": "mysql",
|
||||
"mode": "fields",
|
||||
"database": "udmin",
|
||||
"host": "127.0.0.1",
|
||||
"port": 3306,
|
||||
"username": "root",
|
||||
"password": "123456"
|
||||
},
|
||||
"output": {
|
||||
"mode": "rows"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"db_response": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"sourceNodeID": "start_0",
|
||||
"targetNodeID": "code_y71Sd"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "variable_zy_Ae",
|
||||
"targetNodeID": "end_0"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "http_0IIt-",
|
||||
"targetNodeID": "variable_zOb3P"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "db_LMgXg",
|
||||
"targetNodeID": "variable_zOb3P"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "variable_zOb3P",
|
||||
"targetNodeID": "variable_zy_Ae"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "code_y71Sd",
|
||||
"targetNodeID": "code_iyMNK"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "code_iyMNK",
|
||||
"targetNodeID": "http_0IIt-"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "code_iyMNK",
|
||||
"targetNodeID": "db_LMgXg"
|
||||
}
|
||||
]
|
||||
}
|
||||
227
docs/flow_architecture.md
Normal file
227
docs/flow_architecture.md
Normal file
@ -0,0 +1,227 @@
|
||||
# Flow 架构与执行图(udmin)
|
||||
|
||||
## 模块架构
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph flow
|
||||
dsl[flow/dsl]
|
||||
domain[flow/domain]
|
||||
context[flow/context]
|
||||
engine[flow/engine]
|
||||
task[flow/task]
|
||||
executors[flow/executors/*]
|
||||
mappers[flow/mappers]
|
||||
log_handler[flow/log_handler]
|
||||
end
|
||||
|
||||
subgraph services
|
||||
svc_flow[services/flow_service]
|
||||
svc_logs[services/flow_run_log_service]
|
||||
end
|
||||
|
||||
subgraph routes
|
||||
r_flows[routes/flows]
|
||||
r_run_logs[routes/flow_run_logs]
|
||||
end
|
||||
|
||||
subgraph middlewares
|
||||
mw_sse[middlewares/sse]
|
||||
mw_ws[middlewares/ws]
|
||||
mw_jwt[middlewares/jwt]
|
||||
mw_http[middlewares/http_client]
|
||||
end
|
||||
|
||||
subgraph infra
|
||||
db[db]
|
||||
redis[redis]
|
||||
models_flow[models/flow]
|
||||
models_run[models/flow_run_log]
|
||||
end
|
||||
|
||||
r_flows --> svc_flow
|
||||
r_run_logs --> svc_logs
|
||||
svc_flow --> dsl
|
||||
svc_flow --> mappers
|
||||
dsl --> domain
|
||||
mappers --> context
|
||||
svc_flow --> engine
|
||||
engine --> task
|
||||
task --> executors
|
||||
executors --> mw_http
|
||||
engine --> log_handler
|
||||
log_handler --> svc_logs
|
||||
mw_sse -.events.-> r_flows
|
||||
mw_ws -.events.-> r_flows
|
||||
svc_flow --> models_flow
|
||||
svc_logs --> models_run
|
||||
models_flow --> db
|
||||
models_run --> db
|
||||
mw_jwt --> r_flows
|
||||
redis -.token check.-> mw_jwt
|
||||
```
|
||||
|
||||
引用:
|
||||
- DSL/Design 构建 `backend/src/flow/dsl.rs:60-93,138-170,172-203,246-303`
|
||||
- 领域模型 `backend/src/flow/domain.rs:20-28,31-36,39-47,60-68`
|
||||
- 上下文与事件 `backend/src/flow/context.rs:29-45`
|
||||
- 引擎驱动 `backend/src/flow/engine.rs:117-209,213-577`
|
||||
- 编排服务 `backend/src/services/flow_service.rs:285-305,342-349,351-365,366-399`
|
||||
- 路由入口 `backend/src/routes/flows.rs:26-35,101-133`
|
||||
- 运行日志服务 `backend/src/services/flow_run_log_service.rs:46-63,74-131`
|
||||
|
||||
## 请求/运行编排时序
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant R as routes.flows
|
||||
participant S as services.flow_service
|
||||
participant D as flow.dsl & mappers
|
||||
participant E as FlowEngine
|
||||
participant L as flow.log_handler
|
||||
participant M as models(flow/flow_run_log)
|
||||
participant SSE as middlewares.sse/ws
|
||||
|
||||
C->>R: POST /flows/{id}/run
|
||||
R->>S: run(id, input)
|
||||
S->>M: get flow doc
|
||||
alt design_json
|
||||
S->>D: chain_from_design_json(design)
|
||||
D-->>S: ChainDef
|
||||
S->>D: ctx_from_design_json(design)
|
||||
D-->>S: ctx supplement
|
||||
else YAML
|
||||
S->>D: parse FlowDSL (YAML)
|
||||
D-->>S: ChainDef
|
||||
end
|
||||
S->>E: drive(chain, ctx, opts)
|
||||
E-->>S: (ctx, logs)
|
||||
S->>L: log_success/log_error
|
||||
L->>M: insert flow_run_log
|
||||
E-->>SSE: emit node/done/error (stream)
|
||||
S-->>R: RunResult(ok, ctx, logs)
|
||||
R-->>C: ApiResponse
|
||||
```
|
||||
|
||||
关键实现:
|
||||
- 解析与补充 `backend/src/services/flow_service.rs:285-305`
|
||||
- 引擎驱动 `backend/src/services/flow_service.rs:342-349`
|
||||
- 日志入库与事件推送 `backend/src/flow/log_handler.rs`
|
||||
|
||||
## 引擎执行图(含执行模式)
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[选择起点: Start/入度=0/首节点] --> B{合流屏障}
|
||||
B -- 未达成/去重 --> Z[跳过当前节点]
|
||||
B -- 达成 --> C{任务?}
|
||||
C -- 否 --> D[选择后继]
|
||||
C -- 是 --> E{执行模式}
|
||||
E -- Sync --> ES[同步执行任务; 写回 ctx]
|
||||
E -- Async(Fire&Forget) --> EA[异步执行; 追踪句柄]
|
||||
E -- Queued(组队列) --> EQ[组信号量=1; 获取后执行]
|
||||
E -- Bounded(限并发) --> EB[组信号量=n; 获取后执行]
|
||||
ES --> D
|
||||
EA --> D
|
||||
EQ --> D
|
||||
EB --> D
|
||||
D -->|条件节点| F{条件评估 JSON/Rhai}
|
||||
F -- 通过 --> G[进入后继]
|
||||
F -- 不通过/无匹配 --> H[挑选无条件后继或停止]
|
||||
D -->|非条件| G
|
||||
G --> I{是否 End}
|
||||
I -- 是 --> J[FlowEnd 等待组策略; 推送事件; 结束]
|
||||
I -- 否 --> K{多分支?}
|
||||
K -- 单分支 --> L[计算组离开; BranchExit 等待]
|
||||
K -- 多分支 --> M[并行驱动其他分支]
|
||||
L --> A
|
||||
M --> L --> A
|
||||
```
|
||||
|
||||
参考:
|
||||
- 模式枚举与选项 `backend/src/flow/context.rs:9-15,29-45`
|
||||
- 合流与分支 `backend/src/flow/engine.rs:245-268,502-574`
|
||||
- 组等待策略 `backend/src/flow/engine.rs:62-115,559-573`
|
||||
|
||||
## DSL/Design 转换
|
||||
```mermaid
|
||||
flowchart LR
|
||||
X0[Design JSON 输入] --> X1[校验: 唯一ID/Start&End/合法边]
|
||||
X1 --> X2[兼容 sourcePortID → source_port_id]
|
||||
X2 --> X3[推断节点 kind/name/task]
|
||||
X3 --> X4[解析组: members/parentID/awaitPolicy]
|
||||
X4 --> X5[组装条件: AND组/端口匹配/启发式]
|
||||
X5 --> X6["生成 ChainDef: nodes links groups"]
|
||||
X0 --> Y1[mappers: 节点配置提取]
|
||||
Y1 --> Y2[ctx.nodes.<id>.<executor>]
|
||||
```
|
||||
|
||||
实现位置:`backend/src/flow/dsl.rs:138-170,172-203,246-303` 与 `backend/src/flow/mappers.rs:27-95`
|
||||
|
||||
## 数据模型(ER)
|
||||
```mermaid
|
||||
erDiagram
|
||||
FLOWS {
|
||||
bigint id PK
|
||||
varchar name
|
||||
text yaml
|
||||
text design_json
|
||||
varchar code
|
||||
varchar remark
|
||||
timestamp created_at
|
||||
timestamp updated_at
|
||||
}
|
||||
FLOW_RUN_LOGS {
|
||||
bigint id PK
|
||||
bigint flow_id FK
|
||||
varchar flow_code
|
||||
text input
|
||||
text output
|
||||
bool ok
|
||||
text logs
|
||||
bigint user_id
|
||||
varchar username
|
||||
timestamp started_at
|
||||
bigint duration_ms
|
||||
timestamp created_at
|
||||
}
|
||||
FLOWS ||--o{ FLOW_RUN_LOGS : has
|
||||
```
|
||||
|
||||
## 事件与日志通道
|
||||
```mermaid
|
||||
graph LR
|
||||
engine_push[engine.push_and_emit] --> sse_emit[middlewares.sse.emit_*]
|
||||
engine_push --> ws_emit[middlewares.ws.forward]
|
||||
sse_emit --> client[前端]
|
||||
ws_emit --> client
|
||||
engine_push --> log_handler_db[DatabaseLogHandler]
|
||||
engine_push --> log_handler_sse[SseLogHandler]
|
||||
log_handler_db --> run_log_service
|
||||
log_handler_sse --> run_log_service
|
||||
run_log_service --> db
|
||||
```
|
||||
|
||||
## 执行器生态
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph executors
|
||||
http
|
||||
db
|
||||
variable
|
||||
script_rhai
|
||||
script_js
|
||||
script_python
|
||||
condition
|
||||
end
|
||||
ctx_nodes[ctx.nodes.<id>.*] --> executors
|
||||
ctx_global[ctx.* 顶层] --> executors
|
||||
http --> http_out[(http_response)]
|
||||
db --> db_out[(db_response)]
|
||||
variable --> var_out[(ctx键值写回)]
|
||||
condition --> route[分支选择]
|
||||
```
|
||||
|
||||
注意点:写回策略与幂等,参考 `backend/src/flow/engine.rs:294-375`
|
||||
|
||||
---
|
||||
|
||||
以上图示与说明用于快速理解 Flow 的完整链路:从 DSL/Design 解析到引擎驱动与事件/日志通道,再到数据持久化与执行器生态。结合上文的代码引用,可在 IDE 中跳转到具体实现进行深度阅读。
|
||||
Reference in New Issue
Block a user