告别手动审批:用Jeecg Boot + Activiti打造你的第一个自动化业务流程(含完整代码)
从零构建企业级流程自动化Jeecg Boot与Activiti深度整合实战当团队规模扩大到20人以上时手工处理请假单和报销单的弊端开始显现——审批人经常忘记处理邮件财务部门需要反复核对Excel表格而员工则抱怨流程透明度不足。这正是我们团队三年前面临的困境直到发现Jeecg Boot与Activiti的组合可以像乐高积木一样快速搭建自动化工作流。1. 环境准备与基础架构设计在开始编码之前我们需要明确技术选型的优势所在。Jeecg Boot作为基于Spring Boot的低代码平台提供了用户权限、数据字典等开箱即用的企业级功能而Activiti则是经过十年验证的工作流引擎两者结合能实现112的效果。1.1 开发环境配置推荐使用以下技术栈组合JDK 1.8Activiti 5.x对Java 8兼容性最佳MySQL 5.7需支持事务隔离级别Redis 5.x用于流程实例缓存Maven 3.6管理多模块依赖关键依赖配置pom.xml节选properties activiti.version5.22.0/activiti.version /properties dependencies !-- Activiti核心引擎 -- dependency groupIdorg.activiti/groupId artifactIdactiviti-engine/artifactId version${activiti.version}/version /dependency !-- Jeecg Boot Starter -- dependency groupIdorg.jeecgframework.boot/groupId artifactIdjeecg-boot-starter/artifactId version2.4.0/version /dependency /dependencies1.2 数据库规划建议为工作流建立独立的数据源与业务数据物理隔离数据库类型库名用途MySQLjeecg_boot业务系统数据MySQLactiviti_db流程引擎运行时数据RedisDB1流程实例状态缓存提示生产环境建议将activiti_db配置为读写分离架构历史数据表ACT_HI_*可单独部署到从库2. 核心集成步骤详解2.1 流程设计器整合Activiti Modeler的汉化版需要特殊处理才能与Jeecg Boot的前端框架兼容。经过多次实践我发现以下目录结构最稳定src/main/resources/static/ ├── diagram-viewer/ # 流程图渲染器 ├── editor-app/ # 核心设计器资源 └── modeler.html # 设计器入口页面关键配置修改点编辑editor-app/app-cfg.jsvar ACTIVITI { CONFIG: { contextRoot: /jeecg-boot/service, customStencilSets: [ { url: /static/stencilset.json } ] } };后端控制器需要添加路由前缀RestController RequestMapping(/service) public class ModelEditorJsonRestResource { GetMapping(/model/{modelId}/json) public ObjectNode getEditorJson(PathVariable String modelId) { // 实现逻辑... } }2.2 用户体系对接Jeecg Boot的RBAC系统需要与Activiti的用户组机制建立映射关系。在我的项目中采用事件监听方式实现双向同步Component public class UserSyncListener implements ApplicationListenerJeecgUserEvent { Autowired private IdentityService identityService; Override public void onApplicationEvent(JeecgUserEvent event) { SysUser user event.getUser(); switch(event.getEventType()) { case CREATE: identityService.saveUser(convertToActivitiUser(user)); break; case UPDATE: identityService.deleteUser(user.getId()); identityService.saveUser(convertToActivitiUser(user)); break; case DELETE: identityService.deleteUser(user.getId()); break; } } private User convertToActivitiUser(SysUser sysUser) { User user identityService.newUser(sysUser.getId()); user.setFirstName(sysUser.getRealname()); user.setEmail(sysUser.getEmail()); return user; } }3. 业务流程实现范例3.1 请假流程建模通过Activiti Modeler设计典型的三级审批流程开始事件关联Jeecg Boot的请假表单审批节点直属领导审批UserTask部门负责人审批UserTaskHR备案ServiceTask网关控制时长≤3天跳过部门审批ExclusiveGateway驳回时返回申请人SequenceFlow流程变量定义示例process idleave_process name请假流程 extensionElements activiti:formProperty idleaveType name请假类型 typeenum requiredtrue / activiti:formProperty iddays name请假天数 typelong / /extensionElements /process3.2 业务表单绑定Jeecg Boot的表单设计器需要与流程定义关联PostMapping(/startProcess) public Result? startLeaveProcess(RequestBody LeaveApplyVO vo) { // 1. 保存业务数据 LeaveApply entity new LeaveApply(); BeanUtils.copyProperties(vo, entity); leaveApplyService.save(entity); // 2. 启动流程实例 MapString, Object variables new HashMap(); variables.put(applyUserId, vo.getUserId()); variables.put(businessKey, entity.getId()); ProcessInstance instance runtimeService.startProcessInstanceByKey( leave_process, variables); // 3. 关联业务与流程 ActivitiBusiness business new ActivitiBusiness(); business.setBusinessId(entity.getId()); business.setProcessInstanceId(instance.getId()); businessService.save(business); return Result.OK(流程启动成功); }4. 高级功能实现4.1 动态任务分配通过Jeecg Boot的角色体系实现灵活的任务分配public class DynamicAssignee implements TaskListener { Override public void notify(DelegateTask task) { String roleCode (String) task.getVariable(approvalRole); ListSysUser users sysUserApi.getUsersByRole(roleCode); if(users.size() 1) { task.setAssignee(users.get(0).getUsername()); } else { task.addCandidateUsers( users.stream() .map(SysUser::getUsername) .collect(Collectors.toList()) ); } } }4.2 流程监控看板集成Jeecg Boot的统计报表功能展示流程KPI-- 流程时效统计SQL SELECT p.NAME_ AS process_name, AVG(TIMESTAMPDIFF(HOUR, i.START_TIME_, i.END_TIME_)) AS avg_duration, COUNT(*) AS instance_count FROM ACT_HI_PROCINST i JOIN ACT_RE_PROCDEF p ON i.PROC_DEF_ID_ p.ID_ GROUP BY p.NAME_;前端使用ECharts呈现option { tooltip: { trigger: axis }, xAxis: { data: [请假流程, 报销流程] }, yAxis: { type: value }, series: [{ name: 平均处理时长(h), type: bar, data: [12.5, 36.2] }] }5. 性能优化实践5.1 历史数据归档策略针对ACT_HI_*表的归档方案Scheduled(cron 0 0 3 * * ?) public void archiveHistoricData() { Calendar calendar Calendar.getInstance(); calendar.add(Calendar.MONTH, -3); Date archiveDate calendar.getTime(); historyService.createHistoricProcessInstanceQuery() .finishedBefore(archiveDate) .list() .forEach(instance - { // 归档到数据仓库 archiveService.save(instance); // 清理原数据 historyService.deleteHistoricProcessInstance(instance.getId()); }); }5.2 缓存优化配置Redis缓存工作流状态的配置示例spring: redis: host: 127.0.0.1 timeout: 3000 lettuce: pool: max-active: 20 max-wait: -1ms activiti: async-executor-activate: true process-definition-cache-limit: 100 process-definition-cache-time: 3600在项目上线初期我们曾遇到流程实例堆积的问题。后来通过分析发现异步执行器AsyncExecutor的线程池配置不合理是主因。调整后配置如下# application-activiti.properties activiti.async.executor.thread.pool.size20 activiti.async.executor.queue.size1000 activiti.async.executor.thread.pool.queue.full.policyABORT