# Warm-Flow 工作流框架开发指南 ## 📋 目录 - [1. 工作流框架概述](#1-工作流框架概述) - [2. 技术架构](#2-技术架构) - [3. 核心组件](#3-核心组件) - [4. 工作流开发流程](#4-工作流开发流程) - [5. 请假工作流示例](#5-请假工作流示例) - [6. 前端组件使用](#6-前端组件使用) - [7. 工作流状态管理](#7-工作流状态管理) - [8. 最佳实践](#8-最佳实践) - [9. 常见问题](#9-常见问题) --- ## 1. 工作流框架概述 ### 1.1 框架介绍 本项目集成了 **warm-flow** 工作流框架,这是由 dromara 社区开源的轻量级工作流引擎,具有以下特点: - 🚀 **轻量级**: 核心代码简洁,易于理解和扩展 - 🔧 **可配置**: 支持通过配置文件动态开启/关闭工作流功能 - 🎯 **易集成**: 与 Spring Boot 无缝集成,支持 MyBatis-Plus - 📊 **可视化**: 内置流程设计器和流程图展示 - 🔐 **权限控制**: 集成 Sa-Token 权限认证 - 📧 **消息通知**: 支持站内信、邮件、短信通知 ### 1.2 配置开关 工作流功能通过 `@ConditionalOnEnable` 注解控制,配置如下: ```yaml # application.yml warm-flow: enabled: true # 设置为 true 时,启用工作流功能 ``` --- ## 2. 技术架构 ### 2.1 技术栈 ``` ┌─────────────────────────────────────────────────────────┐ │ 前端层 (Vue 3) │ ├─────────────────────────────────────────────────────────┤ │ - approvalButton (审批按钮组件) │ │ - submitVerify (提交审批组件) │ │ - approvalRecord (审批记录组件) │ │ - flowChart (流程图组件) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ 后端层 (Spring Boot) │ ├─────────────────────────────────────────────────────────┤ │ - FlwTaskController (任务管理控制器) │ │ - FlwInstanceController (流程实例控制器) │ │ - FlwDefinitionController(流程定义控制器) │ │ - TestLeaveController (请假业务控制器) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ 服务层 (Service) │ ├─────────────────────────────────────────────────────────┤ │ - IFlwTaskService (任务服务) │ │ - IFlwInstanceService (实例服务) │ │ - IFlwDefinitionService (定义服务) │ │ - IFlwCommonService (通用服务) │ │ - ITestLeaveService (请假业务服务) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ Warm-Flow 核心引擎 │ ├─────────────────────────────────────────────────────────┤ │ - FlowEngine (工作流引擎) │ │ - DefService (流程定义服务) │ │ - InsService (流程实例服务) │ │ - TaskService (任务服务) │ │ - NodeService (节点服务) │ │ - HisTaskService (历史任务服务) │ └─────────────────────────────────────────────────────────┘ ``` ### 2.2 数据库表结构 warm-flow 使用以下核心表: ```sql -- 流程定义表 flow_definition -- 存储流程定义信息 flow_node -- 存储流程节点信息 flow_skip -- 存储节点间跳转关系 -- 流程实例表 flow_instance -- 存储流程实例信息 flow_task -- 存储当前待办任务 flow_his_task -- 存储历史任务记录 -- 扩展表 flow_category -- 流程分类表 flow_copy -- 抄送表 ``` --- ## 3. 核心组件 ### 3.1 后端核心服务 #### 3.1.1 流程定义服务 (IFlwDefinitionService) ```java public interface IFlwDefinitionService { // 查询流程定义列表 TableDataInfo queryList(FlowDefinition flowDefinition, PageQuery pageQuery); // 发布流程定义 boolean publish(Long id); // 导入流程定义 boolean importJson(MultipartFile file, String category); // 删除流程定义 boolean removeDef(List ids); } ``` #### 3.1.2 流程实例服务 (IFlwInstanceService) ```java public interface IFlwInstanceService { // 查询运行中的流程实例 TableDataInfo selectRunningInstanceList(FlowInstanceBo bo, PageQuery pageQuery); // 查询已结束的流程实例 TableDataInfo selectFinishInstanceList(FlowInstanceBo bo, PageQuery pageQuery); // 撤销流程 boolean cancelProcessApply(FlowCancelBo bo); // 作废流程 boolean invalid(FlowInvalidBo bo); } ``` #### 3.1.3 任务服务 (IFlwTaskService) ```java public interface IFlwTaskService { // 启动工作流 StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo); // 办理任务 boolean completeTask(CompleteTaskBo completeTaskBo); // 查询待办任务 TableDataInfo pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery); // 查询已办任务 TableDataInfo pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery); } ``` ### 3.2 前端核心组件 #### 3.2.1 审批按钮组件 (approvalButton.vue) ```vue ``` **功能特点:** - 根据页面类型和流程状态动态显示按钮 - 支持暂存、提交、审批、查看流程进度等操作 - 自动处理按钮的显示/隐藏逻辑 #### 3.2.2 提交审批组件 (submitVerify.vue) ```vue 站内信 邮件 短信
【{{ item.nodeName }}】:
``` **功能特点:** - 支持消息通知配置(站内信、邮件、短信) - 支持附件上传 - 支持抄送人员选择 - 支持下一步审批人选择 - 支持审批意见填写 - 支持多种操作:提交、委托、转办、加签、减签、终止、退回 #### 3.2.3 审批记录组件 (approvalRecord.vue) ```vue ``` **功能特点:** - 流程图可视化展示 - 详细的审批记录表格 - 支持附件下载 - 审批时长统计 --- ## 4. 工作流开发流程 ### 4.1 后端开发步骤 #### 步骤 1: 创建业务实体 ```java // 1. 创建业务Bo类 @Data @EqualsAndHashCode(callSuper = true) public class TestLeaveBo extends BaseEntity { private Long id; private String leaveType; private String startDate; private String endDate; private Integer leaveDays; private String remark; private String status; } // 2. 创建业务Vo类 @Data @ExcelIgnoreUnannotated public class TestLeaveVo { private Long id; private String leaveType; private String startDate; private String endDate; private Integer leaveDays; private String remark; private String status; // 基础字段(重要:避免MyBatis映射错误) private Long createBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; private Long updateBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date updateTime; } ``` #### 步骤 2: 创建 Service 接口和实现 ```java // Service接口 public interface ITestLeaveService { TestLeaveVo queryById(Long id); TableDataInfo queryPageList(TestLeaveBo bo, PageQuery pageQuery); List queryList(TestLeaveBo bo); TestLeaveVo insertByBo(TestLeaveBo bo); TestLeaveVo updateByBo(TestLeaveBo bo); Boolean deleteWithValidByIds(List ids); } // Service实现类 @Service @RequiredArgsConstructor public class TestLeaveServiceImpl implements ITestLeaveService { private final TestLeaveMapper baseMapper; @Override public TestLeaveVo insertByBo(TestLeaveBo bo) { TestLeave add = BeanUtil.toBean(bo, TestLeave.class); // 设置默认状态为草稿 add.setStatus("draft"); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); return BeanUtil.toBean(add, TestLeaveVo.class); } return null; } // 其他CRUD方法实现... } ``` #### 步骤 3: 创建 Controller ```java @ConditionalOnEnable // 工作流条件开启注解 @Validated @RequiredArgsConstructor @RestController @RequestMapping("/workflow/leave") public class TestLeaveController extends BaseController { private final ITestLeaveService testLeaveService; @SaCheckPermission("workflow:leave:list") @GetMapping("/list") public TableDataInfo list(TestLeaveBo bo, PageQuery pageQuery) { return testLeaveService.queryPageList(bo, pageQuery); } @SaCheckPermission("workflow:leave:add") @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) { return R.ok(testLeaveService.insertByBo(bo)); } @SaCheckPermission("workflow:leave:edit") @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) { return R.ok(testLeaveService.updateByBo(bo)); } // 其他CRUD接口... } ``` ### 4.2 前端开发步骤 #### 步骤 1: 创建 API 接口 ```typescript // api/workflow/leave/types.ts export interface LeaveForm extends BaseEntity { id?: string | number; leaveType?: string; startDate?: string; endDate?: string; leaveDays?: number; remark?: string; status?: string; } export interface LeaveVO extends BaseEntity { id?: string | number; leaveType?: string; startDate?: string; endDate?: string; leaveDays?: number; remark?: string; status?: string; } // api/workflow/leave/index.ts export const listLeave = (query: LeaveQuery): AxiosPromise => { return request({ url: '/workflow/leave/list', method: 'get', params: query, }); }; export const addLeave = (data: LeaveForm): AxiosPromise => { return request({ url: '/workflow/leave', method: 'post', data: data, }); }; ``` #### 步骤 2: 创建列表页面 ```vue ``` #### 步骤 3: 创建编辑页面 ```vue ``` --- ## 5. 请假工作流示例 ### 5.1 工作流模式 系统提供 6 种不同的工作流模式: | 流程编码 | 流程名称 | 特点说明 | | -------- | --------------------- | ------------------------ | | leave1 | 请假申请-普通 | 简单的线性审批流程 | | leave2 | 请假申请-排他网关 | 根据条件选择不同分支 | | leave3 | 请假申请-并行网关 | 多个分支并行执行 | | leave4 | 请假申请-会签 | 多人会签,所有人都需审批 | | leave5 | 请假申请-并行会签网关 | 并行网关+会签组合 | | leave6 | 请假申请-排他并行会签 | 排他网关+并行会签组合 | ### 5.2 流程变量 ```typescript // 工作流变量配置 const taskVariables = { // leave2/6 使用的流程变量(排他网关条件判断) leaveDays: data.leaveDays, // leave4/5 使用的流程变量(会签用户列表) userList: ['1', '3', '4'], }; ``` ### 5.3 状态流转 ``` draft(草稿) → waiting(待审批) → complete(已完成) ↓ ↓ cancel(已取消) back(已退回) ``` --- ## 6. 前端组件使用 ### 6.1 工作流页面模板 ```vue ``` ### 6.2 路由跳转工具 ```typescript // 使用工作流通用路由跳转 import workflowCommon from '@/api/workflow/workflowCommon'; const handleOpen = (row: FlowTaskVO) => { const routerJumpVo = { businessId: row.businessId, taskId: row.id, type: 'approval', // add/update/view/approval formCustom: row.formCustom, formPath: row.formPath, }; workflowCommon.routerJump(routerJumpVo, proxy); }; ``` --- ## 7. 工作流状态管理 ### 7.1 业务状态 | 状态值 | 状态名称 | 说明 | 可用操作 | | --------- | -------- | ------------ | -------------------- | | draft | 草稿 | 新建但未提交 | 编辑、删除、提交 | | waiting | 待审批 | 流程进行中 | 查看、撤销 | | complete | 已完成 | 流程正常结束 | 查看 | | cancel | 已取消 | 申请人撤销 | 编辑、删除、重新提交 | | back | 已退回 | 审批人退回 | 编辑、删除、重新提交 | | invalid | 已作废 | 管理员作废 | 查看 | | terminate | 已终止 | 流程异常终止 | 查看 | ### 7.2 任务状态 | 状态值 | 状态名称 | 说明 | | -------- | -------- | ---------------- | | waiting | 待办 | 等待当前用户处理 | | complete | 已办 | 当前用户已处理 | | reject | 已拒绝 | 当前用户拒绝 | | delegate | 已委托 | 当前用户委托他人 | | transfer | 已转办 | 当前用户转办他人 | ### 7.3 状态检查工具 ```typescript // 前端状态检查 const canEdit = computed(() => { return ['draft', 'cancel', 'back'].includes(form.value.status); }); const canCancel = computed(() => { return form.value.status === 'waiting'; }); const showApprovalButton = computed(() => { return routeParams.value.type === 'approval' && form.value.status === 'waiting'; }); ``` --- ## 8. 最佳实践 ### 8.1 后端开发最佳实践 #### 1. VO 类基础字段规范 ```java // 所有工作流业务VO类必须包含统一基础字段 public class TestLeaveVo { // 业务字段 private Long id; private String leaveType; // ...其他业务字段 // 基础字段(必须包含,避免MyBatis映射错误) private Long createBy; private Date createTime; private Long updateBy; private Date updateTime; } ``` #### 2. 权限注解规范 ```java // 所有工作流接口必须添加权限检查 @SaCheckPermission("workflow:leave:list") @GetMapping("/list") public TableDataInfo list(TestLeaveBo bo, PageQuery pageQuery) { return testLeaveService.queryPageList(bo, pageQuery); } ``` #### 3. 条件开启注解 ```java // 所有工作流相关类必须添加条件开启注解 @ConditionalOnEnable @Service public class TestLeaveServiceImpl implements ITestLeaveService { // 实现逻辑 } ``` #### 4. 事务管理 ```java // 涉及业务数据和工作流数据的操作需要事务保证 @Transactional(rollbackFor = Exception.class) public TestLeaveVo insertByBo(TestLeaveBo bo) { // 1. 保存业务数据 TestLeave entity = BeanUtil.toBean(bo, TestLeave.class); entity.setStatus("draft"); baseMapper.insert(entity); // 2. 如果需要同时操作工作流数据,确保在同一事务中 return BeanUtil.toBean(entity, TestLeaveVo.class); } ``` ### 8.2 前端开发最佳实践 #### 1. 组件引入规范 ```vue ``` #### 2. 表单验证规范 ```javascript // 工作流表单必须包含的验证规则 const rules = { // 业务字段验证 leaveType: [{ required: true, message: '请假类型不能为空', trigger: 'blur' }], leaveDays: [{ required: true, message: '请假天数不能为空', trigger: 'blur' }], // 如果有时间范围,需要自定义验证 leaveTime: [{ required: true, message: '请假时间不能为空', trigger: 'blur' }], }; ``` #### 3. 错误处理规范 ```javascript // 提交表单错误处理 const submitForm = async (status: string) => { try { // 表单验证 await leaveFormRef.value?.validate(); // 业务数据保存 buttonLoading.value = true; const res = form.value.id ? await updateLeave(form.value) : await addLeave(form.value); // 工作流处理 if (status === 'submit') { await handleStartWorkFlow(res.data); } } catch (error) { console.error('提交失败:', error); proxy?.$modal.msgError('操作失败,请重试'); } finally { buttonLoading.value = false; } }; ``` ### 8.3 性能优化 #### 1. 组件懒加载 ```javascript // 工作流组件按需加载 const SubmitVerify = defineAsyncComponent(() => import('@/components/Process/submitVerify.vue')); const ApprovalRecord = defineAsyncComponent(() => import('@/components/Process/approvalRecord.vue'), ); ``` #### 2. 接口优化 ```javascript // 避免重复请求,使用缓存 const { data: taskInfo, loading } = useCachedRequest(() => getTask(taskId.value), { cacheKey: `task_${taskId.value}`, ttl: 5 * 60 * 1000, // 5分钟缓存 }); ``` --- ## 9. 常见问题 ### 9.1 MyBatis 映射错误 **问题**: `Could not set property 'createBy'` **原因**: VO 类缺少基础字段,但 SQL 查询包含这些字段 **解决**: 确保所有 VO 类包含统一的基础字段 ```java // 正确的VO类定义 public class TestLeaveVo { // 业务字段 private Long id; private String leaveType; // 必须包含的基础字段 private Long createBy; private Date createTime; private Long updateBy; private Date updateTime; } ``` ### 9.2 工作流组件不显示 **问题**: 审批按钮或其他工作流组件不显示 **原因**: 1. 没有添加 `@ConditionalOnEnable` 注解 2. 配置文件中 `warm-flow.enabled=false` 3. 组件引入路径错误 **解决**: ```yaml # 确保配置正确 warm-flow: enabled: true ``` ```java // 确保添加条件注解 @ConditionalOnEnable @RestController public class TestLeaveController { // 控制器逻辑 } ``` ### 9.3 权限问题 **问题**: 接口访问被拒绝 **原因**: 缺少权限注解或权限配置不正确 **解决**: 1. 添加权限注解:`@SaCheckPermission("workflow:leave:list")` 2. 在菜单管理中配置对应的权限标识 3. 为用户角色分配相应权限 ### 9.4 流程启动失败 **问题**: 启动工作流时报错 **原因**: 1. 流程定义未发布 2. 流程编码不存在 3. 业务数据状态不正确 4. **退回后残留多个 `flow_task`**(原审批节点未清、历史 draft 申请人任务),重新提交时取到错误 taskId,移动端统一提示「启动工作流失败」 **解决**: 1. 检查流程定义是否已发布 2. 确认 `flowCode` 参数正确 3. 确保业务数据状态为 `draft` / `back` / `cancel`(可重新提交) 4. **退回清理(已实现)**:`FlwTaskServiceImpl.backProcess` 在 `skip(REJECT)` 后调用 `cleanResidualTasksAfterBack`,只保留退回目标节点最新有效任务;重新提交时 `healResidualTasksOnRestart` 自愈历史脏数据 **排查 SQL 示例**(同一 `instance_id` 不应长期存在多条无关待办): ```sql SELECT id, node_name, flow_status, create_time FROM flow_task WHERE instance_id = ? AND IFNULL(del_flag, '0') = '0'; ``` ### 9.5 日期格式问题 **问题**: 日期选择器数据格式不匹配 **原因**: 前端日期格式与后端不一致 **解决**: ```vue ``` --- ## 10. 总结 warm-flow 工作流框架为本项目提供了完整的工作流解决方案,具有以下优势: 1. **轻量级**: 核心简洁,易于理解和维护 2. **可扩展**: 支持自定义业务逻辑和权限控制 3. **可视化**: 内置流程设计器和流程图展示 4. **完整性**: 提供完整的前后端组件和示例 通过请假工作流示例,开发者可以快速掌握工作流开发的完整流程,并应用到其他业务场景中。 --- **文档维护**: 本文档随项目发展持续更新 **最后更新**: 2025-01-09 **版本**: v1.0