# Phase 4: 工作流引擎模块 — 实施计划 ## Context Phase 1(基础设施)、Phase 2(身份与权限)和 Phase 3(系统配置)已完成。Phase 4 需要实现工作流引擎模块(erp-workflow),提供流程定义、流程实例管理、任务审批、Token 驱动的执行引擎和可视化流程设计器。当前 `erp-workflow` 仅为 placeholder。 用户选择"完整实施"方案,包括 BPMN 子集解析器、Token 驱动执行引擎、完整 CRUD 端点和 React Flow 可视化设计器。 --- ## Task 1: erp-workflow 骨架 + WorkflowState + Error **目标:** 创建可编译的最小 crate,注册到 erp-server。 **创建/修改文件:** - 修改: `crates/erp-workflow/Cargo.toml` — 添加完整依赖 - 修改: `crates/erp-workflow/src/lib.rs` — 模块声明 + re-export - 创建: `crates/erp-workflow/src/workflow_state.rs` — `WorkflowState { db, event_bus }` - 创建: `crates/erp-workflow/src/error.rs` — WorkflowError 枚举 - 创建: `crates/erp-workflow/src/module.rs` — WorkflowModule + ErpModule trait - 创建: `crates/erp-workflow/src/dto.rs` — 占位 - 创建: `crates/erp-workflow/src/entity/mod.rs` — 占位 - 创建: `crates/erp-workflow/src/service/mod.rs` — 占位 - 创建: `crates/erp-workflow/src/handler/mod.rs` — 占位 - 创建: `crates/erp-workflow/src/engine/mod.rs` — 占位 - 修改: `crates/erp-server/Cargo.toml` — 确认 erp-workflow 依赖 - 修改: `crates/erp-server/src/state.rs` — 添加 `FromRef` for WorkflowState - 修改: `crates/erp-server/src/main.rs` — 注册 WorkflowModule **依赖:** ```toml erp-core.workspace = true tokio = { workspace = true, features = ["full"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } uuid = { workspace = true, features = ["v7", "serde"] } chrono = { workspace = true, features = ["serde"] } axum = { workspace = true } sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls", "with-uuid", "with-chrono", "with-json"] } tracing = { workspace = true } thiserror = { workspace = true } utoipa = { workspace = true, features = ["uuid", "chrono"] } async-trait = { workspace = true } ``` **验证:** `cargo check` 通过 --- ## Task 2: 数据库迁移(5 张表) **创建文件(`crates/erp-server/migration/src/`):** - `m20260412_000018_create_process_definitions.rs` - `m20260412_000019_create_process_instances.rs` - `m20260412_000020_create_tokens.rs` - `m20260412_000021_create_tasks.rs` - `m20260412_000022_create_process_variables.rs` - 修改: `lib.rs` — 注册新迁移 ### process_definitions | 列 | 类型 | 说明 | |---|---|---| | id | uuid PK | UUIDv7 | | tenant_id | uuid NOT NULL | 租户 ID | | name | string NOT NULL | 流程名称 | | key | string NOT NULL | 流程唯一编码 | | version | int NOT NULL DEFAULT 1 | 版本号 | | category | string NULL | 分类(如 leave, expense) | | description | text NULL | 描述 | | nodes | jsonb NOT NULL DEFAULT '[]' | 节点定义(BPMN 子集) | | edges | jsonb NOT NULL DEFAULT '[]' | 连线定义 | | status | string NOT NULL DEFAULT 'draft' | draft/published/deprecated | | + 标准审计字段 | | | | 唯一索引: | `(tenant_id, key, version) WHERE deleted_at IS NULL` | | ### process_instances | 列 | 类型 | 说明 | |---|---|---| | id | uuid PK | UUIDv7 | | tenant_id | uuid NOT NULL | | | definition_id | uuid NOT NULL FK → process_definitions | | | business_key | string NULL | 业务关联键(如请假单 ID) | | status | string NOT NULL DEFAULT 'running' | running/suspended/completed/terminated | | started_by | uuid NOT NULL | 发起人 user_id | | started_at | timestamptz NOT NULL DEFAULT NOW() | | | completed_at | timestamptz NULL | | | + 标准审计字段 | | | | 索引: | `idx_instances_status (tenant_id, status)` | | ### tokens | 列 | 类型 | 说明 | |---|---|---| | id | uuid PK | UUIDv7 | | tenant_id | uuid NOT NULL | | | instance_id | uuid NOT NULL FK → process_instances | | | node_id | string NOT NULL | 当前所在节点 ID | | status | string NOT NULL DEFAULT 'active' | active/consumed/terminated | | created_at | timestamptz NOT NULL DEFAULT NOW() | | | consumed_at | timestamptz NULL | | | 索引: | `idx_tokens_instance (instance_id)` | | ### tasks | 列 | 类型 | 说明 | |---|---|---| | id | uuid PK | UUIDv7 | | tenant_id | uuid NOT NULL | | | instance_id | uuid NOT NULL FK → process_instances | | | token_id | uuid NOT NULL FK → tokens | | | node_id | string NOT NULL | 对应的流程节点 | | node_name | string NULL | 节点名称(冗余,便于查询) | | assignee_id | uuid NULL | 指定处理人 | | candidate_groups | jsonb NULL | 候选角色组 | | status | string NOT NULL DEFAULT 'pending' | pending/approved/rejected/delegated | | outcome | string NULL | 审批结果 | | form_data | jsonb NULL | 表单数据 | | due_date | timestamptz NULL | 到期时间 | | completed_at | timestamptz NULL | | | + 标准审计字段 | | | | 索引: | `idx_tasks_assignee (tenant_id, assignee_id, status)` | | | 索引: | `idx_tasks_instance (instance_id)` | | ### process_variables | 列 | 类型 | 说明 | |---|---|---| | id | uuid PK | UUIDv7 | | tenant_id | uuid NOT NULL | | | instance_id | uuid NOT NULL FK → process_instances | | | name | string NOT NULL | 变量名 | | var_type | string NOT NULL DEFAULT 'string' | string/number/boolean/date/json | | value_string | text NULL | | | value_number | double precision NULL | | | value_boolean | boolean NULL | | | value_date | timestamptz NULL | | | 唯一索引: | `(instance_id, name)` | | **验证:** `cargo run -p erp-server` 启动后 `\dt` 可见新表 --- ## Task 3: SeaORM Entity **创建文件(`crates/erp-workflow/src/entity/`):** - `mod.rs` — 导出所有实体 - `process_definition.rs` - `process_instance.rs` - `token.rs` - `task.rs` - `process_variable.rs` **模式:** 参考 `erp-config/src/entity/numbering_rule.rs`,包含 Relation 和 Related。 **验证:** `cargo check` 通过 --- ## Task 4: DTO 定义 **修改文件:** `crates/erp-workflow/src/dto.rs` **包含:** - 流程定义:`ProcessDefinitionResp`, `CreateProcessDefinitionReq`, `UpdateProcessDefinitionReq`, `PublishDefinitionReq` - 流程实例:`ProcessInstanceResp`, `StartInstanceReq` - 任务:`TaskResp`, `CompleteTaskReq`(含 outcome + form_data) - 流程变量:`ProcessVariableResp`, `SetVariableReq` - 流程图:`NodeDef`(BPMN 节点), `EdgeDef`(连线), `FlowDiagram`(完整图) **节点类型:** StartEvent, EndEvent, UserTask, ServiceTask, ExclusiveGateway, ParallelGateway **连线条件:** `condition` 字段为可选表达式字符串(如 `amount > 1000`) **验证:** `cargo check` 通过 --- ## Task 5: BPMN 解析器 + 表达式引擎 **创建文件(`crates/erp-workflow/src/engine/`):** - `model.rs` — 流程图内存模型(FlowDiagram, FlowNode, FlowEdge, NodeType 枚举) - `parser.rs` — 解析 JSON nodes/edges 为内存模型,验证流程图合法性 - `expression.rs` — 简单表达式求值器(支持比较运算和流程变量引用) **关键逻辑:** - `FlowDiagram::validate()` — 检查:恰好 1 个 StartEvent,至少 1 个 EndEvent,无悬空连线,网关分支/汇合配对 - `ExpressionEvaluator::eval(expr, variables) -> bool` — 支持 `var > 1000`, `status == "approved"`, `amount <= budget` 格式 - 解析器将 `nodes` 和 `edges` jsonb 反序列化为 `Vec` 和 `Vec` **验证:** 单元测试覆盖:合法流程验证、缺少 StartEvent 报错、表达式求值 --- ## Task 6: Token 驱动执行引擎 **创建文件:** `crates/erp-workflow/src/engine/executor.rs` **核心逻辑:** ``` start(instance_id, definition) → 在 StartEvent 创建 token advance(token_id, instance_id, definition, variables) → 消费当前 token,在下一节点创建新 token - 到达 EndEvent → 消费 token,检查实例是否所有 token 都完成 → 完成实例 - 到达 UserTask → 创建 token + 创建 task 记录 - 到达 ServiceTask → 创建 token + 执行动作(占位,发布事件) - 到达 ExclusiveGateway → 求值条件,选择一条分支 - 到达 ParallelGateway(分支)→ 为每条出边创建 token - 到达 ParallelGateway(汇合)→ 消费当前 token,等待所有入边 token 到达后创建新 token ``` **并发安全:** 使用 `pg_advisory_xact_lock` 保护 token 操作(参考 NumberingService 模式) **验证:** 单元测试覆盖:直线流程、排他网关分支、并行网关分支与汇合 --- ## Task 7: Service 层 **创建文件(`crates/erp-workflow/src/service/`):** - `definition_service.rs` — 流程定义 CRUD + 发布 + 版本管理 - `instance_service.rs` — 启动实例 + 查询 + 挂起/恢复/终止 - `task_service.rs` — 查询待办 + 完成任务 + 委派 + 查询已办 **关键逻辑:** - `DefinitionService::publish` — draft → published,验证流程图合法性 - `InstanceService::start` — 创建实例 + 初始化变量 + 调用 executor.start - `TaskService::complete` — 更新 task 状态 + 调用 executor.advance + 处理下一节点 --- ## Task 8: Handler 层 **创建文件(`crates/erp-workflow/src/handler/`):** - `definition_handler.rs` — 5 个端点 - `instance_handler.rs` — 4 个端点 - `task_handler.rs` — 4 个端点 **端点映射:** ``` GET/POST /workflow/definitions GET /workflow/definitions/{id} PUT /workflow/definitions/{id} POST /workflow/definitions/{id}/publish POST /workflow/instances GET /workflow/instances GET /workflow/instances/{id} POST /workflow/instances/{id}/suspend POST /workflow/instances/{id}/terminate GET /workflow/tasks/pending — 我的待办 GET /workflow/tasks/completed — 我的已办 POST /workflow/tasks/{id}/complete — 完成任务 POST /workflow/tasks/{id}/delegate — 委派任务 ``` **RBAC:** 所有端点使用 `require_permission(&ctx, "workflow:xxx")` --- ## Task 9: 模块注册 + 种子数据 **修改文件:** - `crates/erp-workflow/src/module.rs` — 填充真实路由(12 个端点) - `crates/erp-auth/src/service/seed.rs` — 添加工作流权限 - `crates/erp-server/src/main.rs` — 注册 WorkflowModule,合并路由 **新增种子权限(8 个):** - workflow:create, workflow:list, workflow:read, workflow:update - workflow:publish, workflow:start, workflow:approve, workflow:delegate **验证:** `cargo check` + `cargo test` 通过 --- ## Task 10: 前端 API 层 + 工作流页面 **创建文件(`apps/web/src/`):** - `api/workflowDefinitions.ts` — 流程定义 API - `api/workflowInstances.ts` — 流程实例 API - `api/workflowTasks.ts` — 任务 API - `pages/Workflow.tsx` — Tab 壳页面(流程定义 | 我的待办 | 我的已办 | 流程监控) **修改文件:** - `App.tsx` — 添加 workflow 路由 - `MainLayout.tsx` — 侧边栏添加工作流菜单 --- ## Task 11: React Flow 可视化设计器 **创建文件(`apps/web/src/pages/workflow/`):** - `ProcessDesigner.tsx` — React Flow 画布 + 节点面板 + 属性面板 - `nodes/` — 自定义节点组件(StartEvent, EndEvent, UserTask, ServiceTask, Gateway) - `edges/` — 条件标签连线组件 - `hooks/useFlowValidation.ts` — 流程图前端验证 **依赖:** `@xyflow/react` npm 包 **功能:** - 拖拽添加节点到画布 - 连线编辑(含条件表达式) - 节点属性编辑面板 - 导出为 JSON nodes/edges 格式(匹配后端 DTO) - 流程图合法性前端验证 --- ## Task 12: 流程图查看器 + 超时框架 **创建文件(`apps/web/src/pages/workflow/`):** - `ProcessViewer.tsx` — 只读 React Flow 渲染,高亮当前活跃节点 - `InstanceDetail.tsx` — 实例详情页(流程图 + 变量 + 任务历史) **超时框架(后端占位):** - `crates/erp-workflow/src/engine/timeout.rs` — 超时检查接口 - Task 表 `due_date` 字段已支持 **验证:** `pnpm dev` 启动,工作流设计器可拖拽节点、连线、保存 --- ## 依赖图 ``` Task 1(骨架) | Task 2(迁移)→ Task 3(Entity)→ Task 4(DTO) | +---------------+---------------+ | | Task 5(BPMN 解析器) Task 6(执行引擎) | | +---------------+---------------+ | Task 7(Service) | Task 8(Handler) | Task 9(集成+种子) | +---------------+---------------+ | | Task 10(前端页面) Task 11(可视化设计器) | | +---------------+---------------+ | Task 12(查看器+超时) ``` --- ## 验证清单 - [ ] `cargo check` 全 workspace 通过 - [ ] `cargo test --workspace` 全部通过 - [ ] Docker 环境正常启动 - [ ] 所有迁移可正/反向执行 - [ ] 12 个工作流 API 端点可测试 - [ ] 前端工作流设计器可拖拽节点和连线 - [ ] 流程图保存和加载正常 - [ ] 所有代码已提交 ## 关键参考文件 | 用途 | 文件路径 | |------|----------| | Service 模式 | `crates/erp-config/src/service/numbering_service.rs` | | Handler 模式 | `crates/erp-config/src/handler/numbering_handler.rs` | | State 桥接 | `crates/erp-server/src/state.rs` | | 模块注册 | `crates/erp-config/src/module.rs` | | 迁移模式 | `crates/erp-server/migration/src/m20260412_000016_create_settings.rs` | | Advisory Lock | `crates/erp-config/src/service/numbering_service.rs` (generate_number) | | 前端 Table CRUD | `apps/web/src/pages/Roles.tsx` | | 前端树形展示 | `apps/web/src/pages/Organizations.tsx` | | RBAC | `crates/erp-core/src/rbac.rs` |