目录

  • 前言
  • 一、项目引入 Activiti 依赖
  • 二、 添加 Activiti 相关配置
  • 三、 启动项目,生成数据表
  • 四、 Activiti7 整合 Spring Security
  • 五、 工作流简单测试
  • 六、 数据表命名规则说明
  • 后记

前言

  最近新项目要用工作流,查了几天资料,主要集中在 Activiti7、Flowable、Camunda 三个,同是 jbpm 框架发展而来,各有优劣。最终选择了Activiti7,原因无他,仅是手头参与的其他项目用这个,方便尽快上手,下个项目应该会试试另外两个。
  本次项目采用 RuoYi-Vue-v3.8 开发,使用的 Springboot 版本为v2.5.8。记录一下接入使用过程及踩坑信息,便于自己以后查看,如果对其他人有一些帮助也算意外之喜吧。以下内容主要针对于本次项目,可能一些说明不准确或解决方式不完善的地方,能力有限,只能留有遗憾了。

一、项目引入 Activiti 依赖

  1. 在 pom 文件中添加 Activiti 相关依赖:

    <dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>7.1.0.M4</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion></exclusions>
    </dependency>
    <dependency><groupId>org.activiti.dependencies</groupId><artifactId>activiti-dependencies</artifactId><version>7.1.0.M4</version><type>pom</type>
    </dependency>
    <!-- Activiti生成流程图 -->
    <dependency><groupId>org.activiti</groupId><artifactId>activiti-image-generator</artifactId><version>7.1.0.M4</version>
    </dependency>
    
  2. 项目使用 Mysql 数据库,已引入驱动,因此这里不再添加。Activiti 默认使用 H2 数据库,如项目未使用数据库,应引入数据库驱动。

二、 添加 Activiti 相关配置

  1. 修改 application.yml 配置文件,添加内容如下:

    spring:
    ...# 工作流
    activiti:deployment-mode: never-fail # 关闭 SpringAutoDeploymentcheck-process-definitions: false #自动部署验证设置:true-开启(默认)、false-关闭database-schema-update: true #true表示对数据库中所有表进行更新操作。如果表不存在,则自动创建。history-level: full #full表示全部记录历史,方便绘制流程图db-history-used: true #true表示使用历史表
    main:allow-bean-definition-overriding: true #不同配置文件中存在id或者name相同的bean定义,后面加载的bean定义会覆盖前面的bean定义
    

三、 启动项目,生成数据表

  1. 踩坑1:不明白别人的项目为什么要加 “allow-bean-definition-overriding“ 属性,所以没加。结果很快就明白为什么要加了,项目启动出现如下错误(错误信息还给出了解决提醒,我真搞笑):

    Description:
    The bean 'methodSecurityInterceptor', defined in class path resource [org/activiti/spring/boot/MethodSecurityConfig.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class] and overriding is disabled.Action:
    Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
    
  2. 踩坑2:在上述步骤之后,重新运行项目,然后又出现了如下错误:

    16:46:33.758 [restartedMain] INFO  o.a.e.i.c.ProcessEngineConfigurationImpl - [configuratorsAfterInit,1571] - Executing configure() of class org.activiti.spring.process.conf.ProcessExtensionsConfiguratorAutoConfiguration$$EnhancerBySpringCGLIB$$3d85fc52 (priority:10000)
    16:46:33.893 [restartedMain] ERROR o.a.e.i.i.CommandContext - [logException,149] - Error while closing command context
    org.apache.ibatis.exceptions.PersistenceException:
    ### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Table 'zhhq.act_ge_property' doesn't exist
    ### The error may exist in org/activiti/db/mapping/entity/Property.xml
    ### The error may involve org.activiti.engine.impl.persistence.entity.PropertyEntityImpl.selectProperty-Inline
    ### The error occurred while setting parameters
    ### SQL: select * from ACT_GE_PROPERTY where NAME_ = ?
    ### Cause: java.sql.SQLSyntaxErrorException: Table 'zhhq.act_ge_property' doesn't exist
    
  • 解决方式:修改 mysql 连接字符串,添加 &nullCatalogMeansCurrent=true

    // 原来的 url
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    // 修改后 url,添加了 &nullCatalogMeansCurrent=true
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
    
  • 网友给出的说明:因为 mysql 使用 schema 标识库名而不是 catalog,因此 mysql 会扫描所有的库来找表,如果其他库中有相同名称的表,activiti 就以为找到了,本质上这个表在当前数据库中并不存在。设置nullCatalogMeansCurrent=true,表示 mysql 默认当前数据库操作,在 mysql-connector-java 5.xxx该参数默认为 true,在6.xxx以上默认为 false,因此需要设置 nullCatalogMeansCurrent=true。
  1. 经过以上修改步骤,再次运行项目。好吧,这次我成功了,项目顺利启动,且数据库增加了25个表。
  2. 留个坑,其实这版 Activiti 自动生成的表字段不全,下面有补充说明。

四、 Activiti7 整合 Spring Security

  1. Activiti7 没有身份管理的表,其能力依赖和Spring Security整合,新 Api 包括 TaskRuntime 和 ProcessRuntime 都会强制使用 Security 验证用户权限。
  2. 查看 ProcessRuntime 类的源码可发现,需要“ACTIVITI_USER”角色权限。因此,处理思路是,在登录验证时,给登录用户增加对应权限。
  3. 在 RuoYi 里,登录验证的实现如下:
    public String login(String username, String password, String code, String uuid){boolean captchaOnOff = configService.selectCaptchaOnOff();// 验证码开关if (captchaOnOff){validateCaptcha(username, code, uuid);}// 用户验证Authentication authentication = null;try{// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));}catch (Exception e){if (e instanceof BadCredentialsException){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));throw new ServiceException(e.getMessage());}}AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));LoginUser loginUser = (LoginUser) authentication.getPrincipal();recordLoginInfo(loginUser.getUserId());// 生成tokenreturn tokenService.createToken(loginUser);}
    
  4. 根据上述代码中,需要在 UserDetailsServiceImpl.loadUserByUsername 中加入对应逻辑:
  • 原代码:

    @Service
    public class UserDetailsServiceImpl implements UserDetailsService
    {private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);@Autowiredprivate ISysUserService userService;@Autowiredprivate SysPermissionService permissionService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{SysUser user = userService.selectUserByUserName(username);if (StringUtils.isNull(user)){log.info("登录用户:{} 不存在.", username);throw new ServiceException("登录用户:" + username + " 不存在");}else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){log.info("登录用户:{} 已被删除.", username);throw new ServiceException("对不起,您的账号:" + username + " 已被删除");}else if (UserStatus.DISABLE.getCode().equals(user.getStatus())){log.info("登录用户:{} 已被停用.", username);throw new ServiceException("对不起,您的账号:" + username + " 已停用");}return createLoginUser(user);}public UserDetails createLoginUser(SysUser user){return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));}
    }
    
  • 修改后的代码:
    // 有变更的方法,在原来的用户信息类中加入了需要的权限信息
    public UserDetails createLoginUser(SysUser user) {Set<String> postCode = sysPostService.selectPostCodeByUserId(user.getUserId());postCode = postCode.parallelStream().map(s -> "GROUP_" + s).collect(Collectors.toSet());postCode.add("ROLE_ACTIVITI_USER");List<SimpleGrantedAuthority> collect = postCode.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user), collect);
    }
    
  1. 修改相应的用户信息类 LoginUser
  • 原代码:

    public class LoginUser implements UserDetails
    {private static final long serialVersionUID = 1L;/*** 用户ID*/private Long userId;/*** 部门ID*/private Long deptId;/*** 用户唯一标识*/private String token;/*** 登录时间*/private Long loginTime;/*** 过期时间*/private Long expireTime;/*** 登录IP地址*/private String ipaddr;/*** 登录地点*/private String loginLocation;/*** 浏览器类型*/private String browser;/*** 操作系统*/private String os;/*** 权限列表*/private Set<String> permissions;/*** 用户信息*/private SysUser user;// 省去 set/get 方法@Overridepublic Collection<? extends GrantedAuthority> getAuthorities(){return null;}
    }
    
  • 修改后的代码:
    public class LoginUser implements UserDetails
    {// 去掉不变的代码// +++++private List<SimpleGrantedAuthority> authorities;// +++++public void setAuthorities(List<SimpleGrantedAuthority> authorities) {this.authorities = authorities;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities(){return authorities; // 变更}
    }
    
  • 主要是实体类中,增加了权限属性,且修改了对应的get方法。
  1. 现在,Activiti 就可以识别到登录用户,且有了新 Api 对应的权限。
  2. 总结一下,对于 Ruoyi 框架原来的代码实现而言,修改了两处,LoginUser 类和 UserDetailsServiceImpl 类,当然,依赖方法增加了相应的查询岗位(postCode)的方法。
  3. 整合工作就是这样,实在是不清不楚,马马虎虎。鉴于现在的时间和精力,只能紧着项目的实际问题来,希望以后有机会系统地学习一下 Activiti 吧。

五、 工作流简单测试

  1. 准备工作流(bpmn)文件,因为之后项目要接入 web 流程设计器,所以没在 IDE 上安装设计器插件,在旧项目里随便找了个bpmn文件测试用;
  2. 编写单元测试,先进行工作流部署测试
  • 编写单元测试代码:

    @SpringBootTest()public class ActivitiTest {@Autowiredprivate RepositoryService repositoryService;@Testpublic void deployTest() {Deployment deployment = repositoryService.createDeployment().addClasspathResource("leave.bpmn").name("测试流程").deploy();System.out.println("部署ID:" + deployment.getId());}}
    
  • 运行后,出现缺失表字段错误:
    ### SQL: insert into ACT_RE_DEPLOYMENT(ID_, NAME_, CATEGORY_, KEY_, TENANT_ID_, DEPLOY_TIME_, ENGINE_VERSION_, VERSION_, PROJECT_RELEASE_VERSION_)     values(?, ?, ?, ?, ?, ?, ?, ?, ?)
    ### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'VERSION_' in 'field list'
    
  • 好吧,Activiti7.1.0.M4 这版通过自动创建的表结构是少字段的,这 BUG 之前有网友提过,自己忘记这茬了,现在补坑吧:
    -- 修复Activiti7的M4版本缺失字段Bug
    alter table ACT_RE_DEPLOYMENT add column PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL;
    alter table ACT_RE_DEPLOYMENT add column VERSION_ varchar(255) DEFAULT NULL;
    
  • 再次运行,测试顺利通过,打印部署成功日志:
    15:59:08.035 [main] INFO  o.a.e.i.b.d.BpmnDeployer - [dispatchProcessDefinitionEntityInitializedEvent,234] - Process deployed: {id: leave:1:f67ecd7e-a047-11ec-a9cd-d45d64273150, key: leave, name: 请假流程-普通表单 }
    
  1. 进行创建流程实例测试
  • 编写测试代码:

    @Test
    public void startProcessTest() {ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("test").withName("请假测试").build());System.out.println("实例ID:" + processInstance.getId());
    }
    
  • 运行,如果出现以下错误:
    org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    
  • 是因为没有整合SpringSecurity ,上面整合内容里描述了新提供 Apl,比如 ProcessRuntime 必须有身份验证,看源码可知:
    @PreAuthorize("hasRole('ACTIVITI_USER')")
    public class ProcessRuntimeImpl implements ProcessRuntime {}
    
  • 两种解决思路:一种是整合 SpringSecurity;一种是试一下旧版 Api。那么试下旧版 Api 吧,重新修改代码:
    @Test
    public void startProcessTest() {Map<String, Object> paramMap = new HashMap<>();paramMap.put("deptLeader", "test01");ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave", "测试请假", paramMap);System.out.println("实例ID:" + pi.getId());
    }
    
  • 再次运行,顺利通过测试。
  1. 单元测试就算完了,证明 Activiti 顺利接入到了项目中,等前端流程设计器接入了,才能真正方便地用起来吧。

六、 数据表命名规则说明

  1. 简单记录一下 Activiti 表的命名规则,留个印象吧,想知道具体信息还是得对应到每个表;
  2. Activiti 的表都以 ”ACT_“ 开头。第二部分是表示表的用途的两个字母标识。用途也和服务的 API 对应。
  • act_hi_*:'hi’表示 history,此前缀的表包含历史数据,如历史(结束)流程实例,变量,任务等等。
  • act_ge_*:'ge’表示 general,此前缀的表为通用数据,用于不同场景中。
  • act_evt_*:'evt’表示 event,此前缀的表为事件日志。
  • act_procdef_*:'procdef’表示 processdefine,此前缀的表为记录流程定义信息。
  • act_re_*:'re’表示 repository,此前缀的表包含了流程定义和流程静态资源(图片,规则等等)。
  • act_ru_*:'ru’表示 runtime,此前缀的表是记录运行时的数据,包含流程实例,任务,变量,异步任务等运行中的数据。Activiti只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。

后记

  很基础的一些东西,算是项目使用工作流的第一步吧。接下来,就是在前端项目中接入流程设计器了,然后是实际使用中一些处理和技巧,一步步来吧。

Springboot接入Activiti7相关推荐

  1. (转) SpringBoot接入两套kafka集群

    转自: SpringBoot接入两套kafka集群 - 风小雅 - 博客园引入依赖 compile 'org.springframework.kafka:spring-kafka' 第一套kafka配 ...

  2. 微信开发:springboot接入微信公众号

    微信公众号现今已经成为我们日常生活中获取信息的一个重要途径,今天这篇博客主要是介绍如何用springboot接入微信的公众号.微信的公众号分三种:订阅号,一般个人使用,很多功能受限:服务号,个人与企业 ...

  3. SpringBoot 接入支付宝支付-证书模式

    SpringBoot 接入支付宝支付-证书模式 满足一下条件 1.注册支付宝商户,实名认证,获取APPID 2.申请支付宝公钥证书 3.有些复杂具体还是按照官方引导进行操作 产品大全 https:// ...

  4. SpringBoot接入支付宝支付API

    springboot接入支付宝api(网页或单独支付二维码) 更多内容可以参考支付宝开放平台:支付宝开放平台 (alipay.com) 支付宝沙箱:沙箱环境 - 支付宝文档中心 (alipay.com ...

  5. springboot接入华为微服务引擎CSE全过程及后续遇到的问题一览

    最近有项目需求把现有的单体应用,springboot接入华为的微服务引擎CSE,这里列出我遇到的问题,以及解决方案. 1.华为CSE微服务引擎的POM文件引入 问题:POM文件无法找到对应的华为CSE ...

  6. Cat3.0.0监控本地部署+springboot接入cat例子

    Cat监控本地部署 说明 目标 前提 cat.war和sql脚本 源码下载 编译 sql脚本----mysql这里就配置完了 部署cat springboot程序接入cat win10本地配置 my- ...

  7. SpringBoot整合Activiti7

    一.背景 因工作需要,公司需要建设一个类似工单系统,想借助activiti工作流引擎来进行实现,但目前网上Spring Boot与Activiti7整合的例子都不是特别全,大多都是Activiti6居 ...

  8. springboot接入hateoas与swagger2问题记录

    环境:springboot 2.2.1.RELEASE,swagger 2.9.2 问题记录 启动服务报错如下 Parameter 0 of method linkDiscoverers in org ...

  9. SpringBoot接入微信JSSDK,看这篇妥妥的

    虽然我很菜鸟,但我还想分享 先给猴急的客官上干货代码 GitHub 接入微信JSSDK GitHub地址 Gitee 接入微信JSSDK GitHub地址 前言 事情的起因是因为疫情严重,领导要求做一 ...

最新文章

  1. inotify结合rsync监控目录的实时变化
  2. Deep Learning论文笔记之(一)K-means特征学习
  3. 【NLP系列公开课】详解BERT、知识图谱、对话生成、图卷积神经网络
  4. 第一阶段:Java基础之OOP
  5. ERROR: org.apache.hadoop.hbase.MasterNotRunningException: Retried 7 times
  6. nssl1320,jzoj(初中)2108-买装备【dfs,水题】
  7. 【Python CheckiO 题解】Easy Unpack
  8. 【设计模式】—— 访问者模式Visitor
  9. abstract class和interface的区别
  10. sql 没有调试 菜单_关于ThinkPHP5.1+的Log无法记录SQL调试记录的小经历
  11. 将现有MFC应用程序迁移到BCGControlBar VC界面库中
  12. java语言实现常用算法(排序和查找)
  13. Hinton神经网络公开课16 Recent applications of deep neural nets
  14. 北斗导航 | 基于改进RANSAC算法的BDS接收机自主完好性监测算法研究
  15. loadrunner-11安装+破解+汉化(提供安装包,破解方式,汉化包)
  16. 如何解决“配置系统未能初始化“ 错误的解决方案?
  17. 迅雷手机版苹果版_「9月22日」最新 苹果IOS手机迅雷Beta版证书修复版 安卓不限速...
  18. 【活动回顾】大咖分享:流量过后,在线教育的留存和发展
  19. 行列式的计算机应用结题报告,行列式计算开题报告(共10篇).doc
  20. c语言代码大全复制,垃圾回收算法实现之 - 复制算法(完整可运行C语言代码)...

热门文章

  1. Ucore Lab6
  2. Minifilter Eop漏洞 CVE-2020-17103复现
  3. 关于Kafka的诞生
  4. DLL生成与调用总结
  5. linux iostat 命令详解
  6. 大部分人做SEO的目的是为了获取短期快速的流量
  7. 摩根大通开展试点,将债券发行放上区块链
  8. AD19设置板框原点
  9. 【Python】Matplotlib中的annotate(注解)【转载】
  10. Git 安装 (三)清除右键菜单选项[修改注册表]