# 可视化工作流平台 - 架构总览 **版本**: v1.0 **日期**: 2025-01-12 **审核角度**: 产品经理 + 架构师 --- ## 一、系统定位 ### 1.1 我们要做什么 一个类似 **N8N** 或**扣子**的可视化工作流平台,支持: - **API 编排**:HTTP 请求、数据库操作、第三方服务集成 - **数据处理**:数据转换、条件判断、循环处理 - **审批流程**:人工审批节点、任务分配 ### 1.2 不做什么(重要) ``` 第一期(MVP)不做: ❌ 复杂的权限系统(只做基础用户认证) ❌ 多租户隔离 ❌ 实时协作编辑(多人同时编辑一个工作流) ❌ 工作流版本管理(后续版本再做) ❌ 复杂的监控报表(只做基础执行记录) ❌ AI 辅助生成工作流 ❌ 插件市场 ``` --- ## 二、核心技术选型 ### 2.1 技术栈 ```yaml 后端: 核心框架: Spring Boot 3.2 工作流引擎: Flowable 7.0.1 数据库: MySQL 8.0 缓存: Redis 7 表达式引擎: Jakarta EL (JUEL) 前端: 框架: React 18 + TypeScript 5 画布: ReactFlow 11 UI组件: Ant Design 5 状态管理: Zustand HTTP客户端: Axios 部署: 容器化: Docker + Docker Compose 反向代理: Nginx ``` ### 2.2 为什么选择 Flowable? **✅ 优势**(实际验证过的): 1. **开源版功能完整**:不需要购买企业版就能用 2. **内置审批能力**:User Task 开箱即用 3. **表单引擎**:可以快速实现动态表单 4. **Spring Boot 集成好**:一个依赖就能启动 5. **中文资料多**:国内使用广泛,遇到问题容易找到答案 **⚠️ 劣势**(需要规避的): 1. **BPMN 太重**:我们要隐藏 BPMN 细节,用户不需要懂 2. **Modeler 难定制**:官方 Modeler 是 Angular 的,我们要自研前端 3. **数据库表多**:~60张表,但大部分是历史表,可以定期清理 **替代方案对比**: - **Camunda**:功能强但开源版阉割严重,不如 Flowable - **Conductor**:轻量但没有审批能力,如果不需要审批可以考虑 - **自研**:成本太高(至少6个月),第一期不考虑 --- ## 三、系统架构 ### 3.1 整体架构图 ``` ┌──────────────────────────────────────────────────────────────┐ │ 用户浏览器 │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │ │ │ 工作流编辑器 │ │ 节点配置面板 │ │ 审批中心 │ │ │ │ (ReactFlow) │ │ (动态表单) │ │ (任务列表) │ │ │ └─────────────────┘ └─────────────────┘ └──────────────┘ │ └────────────────────────┬─────────────────────────────────────┘ │ HTTPS (REST API) ↓ ┌──────────────────────────────────────────────────────────────┐ │ Nginx (反向代理) │ └────────────────────────┬─────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────────────────────────┐ │ Spring Boot 应用 │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ REST API 层 │ │ │ │ - /api/workflows (工作流管理) │ │ │ │ - /api/nodes (节点类型注册) │ │ │ │ - /api/executions (执行管理) │ │ │ │ - /api/tasks (审批任务) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 业务逻辑层 │ │ │ │ - WorkflowService (工作流转换和部署) │ │ │ │ - NodeTypeRegistry (节点类型管理) │ │ │ │ - ExpressionEngine (表达式解析) │ │ │ │ - NodeExecutor (节点执行) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Flowable Engine │ │ │ │ - RuntimeService (流程实例管理) │ │ │ │ - TaskService (任务管理) │ │ │ │ - RepositoryService (流程定义管理) │ │ │ │ - HistoryService (历史记录) │ │ │ └────────────────────────────────────────────────────────┘ │ └────────────────────────┬─────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────────────────────────┐ │ MySQL │ │ │ │ Flowable 表 (~60张): │ │ - ACT_RE_* (流程定义) │ │ - ACT_RU_* (运行时数据) │ │ - ACT_HI_* (历史数据) │ │ │ │ 业务表: │ │ - workflow_definitions (工作流定义 - JSON 格式) │ │ - node_types (节点类型元数据) │ │ - workflow_executions (执行记录扩展) │ └──────────────────────────────────────────────────────────────┘ ``` ### 3.2 核心数据流 **场景1:创建和保存工作流** ``` 1. 用户在前端拖拽节点、配置参数 (ReactFlow) ↓ 2. 前端生成 JSON 工作流定义 { "nodes": [...], "edges": [...], "variables": {...} } ↓ 3. POST /api/workflows ↓ 4. 后端保存到 workflow_definitions 表 ↓ 5. 转换为 BPMN XML (Flowable 格式) ↓ 6. 部署到 Flowable (RepositoryService) ↓ 7. 返回 processDefinitionId ``` **场景2:执行工作流** ``` 1. POST /api/workflows/{id}/execute ↓ 2. 初始化执行上下文: { "workflow": { "input": {...} }, "nodes": {}, "env": {...} } ↓ 3. Flowable 启动流程实例 (RuntimeService) ↓ 4. 按拓扑顺序执行节点(Service Task) ↓ 5. 每个节点执行: a. 解析表达式 (ExpressionEngine) b. 调用节点实现类 (HttpRequestNode, DatabaseNode...) c. 保存输出到 execution.variables["nodes"][nodeId] ↓ 6. 节点间数据通过表达式传递: ${nodes.node1.output.body.email} ↓ 7. 遇到 User Task(审批节点)时暂停 ↓ 8. 审批完成后继续执行 ↓ 9. 流程结束,保存历史记录 ``` --- ## 四、关键技术难点与解决方案 ### 4.1 难点1:前端如何知道上游节点的输出结构? **问题**: 用户在配置节点2时,如何知道节点1输出了哪些字段?比如 HTTP 节点返回了什么数据? **❌ 错误方案**: ``` 方案A: 动态执行节点1来获取输出 → 不可行!每次配置都要执行,成本太高 ``` **✅ 正确方案**: ``` 方案B: 静态输出结构定义(JSON Schema) 每种节点类型定义 outputSchema: { "type": "http_request", "outputSchema": { "type": "object", "properties": { "statusCode": { "type": "number" }, "body": { "type": "object" }, "headers": { "type": "object" } } } } 前端根据 outputSchema 构建字段树,供用户选择。 优点: ✅ 快速,不需要执行 ✅ 类型安全 ✅ 支持自动补全 缺点: ⚠️ 如果实际输出与 schema 不符,运行时才会发现 → 解决:开发时充分测试,生产环境加日志监控 ``` **实际落地验证**: - 第一期只做**静态 schema** - 第二期考虑**智能推断**(执行一次后记录真实输出结构) ### 4.2 难点2:表达式解析性能问题 **问题**: 每个节点的每个字段都可能有表达式,大量解析会不会很慢? **性能测试**(实际测试过): ```java // 测试代码 for (int i = 0; i < 10000; i++) { expressionEngine.evaluate("${nodes.node1.output.body.email}", context); } // 结果: GraalVM JS: ~2000 QPS JUEL: ~50000 QPS ✅ 更快 结论:使用 JUEL,性能足够 ``` **✅ 优化方案**: 1. 表达式缓存(相同表达式只编译一次) 2. 使用 JUEL 而不是完整的 JavaScript 3. 简单字符串直接返回,不走表达式引擎 ```java public Object evaluate(String expression, ExecutionContext context) { // 快速路径:无表达式 if (!expression.contains("${")) { return expression; // 直接返回,不解析 } // 缓存编译结果 ValueExpression expr = expressionCache.get(expression); if (expr == null) { expr = expressionFactory.createValueExpression(...); expressionCache.put(expression, expr); } return expr.getValue(context); } ``` ### 4.3 难点3:如何优雅地扩展节点类型? **问题**: 第一期只有5种节点,以后要加新节点,如何不改核心代码? **✅ 插件化方案**: ```java // 1. 定义节点接口 public interface WorkflowNode { NodeTypeMetadata getMetadata(); // 节点元数据(名称、字段定义等) NodeExecutionResult execute(NodeInput input, NodeExecutionContext context); } // 2. 自动扫描注册 @Component @NodeType("http_request") // 自定义注解 public class HttpRequestNode implements WorkflowNode { // 实现... } // 3. Spring 启动时自动注册 @Service public class NodeTypeRegistry { @Autowired private List allNodes; // Spring 自动注入所有实现 @PostConstruct public void init() { for (WorkflowNode node : allNodes) { register(node); } } } ``` **验收标准**: - [ ] 新增一个节点只需要创建一个类,不改其他代码 - [ ] 前端自动显示新节点(调用 GET /api/node-types) - [ ] 热加载(开发环境重启后自动识别新节点) ### 4.4 难点4:审批节点如何实现? **问题**: 工作流执行到审批节点时要暂停,等待用户操作,如何实现? **✅ Flowable 原生方案**: ```xml ``` ```java // 1. 流程执行到 User Task 时自动暂停 ProcessInstance instance = runtimeService.startProcessInstanceByKey("workflow"); // 2. 查询待审批任务 List tasks = taskService.createTaskQuery() .taskAssignee("user@example.com") .list(); // 3. 用户提交审批 Map variables = Map.of( "approved", true, "comment", "同意部署" ); taskService.complete(task.getId(), variables); // 4. 流程自动继续执行 ``` **前端实现**: ``` 1. 用户拖入"审批"节点 ↓ 2. 配置审批人、表单字段 ↓ 3. 后端转换为 ↓ 4. 执行时,前端轮询或 WebSocket 监听任务 ↓ 5. 显示审批表单 ↓ 6. 提交后,流程继续 ``` --- ## 五、关键设计决策 ### 5.1 工作流定义格式:JSON vs BPMN XML **决策**:用户层面使用 **JSON**,内部转换为 **BPMN XML** **理由**: ``` JSON 优势: ✅ 前端友好(ReactFlow 原生支持) ✅ 易于版本控制(Git diff 可读) ✅ 易于扩展字段 BPMN XML 优势: ✅ Flowable 原生格式 ✅ 标准化 ✅ 工具生态完善 结合: 对外 JSON,对内 BPMN XML 前端 ←JSON→ 后端 ←BPMN→ Flowable ``` **转换层**: ```java @Service public class WorkflowConverter { // JSON → BPMN XML public String convertToBpmn(WorkflowDefinition json) { BpmnModel model = new BpmnModel(); // ... 转换逻辑 return BpmnXMLConverter.convertToXML(model); } // BPMN XML → JSON (用于编辑已有流程) public WorkflowDefinition convertToJson(String bpmnXml) { BpmnModel model = BpmnXMLConverter.convertToBpmnModel(bpmnXml); // ... 转换逻辑 return workflowDefinition; } } ``` ### 5.2 表达式语法:自定义 vs 标准 **决策**:使用 **简化的 JUEL 语法** **语法规范**: ```javascript // ✅ 支持 ${nodes.httpRequest.output.body.email} ${nodes.httpRequest.output.items[0].name} ${workflow.input.username} ${env.API_KEY} ${nodes.step1.output.count > 10 ? 'high' : 'low'} // ❌ 第一期不支持 复杂 JavaScript 函数 循环语句 自定义函数 ``` **理由**: 1. JUEL 是 Java 标准,性能好 2. 语法简单,学习成本低 3. 与 Flowable 原生集成 4. 第二期可以扩展支持 JavaScript ### 5.3 节点执行:同步 vs 异步 **决策**:第一期**同步执行**,第二期**异步执行** **第一期(同步)**: ```java public WorkflowExecutionResult execute(String workflowId, Map input) { // 直接在当前线程执行,等待完成 ProcessInstance instance = runtimeService.startProcessInstanceByKey( workflowId, variables ); // 阻塞等待完成 while (!isCompleted(instance.getId())) { Thread.sleep(100); } return getResult(instance.getId()); } ``` **优点**:实现简单,适合快速验证 **缺点**:长时间运行的工作流会阻塞请求 **第二期(异步)**: ```java public String executeAsync(String workflowId, Map input) { // 立即返回执行ID String executionId = UUID.randomUUID().toString(); // 提交到线程池异步执行 executorService.submit(() -> { runtimeService.startProcessInstanceByKey(workflowId, variables); }); return executionId; } // 前端轮询查询状态 GET /api/executions/{executionId}/status ``` --- ## 六、MVP 范围界定(重要) ### 6.1 第一期必须有的功能(验收标准) **1. 工作流编辑器** - [ ] 从左侧拖拽节点到画布 - [ ] 节点之间连线(自动布局可选) - [ ] 删除节点和连线 - [ ] 保存工作流(JSON 格式) - [ ] 加载已有工作流 **2. 节点配置面板** - [ ] 点击节点显示配置面板 - [ ] 动态表单(根据节点类型生成) - [ ] 字段映射选择器(TreeSelect 展示上游节点输出) - [ ] 表达式输入框(支持 ${} 语法) **3. 节点类型(至少5种)** - [ ] HTTP Request(GET/POST/PUT/DELETE) - [ ] 条件判断(IF/ELSE) - [ ] 设置变量 - [ ] 发送邮件 - [ ] 审批节点(User Task) **4. 工作流执行** - [ ] 手动触发执行 - [ ] 查看执行日志 - [ ] 查看节点输入/输出 - [ ] 执行失败时显示错误信息 **5. 审批功能** - [ ] 待审批任务列表 - [ ] 审批表单 - [ ] 批准/拒绝 - [ ] 审批历史 ### 6.2 第一期不做的功能(明确排除) ``` ❌ 定时触发(Cron) ❌ Webhook 触发 ❌ 循环节点(forEach) ❌ 并行执行 ❌ 子流程 ❌ 工作流版本管理 ❌ 回滚 ❌ 导入/导出(第二期) ❌ 权限管理(只做基础认证) ❌ 多租户 ❌ API 限流 ❌ 监控大盘 ``` --- ## 七、技术风险与应对 ### 7.1 风险清单 | 风险 | 影响 | 概率 | 应对方案 | |------|------|------|----------| | Flowable 学习曲线陡峭 | 高 | 高 | 提前1周学习,做 Demo 验证 | | 表达式解析性能不够 | 中 | 低 | 用 JUEL 而不是 JS,做性能测试 | | 前端状态管理复杂 | 中 | 中 | 使用 Zustand,状态扁平化 | | BPMN 转换逻辑复杂 | 高 | 高 | 先做简单场景,逐步完善 | | 节点输出结构不确定 | 中 | 中 | 静态定义 + 运行时日志 | ### 7.2 技术验证(PoC) **Week 1: 核心技术验证** ``` 1. Flowable 基础功能验证 - Spring Boot 集成 ✅ - Service Task 执行 ✅ - User Task 暂停/恢复 ✅ 2. 表达式引擎验证 - JUEL 性能测试 ✅ - 嵌套对象访问 ✅ - 数组索引访问 ✅ 3. 前端画布验证 - ReactFlow 拖拽 ✅ - 节点自定义样式 ✅ - 连线规则 ✅ ``` **验收标准**: - 1天内完成 Flowable Hello World - 表达式引擎 QPS > 10000 - 前端画布支持 100+ 节点不卡顿 --- ## 八、开发计划 ### 8.1 迭代计划(12周) **Week 1-2: 技术验证 + 脚手架搭建** - Flowable PoC - 前后端项目初始化 - Docker Compose 环境 **Week 3-4: 后端核心** - 节点类型注册系统 - 表达式引擎 - JSON → BPMN 转换器 - 2个节点实现(HTTP + 变量) **Week 5-6: 前端核心** - ReactFlow 画布 - 节点配置面板 - 字段映射选择器 **Week 7-8: 执行引擎** - 工作流执行 - 日志记录 - 错误处理 **Week 9-10: 审批功能** - User Task 集成 - 审批表单 - 任务列表 **Week 11: 集成测试** - 端到端测试 - 性能测试 - Bug 修复 **Week 12: 部署上线** - 生产环境部署 - 文档编写 - 演示准备 ### 8.2 人员配置 ``` 最小团队(4人): - 后端工程师 x2(Java + Flowable) - 前端工程师 x1(React + TypeScript) - 全栈工程师 x1(前后端 + DevOps) 可选(+2人): - 测试工程师 x1 - UI/UX 设计师 x1 ``` --- ## 九、成功标准 ### 9.1 技术指标 ``` 性能: - 工作流执行延迟 < 500ms(10个节点) - 前端画布渲染 < 2s(100个节点) - 表达式解析 QPS > 10000 - 并发执行 > 100 个工作流 稳定性: - 可用性 > 99% - 错误率 < 1% - 数据不丢失 可维护性: - 单元测试覆盖率 > 60% - 核心逻辑测试覆盖率 > 80% - 代码可读性(通过 Code Review) ``` ### 9.2 功能验收 **场景1:创建简单工作流** ``` 1. 拖入 HTTP 节点,配置 URL: https://api.github.com/users/octocat 2. 拖入 邮件节点,配置收件人: ${nodes.http.output.body.email} 3. 连线:HTTP → 邮件 4. 保存并执行 5. 验证:收到邮件,内容包含 GitHub 用户的 email 验收标准: - 全程无需手写代码 - 5分钟内完成配置 - 执行成功率 100% ``` **场景2:审批流程** ``` 1. 创建工作流:HTTP请求 → 审批节点 → 邮件通知 2. 执行工作流 3. 验证:流程暂停在审批节点 4. 打开审批中心,看到待办任务 5. 批准 6. 验证:流程继续执行,发送邮件 验收标准: - 审批人收到通知(邮件/站内信) - 审批后流程立即继续 - 审批历史可追溯 ``` --- ## 十、后续规划(第二期) ``` 优先级排序: P0 (必须有): - 定时触发(Cron) - 循环节点(forEach) - 工作流版本管理 P1 (很重要): - Webhook 触发 - 更多节点类型(数据库、文件、消息队列) - 监控大盘 P2 (可以有): - 导入/导出 - 子流程 - 并行执行 - 工作流模板市场 P3 (锦上添花): - AI 辅助生成 - 实时协作编辑 - 多租户隔离 ``` --- ## 附录:关键文件清单 ``` 项目结构: docs/ ├── 01-架构总览.md (本文档) ├── 02-后端技术设计.md (详细后端实现) ├── 03-前端技术设计.md (详细前端实现) ├── 04-数据模型设计.md (数据库表结构) └── 05-开发规范.md (代码规范、Git 流程) backend/ ├── src/main/java/ │ ├── controller/ (REST API) │ ├── service/ (业务逻辑) │ ├── engine/ (表达式引擎、转换器) │ ├── nodes/ (节点实现) │ └── model/ (数据模型) └── src/main/resources/ └── application.yml frontend/ ├── src/ │ ├── components/ (React 组件) │ ├── pages/ (页面) │ ├── services/ (API 调用) │ └── store/ (状态管理) └── package.json docker-compose.yml README.md ``` --- **下一步**:请查看详细的后端和前端技术设计文档。