http://blog.youkuaiyun.com/qq_27063119/article/details/76152040
Activiti初尝试(一)
一.
背景介绍
1. Activiti 其核心是 BPMN 2.0 的流程引擎。BPMN 是目前被各 BPM 厂商广泛接受的 BPM 标准,全称为 Business Process Model and Notation,由 OMG 组织进行维护,2011 年 1 月份发布了其 2.0 的正式版。BPMN 2.0 对比于第一个版本,其最重要的变化在于其定义了流程的元模型和执行语义,即它自己解决了存储、交换和执行的问题。这代表着 BPMN 2.0 流程定义模型不仅仅可以在任何兼容 BPMN 2.0 的引擎中执行,而且也可以在图形编辑器间交换。作为一个标准,BPMN 2.0 统一了工作流社区。
2. Activiti 是由 jBPM 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目,构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上,旨在创建下一代的 BPM 解决方案。同时 Activiti 选择了 Apache 许可,一方面是希望 Activiti 能有更长久的生命力,因为它不受任何个人或是公司的控制而是属于整个社区,另一方面更是希望这个宽松的许可能够让 Activiti BPM 引擎和 BPMN2.0 被更广泛的采纳、使用和商业化。
3. 对于 Java 开发者来说,Activiti 的首席架构师 Tom Baeyens 曾提到,Activiti 的首个目标就是要获得开发者的青睐。首先它在使用时极为方便,只是个 jar 文件,使用时仅需要将其放在类路径中,当然,Activiti 也可以作为独立服务器的方式使用;同时 Activiti 提供了很多 BPM 高级工具,其中还包括开发了协作工具,使得开发人员、业务人员和运维人员能够更好的协同工作。
本文将会介绍 Activiti 的基本概念,同时通过示例来介绍如何通过搭建 Activiti 开发环境和 Activiti API 使用,同时也会接触到 Activiti 的一些工具,希望通过阅读这篇文章,Activiti 能成为您在开发 BPM 系统时的一个选择。
2. Activiti 是由 jBPM 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目,构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上,旨在创建下一代的 BPM 解决方案。同时 Activiti 选择了 Apache 许可,一方面是希望 Activiti 能有更长久的生命力,因为它不受任何个人或是公司的控制而是属于整个社区,另一方面更是希望这个宽松的许可能够让 Activiti BPM 引擎和 BPMN2.0 被更广泛的采纳、使用和商业化。
3. 对于 Java 开发者来说,Activiti 的首席架构师 Tom Baeyens 曾提到,Activiti 的首个目标就是要获得开发者的青睐。首先它在使用时极为方便,只是个 jar 文件,使用时仅需要将其放在类路径中,当然,Activiti 也可以作为独立服务器的方式使用;同时 Activiti 提供了很多 BPM 高级工具,其中还包括开发了协作工具,使得开发人员、业务人员和运维人员能够更好的协同工作。
本文将会介绍 Activiti 的基本概念,同时通过示例来介绍如何通过搭建 Activiti 开发环境和 Activiti API 使用,同时也会接触到 Activiti 的一些工具,希望通过阅读这篇文章,Activiti 能成为您在开发 BPM 系统时的一个选择。
-----上面内容都是复制的-----
二.简单使用
直接看代码吧:
- /** 部署流程定义 **/
- @Test
- public void deploymentProcessDefinition(){
- Deployment deployment = processEngine.getRepositoryService() //与流程定义和部署相关的service
- .createDeployment() //创建一个部署对象
- .name("activiti入门") //添加部署对象的名字
- .addClasspathResource("diagrams/helloworld.bpmn") //从classpath资源中加载,一次只能加载一个文件
- .addClasspathResource("diagrams/helloworld.png") //从classpath资源中加载,一次只能加载一个文件
- .deploy();
- System.out.println(deployment.getId());
- System.out.println(deployment.getName());
- }
- /** 启动流程实例 **/
- @Test
- public void startProcessInstance(){
- String processDefinitionKey = "helloworld";
- ProcessInstance pi = processEngine.getRuntimeService() //与正在执行的流程实例和执行对象相关的service
- .startProcessInstanceByKey(processDefinitionKey); //使用流程定义的key启动流程实例,key对应的helloworld.bpmn--id的属性值,使用key值启动,默认按照最新版本流程启动
- System.out.println("流程实例id:"+pi.getId()); //流程实例id
- System.out.println("流程定义id:"+pi.getProcessDefinitionId()); //流程定义id
- }
- /** 查询当前人的个人任务 **/
- @Test
- public void findMyPersionTask(){
- System.out.println("**************************开始查询************************");
- String assignee = "张三";
- List<Task> list = processEngine.getTaskService()
- .createTaskQuery() //创建任务查询对象
- .taskAssignee(assignee) //指定个人任务查询,指定办理人
- .list();
- if(list!=null&&list.size()>0){
- for (Task task : list) {
- System.out.println("任务id:"+task.getId());
- System.out.println("任务名称:"+task.getName());
- System.out.println("任务创建时间:"+task.getCreateTime());
- System.out.println("任务办理人:"+task.getAssignee());
- System.out.println("流程实例id:"+task.getProcessInstanceId());
- System.out.println("执行对象id:"+task.getExecutionId());
- System.out.println("流程定义id:"+task.getProcessDefinitionId());
- }
- }
- System.out.println("**************************查询结束************************");
- }
- /** 完成我的任务 **/
- @Test
- public void completeMyPersionTask(){
- String taskId = "902";
- processEngine.getTaskService().complete(taskId); //与正在执行的任务管理相关的service
- System.out.println("完成任务:任务id:"+taskId);
- }
- /**
- * 查询历史任务
- */
- @Test
- public void queryHistoryTask(){
- //历史任务办理人
- String taskAssignee = "张三";
- List<HistoricTaskInstance> list = processEngine.getHistoryService().createHistoricTaskInstanceQuery().taskAssignee(taskAssignee).list();
- if(list!=null&&list.size()>0){
- for (HistoricTaskInstance hts : list) {
- System.out.println("任务ID:"+hts.getId());
- System.out.println("流程实例ID:"+hts.getProcessInstanceId());
- System.out.println("任务的办理人:"+hts.getAssignee());
- System.out.println("执行对象ID:"+hts.getExecutionId());
- System.out.println(hts.getStartTime()+" "+hts.getEndTime()+" "+hts.getDurationInMillis());
- }
- }
- }
- /**
- * 查询流程定义
- */
- @Test
- public void findProcessDefinition(){
- List<ProcessDefinition> list = processEngine.getRepositoryService()
- .createProcessDefinitionQuery()
- //指定查询条件
- // .deploymentId(deploymentId) //使用部署对象id查找
- // .processDefinitionId(processDefinitionId) //使用流程定义id查找
- // .processDefinitionKey(processDefinitionKey) //使用流程定义的key查找
- //排序
- // .orderByProcessDefinitionVersion().asc(); // 按照版本的升序排列
- // .orderByProcessDefinitionName().desc(); //按照流程定义的名称降序排列
- //返回结果集
- .list(); //返回一个集合列表,分装流程定义
- // .singleResult(); //返回唯一结果集
- // .count(); //返回结果集数量
- // .listPage(firstResult, maxResults); //分页查询
- if(list!=null&&list.size()>0){
- for (ProcessDefinition pd : list) {
- System.out.println("流程定义id:"+pd.getId());
- System.out.println("流程定义名称:"+pd.getName());
- System.out.println("流程定义的key:"+pd.getKey());
- System.out.println("流程定义版本:"+pd.getVersion());
- System.out.println("资源名称bpmn文件:"+pd.getResourceName());
- System.out.println("资源名称bpmn文件:"+pd.getDiagramResourceName());
- System.out.println("部署对象ID::"+pd.getDeploymentId());
- System.out.println("**********************************************");
- }
- }
- }
- /**
- * 删除流程定义
- */
- @Test
- public void deleteProcessDefinitionByDeploymentId(){
- String deploymentId = "1";
- processEngine.getRepositoryService()
- //不带级联的删除 只能删除没有启动的流程,如果流程启动就会抛出异常
- // .deleteDeployment(deploymentId);
- //级联删除 不管流程是否启动 都可以删除
- .deleteDeployment(deploymentId, true);
- System.out.println("删除成功!");
- }
- /**
- * 查看流程图
- */
- @Test
- public void viewPic(){
- //将生成的图片放置到文件夹下
- String deploymentId = "501";
- String resourceName = null;
- //获取图片资源名称
- List<String> resourcelist = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
- if(resourcelist!=null&&resourcelist.size()>0){
- for (String namestr : resourcelist) {
- if(namestr.indexOf(".png")>=0){
- resourceName = namestr;
- }
- }
- }
- //获取图片输入流
- InputStream in = processEngine.getRepositoryService()
- .getResourceAsStream(deploymentId, resourceName);
- //定义生成图片资源路径
- File file = new File("D:/"+resourceName);
- try {
- FileUtils.copyInputStreamToFile(in, file);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /** 附加功能 查询最新版本的流程定义 **/
- @Test
- public void findLastVersionProcessDefinition(){
- List<ProcessDefinition> list = processEngine.getRepositoryService()
- .createProcessDefinitionQuery()
- .orderByProcessDefinitionVersion().asc().list(); //使用流程定义版本升序排列
- Map<String, ProcessDefinition> map = new LinkedHashMap<String, ProcessDefinition>();
- if(list!=null&&list.size()>0){
- for (ProcessDefinition pd : list) {
- map.put(pd.getKey(), pd);
- }
- }
- List<ProcessDefinition> pdlist = new ArrayList<ProcessDefinition>(map.values());
- if(pdlist!=null&&pdlist.size()>0){
- for (ProcessDefinition pd : list) {
- System.out.println("流程定义id:"+pd.getId());
- System.out.println("流程定义名称:"+pd.getName());
- System.out.println("流程定义的key:"+pd.getKey());
- System.out.println("流程定义版本:"+pd.getVersion());
- System.out.println("资源名称bpmn文件:"+pd.getResourceName());
- System.out.println("资源名称bpmn文件:"+pd.getDiagramResourceName());
- System.out.println("部署对象ID::"+pd.getDeploymentId());
- System.out.println("**********************************************");
- }
- }
- }
这些都没啥可说的,部署注意一下,从方法上来看有5中部署方式:
- processEngine.getRepositoryService()
- .addBpmnModel(resourceName, bpmnModel)
- .addInputStream(resourceName, inputStream)
- .addString(resourceName, text)
- .addZipInputStream(zipInputStream)
- .addClasspathResource(resource)
主要使用的是两种:
1. ZIP方式,在项目整合时,最初用的就是这种,用eclipse插件将流程图画好后,上传至服务器,自动解析进行部署,
- <span style="font-size:18px;"> /**
- * 部署流程定义
- */
- @Override
- public boolean saveNewDeploy(String filepath,String filename) {
- boolean returnflag = true;
- ZipInputStream zipInputStream;
- try {
- System.out.println(filepath);
- zipInputStream = new ZipInputStream(new FileInputStream(new File(filepath)));
- repositoryService.createDeployment()
- .name(filename)
- .addZipInputStream(zipInputStream)
- .deploy();
- System.out.println("部署完成!");
- return returnflag;
- } catch (FileNotFoundException e) {
- returnflag = false;
- e.printStackTrace();
- }
- return returnflag;
- }</span>
传入上传文件的路径以及名字就可以进行部署了。
2. addString(resourceName, text)方式,这种方式是整合Activiti Modeler设计器后,比较常见的部署方式,
然后就是可能会遇到的一些问题了:
1. eclipse 安装activiti插件后为什么 画好了流程图保存后 只生成了 bpmn文件,而没有生成对应的png图片呢:
perference -> Activiti -> Save Actions 勾选 Create Process definition image when saving the diagram即可
2. 还有一处要注意:
初尝试 就写这么多吧,下一期说说怎么和项目整合与业务结合,然后再说说怎样集成设计器等
提供一下 demo源码,这里写的比较全些:http://download.youkuaiyun.com/detail/qq_27063119/9911518
到这里配置的就差不多了!
关于service层有段代码,注释为:查找数据库来确定需要执行哪个 后面会说这是干什么的。
通过向该表中添加一些配置数据后,如:
业务实现层没啥可贴的了
以上就是怎样关联业务的解析:
项目整合Activiti关联业务(二)
1.说说怎样整合Activiti
先提一下有个开源项目lemon(一款基于Java开发的开源OA),对于我的整合帮助很大,遇到的一些问题参考该项目源码都得到了解决,在此感谢一下!
首先我的整合是基于maven的,所以没有配置maven的童鞋需要先配置一下,pom文件需要加入:
- <properties>
- <activiti.version>5.18.0</activiti.version>
- </properties>
- <dependency>
- <groupId>org.activiti</groupId>
- <artifactId>activiti-modeler</artifactId>
- <version>${activiti.version}</version>
- </dependency>
- <dependency>
- <groupId>org.activiti</groupId>
- <artifactId>activiti-spring</artifactId>
- <version>${activiti.version}</version>
- </dependency>
- <dependency>
- <groupId>org.activiti</groupId>
- <artifactId>activiti-json-converter</artifactId>
- <version>${activiti.version}</version>
- </dependency>
可以看出 使用的版本是5.18.0,为什么这样呢,其实为了少走坑,写这篇博客的时候 lemon 项目使用的Activiti也是5.18.0的,
与Spring整合需要增加配置文件至Resource文件夹下:activiti-context.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
- <!-- spring负责创建流程引擎的配置文件 -->
- <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
- <!-- 数据源 -->
- <property name="dataSource" ref="myDataSource" />
- <!-- 配置事务管理器,统一事务 -->
- <property name="transactionManager" ref="transactionManager" />
- <!-- 设置建表策略,如果没有表,自动创建表 -->
- <property name="databaseSchemaUpdate" value="true" />
- <!-- 用于更改流程节点的执行行为 -->
- <property name="activityBehaviorFactory" ref="activityBehaviorFactoryExt"/>
- <!-- 生成流程图的字体 解决图片中文不显示问题-->
- <property name="activityFontName" value="黑体"></property>
- <property name="labelFontName" value="黑体"></property>
- </bean>
- <bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/>
- <!-- 创建流程引擎对象 -->
- <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
- <property name="processEngineConfiguration" ref="processEngineConfiguration" />
- </bean>
- <!--
- 相当于下面的代码
- RepositoryServicie repositoryService = processEngine.getRepositoryService();
- RuntimeServicie repositoryService = processEngine.getRuntimeServicie();
- TaskServicie taskServicie = processEngine.getTaskServicie();
- HistoryServicie historyServicie = processEngine.getHistoryServicie();
- -->
- <!-- 由流程引擎对象,提供的方法,创建项目中使用的Activiti工作流的Service -->
- <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
- <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
- <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
- <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
- <bean id="formService" factory-bean="processEngine" factory-method="getFormService" />
- </beans>
其中配置的数据源myDataSource 是使用SSH项目配置的,这里无需再次配置
最后在Spring配置文件中引入一下:
- <import resource="classpath*:/activiti-context.xml" />
到这里配置的就差不多了!
2.怎样关联业务的:
一句话:使用正在执行对象表中的一个字段BUSINESS_KEY(Activiti提供的一个字段),让启动的流程(流程实例)关联业务
具体怎样实现的呢,在启动流程实例时需要将提交的表单关联住工作流,这里有个小技巧,设计流程图时要将process ID(流程标识符)与表单类型标志人为设置成一样的,通过key进行启动流程实例,操作起来很方便,默认会使用最新版本的流程进行启动,看代码:
controller层:
- @RequestMapping(value="/startProcess.do")
- public void startProcess(HttpServletRequest request,HttpServletResponse response,String formKey,String formid){
- LogonInfo userinfo = (LogonInfo) request.getSession().getAttribute(SessionKey.UserInfoKey);
- if(actworkflowservice.startProcess(formKey, userinfo.getUserInfo().getId()+"", formid)){
- this.writeJson(response, true);
- }else{
- this.writeJson(response, false);
- }
- }
比如我提交的是请假单,那么在前台默认指定formKey就是LeaveBill,并且在先前设计流程图的时候就要指定process ID(流程标识符)也是 LeaveBill,同时将该表单的id也传过来。
service层:
- /**
- * 启动流程实例
- */
- @Override
- public boolean startProcess(String formKey,String userid,String formid) {
- //查找数据库来确定需要执行哪个
- String customBeanName = approvalBeanConfDao.getApprovalSerBeanById(formKey).getBeanName();
- IActWorkFlowCustomService customService = null;
- if(customBeanName!=null){
- WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
- customService = (IActWorkFlowCustomService)wac.getBean(customBeanName);
- customService.startRunTask(formid);
- }
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("userid", userid);
- //使用流程变量设置字符串(格式 : LeaveBill.Id 的形式)
- //使用正在执行对象表中的一个字段BUSINESS_KEY(Activiti提供的一个字段),让启动的流程(流程实例)关联业务
- String objId = formKey + "." +formid;
- map.put("objId", objId);
- runtimeService.startProcessInstanceByKey(formKey, objId, map);
- return true;
- }
关于service层有段代码,注释为:查找数据库来确定需要执行哪个 后面会说这是干什么的。
service层会将正在执行对象表中的BUSINESS_KEY字段写入 表单类型.表单ID,这样就起到了关联作用
3.怎样在Activiti审批中操作业务的:
1.使用反射机制,之前做过后来发现不支持事务就没用了
2.使用spring中的bean直接调用方法,现在使用的就是这种
首先需要新建一张表存放配置 (1).表单类型(唯一标志符,比如:LeaveBill),(2)该表单关联的业务service层的bean名称,(3)查看该表单的url地址,下面就是实体类:
- import javax.persistence.Entity;
- import javax.persistence.Id;
- /**
- * 审批 个性化接口实现 bean 对照表
- * @author nuohy
- * @Description: TODO
- * @date 2017年7月21日
- */
- @Entity(name="actself_approvalBeanConf")
- public class ApprovalBeanConf {
- private String id;
- private String beanName;
- private String showFormUrl;
- @Id
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public String getBeanName() {
- return beanName;
- }
- public void setBeanName(String beanName) {
- this.beanName = beanName;
- }
- public String getShowFormUrl() {
- return showFormUrl;
- }
- public void setShowFormUrl(String showFormUrl) {
- this.showFormUrl = showFormUrl;
- }
- public ApprovalBeanConf(String id, String beanName) {
- super();
- this.id = id;
- this.beanName = beanName;
- }
- public ApprovalBeanConf() {
- super();
- }
- public ApprovalBeanConf(String id, String beanName, String showFormUrl) {
- super();
- this.id = id;
- this.beanName = beanName;
- this.showFormUrl = showFormUrl;
- }
- @Override
- public String toString() {
- return "ApprovalBeanConf [id=" + id + ", beanName=" + beanName + ", showFormUrl=" + showFormUrl + "]";
- }
- }
通过向该表中添加一些配置数据后,如:
LeaveBill leavebillservice activiti/leave/turnShowForm.do
并且业务层的service接口必须继承IActWorkFlowCustomService接口
IActWorkFlowCustomService.java:
- import java.util.Map;
- /**
- * 所有业务审批service 的接口 必须继承该接口 实现其方法
- * @author nuohy
- * @Description: TODO
- * @date 2017年7月21日
- */
- public interface IActWorkFlowCustomService {
- /**
- * 设置流程变量
- * @param formid
- * @return
- */
- public Map<String, Object> setvariables(String formid);
- /**
- * 整个流程开始时需要执行的任务
- * @param formid
- */
- public void startRunTask(String formid);
- /**
- * 整个流程结束需要执行的任务
- * @param formid
- */
- public void endRunTask(String formid);
- }
于是可以在流程启动的时候操作一下业务层的方法,在结束的时候操作业务的方法,也可以在流程执行的中间操作业务的方法
放上一段请假业务层的代码:ILeaveBillService.java
- import java.text.ParseException;
- import java.util.Map;
- public interface ILeaveBillService extends IActWorkFlowCustomService{
- public DataGrid<Map<String, Object>> getleavebills(String userid,PageInfo page);
- public boolean addLeave(LeaveBill leaveBill);
- public boolean delleave(LeaveBill leaveBill);
- public FLeaveBill getLeaveBill(String formid) throws ParseException;
- }
业务实现层没啥可贴的了
说到这里还没有说到重点:activiti到底是怎样操作的呢,上代码,这里就解释了上面说的为什么在启动流程实例时会有段查找数据库进行确定执行哪个方法的代码:
流程开始时执行的方法上写过了就不重复了,接下来看看执行过程中以及执行结束时怎样调用业务层的:
- /**
- * 完成提交任务
- */
- @Override
- public int completeProcess(String formtype,String remark, String taskId,String userId,String formid,String outcome) {
- //任务Id 查询任务对象
- Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
- //任务对象 获取流程实例Id
- String processInstanceId = task.getProcessInstanceId();
- //设置审批人的userId
- Authentication.setAuthenticatedUserId(userId);
- //添加记录
- taskService.addComment(taskId, processInstanceId, remark);
- /**
- * 如果连线的名称是'默认提交',那么就不需要设置,如果不是,就需要设置流程变量
- * 在完成任务之前,设置流程变量,按照连线的名称,去完成任务
- 流程变量的名称:outcome
- 流程变量的值:连线的名称
- */
- Map<String, Object> variables = new HashMap<String,Object>();
- if(outcome!=null && !outcome.equals("默认提交")){
- variables.put("outcome", outcome);
- }
- //设置流程变量 条件控制
- String customBean = null;
- //查找数据库来确定需要执行哪个
- customBean = approvalBeanConfDao.getApprovalSerBeanById(formtype).getBeanName();
- IActWorkFlowCustomService customService = null;
- if(customBean!=null){
- WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
- customService = (IActWorkFlowCustomService)wac.getBean(customBean);
- if(customService!=null){
- Map<String, Object> map = customService.setvariables(formid);
- variables.putAll(map);
- }
- }
- //完成办理
- taskService.complete(taskId,variables);
- //执行结束 更改状态
- ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
- if(processInstance==null&&customService!=null){
- if(customService!=null){
- customService.endRunTask(formid);
- }
- return 2;
- }else{
- return 1;
- }
- }
为了更好的学习交流 贴上完整的service代码:ActWorkFlowCommServiceImpl.java
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.zip.ZipInputStream;
- import javax.annotation.Resource;
- import org.activiti.engine.FormService;
- import org.activiti.engine.HistoryService;
- import org.activiti.engine.RepositoryService;
- import org.activiti.engine.RuntimeService;
- import org.activiti.engine.TaskService;
- import org.activiti.engine.history.HistoricTaskInstance;
- import org.activiti.engine.history.HistoricVariableInstance;
- import org.activiti.engine.impl.identity.Authentication;
- import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
- import org.activiti.engine.impl.pvm.PvmTransition;
- import org.activiti.engine.impl.pvm.process.ActivityImpl;
- import org.activiti.engine.repository.Deployment;
- import org.activiti.engine.repository.ProcessDefinition;
- import org.activiti.engine.runtime.ProcessInstance;
- import org.activiti.engine.task.Comment;
- import org.activiti.engine.task.Task;
- import org.activiti.engine.task.TaskQuery;
- import org.apache.commons.lang.StringUtils;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import org.springframework.web.context.ContextLoader;
- import org.springframework.web.context.WebApplicationContext;
- /**
- * Activiti工作流引擎 核心 service
- * @author nuohy
- * @Description: TODO
- * @date 2017年7月21日
- */
- @Service(value="actworkflowservice")
- @Transactional
- public class ActWorkFlowCommServiceImpl implements IActWorkFlowCommService{
- @Resource(name="repositoryService")
- private RepositoryService repositoryService;
- @Resource(name="runtimeService")
- private RuntimeService runtimeService;
- @Resource(name="taskService")
- private TaskService taskService;
- @Resource(name="formService")
- private FormService formService;
- @Resource(name="historyService")
- private HistoryService historyService;
- @Resource(name="leavebilldao")
- private ILeaveBillDao leavebilldao;
- @Resource(name="rebMoneyDao")
- private IRebMoneyDao rebMoneyDao;
- @Resource(name="approvalBeanConfDao")
- private IApprovalBeanConfDao approvalBeanConfDao;
- /**
- * 部署流程定义
- */
- @Override
- public boolean saveNewDeploy(String filepath,String filename) {
- boolean returnflag = true;
- ZipInputStream zipInputStream;
- try {
- System.out.println(filepath);
- zipInputStream = new ZipInputStream(new FileInputStream(new File(filepath)));
- repositoryService.createDeployment()
- .name(filename)
- .addZipInputStream(zipInputStream)
- .deploy();
- System.out.println("部署完成!");
- return returnflag;
- } catch (FileNotFoundException e) {
- returnflag = false;
- e.printStackTrace();
- }
- return returnflag;
- }
- /**
- * 查询部署对象信息
- */
- @Override
- public List<FDeployment> findDeployList() {
- List<FDeployment> relist = null;
- List<Deployment> list = repositoryService.createDeploymentQuery().orderByDeploymenTime().desc().list();
- if(list!=null&&list.size()>0){
- relist = new ArrayList<FDeployment>();
- for (Deployment dm : list) {
- FDeployment fDeployment = new FDeployment(dm.getId(), dm.getName(), dm.getDeploymentTime(), dm.getCategory(), dm.getTenantId());
- relist.add(fDeployment);
- System.err.println(fDeployment.toString());
- }
- }
- return relist;
- }
- /**
- * 删除部署信息
- */
- @Override
- public boolean deldeployment(String deploymentid) {
- try{
- repositoryService.deleteDeployment(deploymentid, true);
- }catch(Exception e){
- return false;
- }
- return true;
- }
- /**
- * 启动流程实例
- */
- @Override
- public boolean startProcess(String formKey,String userid,String formid) {
- //查找数据库来确定需要执行哪个
- String customBeanName = approvalBeanConfDao.getApprovalSerBeanById(formKey).getBeanName();
- IActWorkFlowCustomService customService = null;
- if(customBeanName!=null){
- WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
- customService = (IActWorkFlowCustomService)wac.getBean(customBeanName);
- customService.startRunTask(formid);
- }
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("userid", userid);
- //使用流程变量设置字符串(格式 : LeaveBill.Id 的形式)
- //使用正在执行对象表中的一个字段BUSINESS_KEY(Activiti提供的一个字段),让启动的流程(流程实例)关联业务
- String objId = formKey + "." +formid;
- map.put("objId", objId);
- runtimeService.startProcessInstanceByKey(formKey, objId, map);
- return true;
- }
- /**
- * 查询流程定义信息
- */
- @Override
- public List<FProcessDefinition> findProcessDefinitionList(){
- List<FProcessDefinition> relist = null;
- List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionVersion().desc().list();
- if(list!=null&&list.size()>0){
- relist = new ArrayList<FProcessDefinition>();
- for (ProcessDefinition pd : list) {
- FProcessDefinition fProcessDefinition = new FProcessDefinition(pd.getId(), pd.getCategory(), pd.getName(), pd.getKey(), pd.getDescription(), pd.getVersion(), pd.getResourceName(), pd.getDeploymentId(),pd.getDiagramResourceName(), pd.hasStartFormKey(), pd.isSuspended(), pd.getTenantId());
- relist.add(fProcessDefinition);
- System.out.println(fProcessDefinition.toString());
- }
- }
- return relist;
- }
- /**
- * 查看总体流程图
- */
- @Override
- public InputStream lookProcessImage(String deploymentid, String imagename) {
- InputStream in = repositoryService.getResourceAsStream(deploymentid, imagename);
- return in;
- }
- /**
- * 查看当前流程图(1)
- * @param taskid
- */
- @Override
- public ProcessDefinition lookCurrentProcessImage(String taskId) {
- //任务ID
- //1:获取任务ID,获取任务对象,使用任务对象获取流程定义ID,查询流程定义对象
- //使用任务ID,查询任务对象
- Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
- //获取流程定义ID
- String processDefinitionId = task.getProcessDefinitionId();
- //查询流程定义的对象
- ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//创建流程定义查询对象,对应表act_re_procdef
- .processDefinitionId(processDefinitionId)//使用流程定义ID查询
- .singleResult();
- return pd;
- }
- /** 查看当前流程图(2) -- 公用
- * 查看当前活动,获取当期活动对应的坐标x,y,width,height,将4个值存放到Map<String,Object>中
- * map集合的key:表示坐标x,y,width,height
- * map集合的value:表示坐标对应的值
- */
- @Override
- public Map<String, Object> findCoordingByTask(String taskId) {
- //存放坐标
- Map<String, Object> map = new HashMap<String,Object>();
- //使用任务ID,查询任务对象
- Task task = taskService.createTaskQuery()//
- .taskId(taskId)//使用任务ID查询
- .singleResult();
- //获取流程定义的ID
- String processDefinitionId = task.getProcessDefinitionId();
- //获取流程定义的实体对象(对应.bpmn文件中的数据)
- ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
- //流程实例ID
- String processInstanceId = task.getProcessInstanceId();
- //使用流程实例ID,查询正在执行的执行对象表,获取当前活动对应的流程实例对象
- ProcessInstance pi = runtimeService.createProcessInstanceQuery()//创建流程实例查询
- .processInstanceId(processInstanceId)//使用流程实例ID查询
- .singleResult();
- //获取当前活动的ID
- String activityId = pi.getActivityId();
- //获取当前活动对象
- ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//活动ID
- //获取坐标
- map.put("x", activityImpl.getX());
- map.put("y", activityImpl.getY());
- map.put("width", activityImpl.getWidth());
- map.put("height", activityImpl.getHeight());
- return map;
- }
- @Override
- public ProcessDefinition lookCurrentProcessImgByFormId(String formid, String formKey) {
- Task task = taskService.createTaskQuery().processInstanceBusinessKey(formKey+"."+formid).singleResult();
- String processDefinitionId = task.getProcessDefinitionId();
- ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//创建流程定义查询对象,对应表act_re_procdef
- .processDefinitionId(processDefinitionId)//使用流程定义ID查询
- .singleResult();
- return pd;
- }
- @Override
- public Map<String, Object> findCoordingByTaskByFormId(String formid, String formKey) {
- //存放坐标
- Map<String, Object> map = new HashMap<String,Object>();
- Task task = taskService.createTaskQuery().processInstanceBusinessKey(formKey+"."+formid).singleResult();
- //获取流程定义的ID
- String processDefinitionId = task.getProcessDefinitionId();
- //获取流程定义的实体对象(对应.bpmn文件中的数据)
- ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
- //流程实例ID
- String processInstanceId = task.getProcessInstanceId();
- //使用流程实例ID,查询正在执行的执行对象表,获取当前活动对应的流程实例对象
- ProcessInstance pi = runtimeService.createProcessInstanceQuery()//创建流程实例查询
- .processInstanceId(processInstanceId)//使用流程实例ID查询
- .singleResult();
- //获取当前活动的ID
- String activityId = pi.getActivityId();
- //获取当前活动对象
- ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//活动ID
- //获取坐标
- map.put("x", activityImpl.getX());
- map.put("y", activityImpl.getY());
- map.put("width", activityImpl.getWidth());
- map.put("height", activityImpl.getHeight());
- return map;
- }
- /**
- * 查看个人任务列表
- */
- @Override
- public DataGrid<Map<String, Object>> mytasklist(String userid,PageInfo page) {
- DataGrid<Map<String, Object>> dataGrid = new DataGrid<Map<String, Object>>();
- int pagenum = page.getPage();
- int rowsnum = page.getRows();
- int firstResult = (pagenum-1)*rowsnum;
- int maxResults = firstResult+rowsnum;
- TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(userid);
- long allcount = taskQuery.count();
- dataGrid.setTotal(allcount);
- List<Task> list = taskQuery.orderByTaskCreateTime().desc().listPage(firstResult, maxResults);
- List<Map<String, Object>> listmap = new ArrayList<Map<String, Object>>();
- for (Task task : list) {
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("id", task.getId());
- map.put("name", task.getName());
- map.put("description", task.getDescription());
- map.put("priority", task.getPriority());
- map.put("owner", task.getOwner());
- map.put("assignee", task.getAssignee());
- map.put("delegationState", task.getDelegationState());
- map.put("processInstanceId", task.getProcessInstanceId());
- map.put("executionId", task.getExecutionId());
- map.put("processDefinitionId", task.getProcessDefinitionId());
- map.put("createTime", task.getCreateTime());
- map.put("taskDefinitionKey", task.getTaskDefinitionKey());
- map.put("dueDate", task.getDueDate());
- map.put("category", task.getCategory());
- map.put("parentTaskId", task.getParentTaskId());
- map.put("tenantId", task.getTenantId());
- Map<String, Object> FormModel_map = findFormModelByTaskId(task.getId());
- //表单中避免使用 hfmx_actWorkFlow_formType 关键字
- /*map.put("hfmx_actWorkFlow_formType", FormModel_map.get("hfmx_actWorkFlow_formType"));
- map.put("formid", FormModel_map.get("id"));*/
- map.putAll(FormModel_map);
- List<Map<String,Object>> searchForMap = leavebilldao.searchForMap("select * from sysuser where id="+task.getAssignee());
- map.put("processUser", searchForMap.get(0).get("name"));
- listmap.add(map);
- }
- dataGrid.setRows(listmap);
- return dataGrid;
- }
- @Override
- public Map<String, Object> findFormModelByTaskId(String taskId) {
- //任务Id 查询任务对象
- Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
- //任务对象 获取流程实例Id
- String processInstanceId = task.getProcessInstanceId();
- //流程实例Id 查询正在执行的执行对象表 返回流程实例对象
- ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
- //流程实例对象 获取 BUSINESS_KEY ,从而获取表单对象
- String businessKey = processInstance.getBusinessKey();
- String[] split = businessKey.split("\\.");
- Map<String, Object> map = new HashMap<String,Object>();
- ApprovalBeanConf approvalBeanConf = approvalBeanConfDao.getApprovalSerBeanById(split[0]);
- map.put("hfmx_actWorkFlow_formType", split[0]);
- map.put("showFormUrl", approvalBeanConf.getShowFormUrl());
- map.put("formid", split[1]);
- return map;
- }
- /**
- * 完成提交任务
- */
- @Override
- public int completeProcess(String formtype,String remark, String taskId,String userId,String formid,String outcome) {
- //任务Id 查询任务对象
- Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
- //任务对象 获取流程实例Id
- String processInstanceId = task.getProcessInstanceId();
- //设置审批人的userId
- Authentication.setAuthenticatedUserId(userId);
- //添加记录
- taskService.addComment(taskId, processInstanceId, remark);
- /**
- * 如果连线的名称是'默认提交',那么就不需要设置,如果不是,就需要设置流程变量
- * 在完成任务之前,设置流程变量,按照连线的名称,去完成任务
- 流程变量的名称:outcome
- 流程变量的值:连线的名称
- */
- Map<String, Object> variables = new HashMap<String,Object>();
- if(outcome!=null && !outcome.equals("默认提交")){
- variables.put("outcome", outcome);
- }
- //设置流程变量 条件控制
- String customBean = null;
- //查找数据库来确定需要执行哪个
- customBean = approvalBeanConfDao.getApprovalSerBeanById(formtype).getBeanName();
- IActWorkFlowCustomService customService = null;
- if(customBean!=null){
- WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
- customService = (IActWorkFlowCustomService)wac.getBean(customBean);
- if(customService!=null){
- Map<String, Object> map = customService.setvariables(formid);
- variables.putAll(map);
- }
- }
- //完成办理
- taskService.complete(taskId,variables);
- //执行结束 更改状态
- ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
- if(processInstance==null&&customService!=null){
- if(customService!=null){
- customService.endRunTask(formid);
- }
- return 2;
- }else{
- return 1;
- }
- }
- /**
- * 通过当前任务Id 获取 批注时的备注信息
- */
- @Override
- public List<FComment> getComment(String currenttaskId) {
- List<FComment> relist = new ArrayList<FComment>();
- List<Comment> list = new ArrayList<Comment>();
- Task task = taskService.createTaskQuery().taskId(currenttaskId).singleResult();
- String processInstanceId = task.getProcessInstanceId();
- List<HistoricTaskInstance> htilist = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list();
- if(htilist!=null&&htilist.size()>0){
- for (HistoricTaskInstance hti : htilist) {
- String htaskid = hti.getId();
- List<Comment> tasklist = taskService.getTaskComments(htaskid);
- list.addAll(tasklist);
- }
- }
- for (Comment com : list) {
- FComment fc = new FComment();
- fc.setId(com.getId());
- fc.setUserid(com.getUserId());
- List<Map<String,Object>> searchForMap = leavebilldao.searchForMap("select * from sysuser where id="+com.getUserId());
- fc.setUserName(searchForMap.get(0).get("name").toString());
- fc.setTime(com.getTime());
- fc.setTaskId(com.getTaskId());
- fc.setProcessInstanceId(com.getProcessInstanceId());
- fc.setType(com.getType());
- fc.setFullMessage(com.getFullMessage());
- relist.add(fc);
- }
- System.out.println(relist.toString());
- return relist;
- }
- /**
- * 通过请假单id查找 批注信息
- */
- @Override
- public List<FComment> getCommentByLeavebillId(String leaveBillId) {
- List<FComment> relist = new ArrayList<FComment>();
- LeaveBill leaveBill = new LeaveBill();
- String simpleName = leaveBill.getClass().getSimpleName();
- String objId = simpleName+"."+leaveBillId;
- HistoricVariableInstance hvi = historyService.createHistoricVariableInstanceQuery().variableValueEquals("objId", objId).orderByProcessInstanceId().asc().singleResult();
- String processInstanceId = hvi.getProcessInstanceId();
- List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
- for (Comment com : list) {
- FComment fc = new FComment();
- fc.setId(com.getId());
- fc.setUserid(com.getUserId());
- List<Map<String,Object>> searchForMap = leavebilldao.searchForMap("select * from sysuser where id="+com.getUserId());
- fc.setUserName(searchForMap.get(0).get("name").toString());
- fc.setTime(com.getTime());
- fc.setTaskId(com.getTaskId());
- fc.setProcessInstanceId(com.getProcessInstanceId());
- fc.setType(com.getType());
- fc.setFullMessage(com.getFullMessage());
- relist.add(fc);
- }
- Collections.reverse(relist);
- return relist;
- }
- /**
- * 已知任务ID,查询ProcessDefinitionEntiy对象,从而获取当前任务完成之后的连线名称,并放置到List<String>集合中
- */
- @Override
- public List<String> findOutComeListByTaskId(String taskId) {
- //返回存放连线的名称集合
- List<String> list = new ArrayList<String>();
- //1:使用任务ID,查询任务对象
- Task task = taskService.createTaskQuery()//
- .taskId(taskId)//使用任务ID查询
- .singleResult();
- //2:获取流程定义ID
- String processDefinitionId = task.getProcessDefinitionId();
- //3:查询ProcessDefinitionEntiy对象
- ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
- //使用任务对象Task获取流程实例ID
- String processInstanceId = task.getProcessInstanceId();
- //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
- ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
- .processInstanceId(processInstanceId)//使用流程实例ID查询
- .singleResult();
- //获取当前活动的id
- String activityId = pi.getActivityId();
- //4:获取当前的活动
- ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);
- //5:获取当前活动完成之后连线的名称
- List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
- if(pvmList!=null && pvmList.size()>0){
- for(PvmTransition pvm:pvmList){
- String name = (String) pvm.getProperty("name");
- if(StringUtils.isNotBlank(name)){
- list.add(name);
- }else{
- list.add("默认提交");
- }
- }
- }
- return list;
- }
- }
请假单实体类:LeaveBill.java
- import java.util.Date;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.Id;
- @Entity(name="actself_leavebill")
- public class LeaveBill {
- private int id;
- private String days;
- private String content;
- private String remark;
- private Date leavedate;
- private int state; //0-初始录入 1-开始审批 2-审批完成
- private String user_id;
- @Id
- @GeneratedValue
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getDays() {
- return days;
- }
- public void setDays(String days) {
- this.days = days;
- }
- public String getContent() {
- return content;
- }
- public void setContent(String content) {
- this.content = content;
- }
- public String getRemark() {
- return remark;
- }
- public void setRemark(String remark) {
- this.remark = remark;
- }
- public Date getLeavedate() {
- return leavedate;
- }
- public void setLeavedate(Date leavedate) {
- this.leavedate = leavedate;
- }
- public int getState() {
- return state;
- }
- public void setState(int state) {
- this.state = state;
- }
- public String getUser_id() {
- return user_id;
- }
- public void setUser_id(String user_id) {
- this.user_id = user_id;
- }
- public LeaveBill() {
- super();
- }
- public LeaveBill(int id, String days, String content, String remark, Date leavedate, int state, String user_id) {
- super();
- this.id = id;
- this.days = days;
- this.content = content;
- this.remark = remark;
- this.leavedate = leavedate;
- this.state = state;
- this.user_id = user_id;
- }
- public LeaveBill(String days, String content, String remark, Date leavedate, int state, String user_id) {
- super();
- this.days = days;
- this.content = content;
- this.remark = remark;
- this.leavedate = leavedate;
- this.state = state;
- this.user_id = user_id;
- }
- @Override
- public String toString() {
- return "LeaveBill [id=" + id + ", days=" + days + ", content=" + content + ", remark=" + remark + ", leavedate="
- + leavedate + ", state=" + state + ", user_id=" + user_id + "]";
- }
- }
下次说说怎样整合在线流程图设计器,以及遇到的问题和解决方法,感谢阅读!
activiti 修改为你的项目名即可,这里整合就结束了,是不是非常之快!
(2)流程部署的时候,发生错误:
修改为:
如果需要更改分支条件名的字体那么需要修改org.activiti.image.impl.DefaultProcessDiagramCanvas.java 大概219行左右,原代码为:
修改为:
编译好后,替换至jar中,即可,更新pom,刷新依赖包即可
加入三个类:
对网关的条件判断类,ExclusiveGatewayActivityBehaviorExt.java
activiti 条件表达式 解析类,ActivitiConditionAnalysis.java (具体怎样解析,都在这里面写好实现,这里我用了ScriptEngine引擎)
最后需要在spring-activiti.xml中配置一下:
比较匆忙,以后再补充吧!
整合在线流程图设计器及遇到的问题(三)
1.整合注意事项:
在整合之前先要确定你的项目spring版本是否为4.0+,如果不是就麻烦些了,因为activiti modeler设计器需要用到rest风格即RestControl注解,如果是低版本的,那么我猜想需要将这部分代码单独拿出来进行处理,不过我没有做过,这点需要注意了。
2.开始整合:
直接下载给的项目(maven版本的),
链接:http://download.youkuaiyun.com/detail/qq_27063119/9916164
打开activiti项目,复制editor-app目录至项目的webapp下,将modeler.html拷至webapp目录下,将stencilset.json拷贝至resource目录下,spring MVC配置中加入:
- <!--加入Spring Activiti-Modeler的运行配置 -->
- <context:component-scan base-package="org.activiti.rest.editor.*"/>
- <context:component-scan base-package="org.activiti.rest.common.*"/>
最后还有一个地方,如果你的项目名不是activiti的,需要打开editor-app -> app-cfg.js,将
- var ACTIVITI = ACTIVITI || {};
- ACTIVITI.CONFIG = {
- 'contextRoot' : '/activiti/service',
- };
activiti 修改为你的项目名即可,这里整合就结束了,是不是非常之快!
activiti项目中已经给了创建modeler并且跳转至流程编辑页面的controller
- <span style="font-size:18px;">@Controller
- @RequestMapping("/model")
- public class ModuleController {
- private Logger logger = LoggerFactory.getLogger(ModuleController.class);
- @Autowired
- private RepositoryService repositoryService;
- @RequestMapping(value = "create")
- public void create(@RequestParam("name") String name, @RequestParam("key") String key, @RequestParam("description") String description,
- HttpServletRequest request, HttpServletResponse response) {
- try {
- ObjectMapper objectMapper = new ObjectMapper();
- ObjectNode editorNode = objectMapper.createObjectNode();
- editorNode.put("id", "canvas");
- editorNode.put("resourceId", "canvas");
- ObjectNode stencilSetNode = objectMapper.createObjectNode();
- stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
- editorNode.put("stencilset", stencilSetNode);
- Model modelData = repositoryService.newModel();
- ObjectNode modelObjectNode = objectMapper.createObjectNode();
- modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name);
- modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
- description = StringUtils.defaultString(description);
- modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
- modelData.setMetaInfo(modelObjectNode.toString());
- modelData.setName(name);
- modelData.setKey(StringUtils.defaultString(key));
- repositoryService.saveModel(modelData);
- repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
- response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + modelData.getId());
- } catch (Exception e) {
- logger.error("创建模型失败:", e);
- }
- }
- }</span>
项目访问地址为:
http://IP地址:端口号/项目名/model/create?name=test&key=test&description=testModel
3.遇到的问题以及解决办法:
(1)流程图中文错误乱码问题
通过activiti modeler画好流程图保存后,发现生成的流程图片中文显示乱码,或者不显示,
这是由于缺少字体的原因:
向activiti的配置文件中(spring-activiti.xml)加入字体配置即可:
- <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
- <!-- 数据源 -->
- <property name="dataSource" ref="myDataSource" />
- <!-- 配置事务管理器,统一事务 -->
- <property name="transactionManager" ref="transactionManager" />
- <!-- 设置建表策略,如果没有表,自动创建表 -->
- <property name="databaseSchemaUpdate" value="true" />
- <!-- 用于更改流程节点的执行行为 -->
- <property name="activityBehaviorFactory" ref="activityBehaviorFactoryExt"/>
- <span style="color:#ff0000;"><!-- 生成流程图的字体 解决图片中文不显示问题-->
- <property name="activityFontName" value="宋体"></property>
- <property name="labelFontName" value="宋体"></property></span>
- </bean>
(2)流程部署的时候,发生错误:
java.lang.NoSuchMethodError: org.apache.commons.collections.CollectionUtils.isNotEmpty(Ljava/util/Collection;)Z
这是由于common.jar冲突了,activiti也会引入该jar,如果你的项目之前引入过不同版本的该jar,那么部署流程的时候就会发生该错误。
(3)生成流程图片时,分支条件名不显示的问题
下载源码activiti-image-generator,修改org.activiti.image.impl.DefaultProcessDiagramGenerator.java 大概655行左右:
原代码为:
- <span style="font-size:18px;">if (labelGraphicInfo != null) {
- processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
- }</span>
- <span style="font-size:18px;">//2017-7-27 @yn 针对解决流程设计器绘图 生成图片分支条件不显示问题
- if (labelGraphicInfo != null) {
- processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
- }else{
- GraphicInfo lineCenter = getLineCenter(graphicInfoList);
- processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false);
- }</span>
- LABEL_FONT = new Font(labelFontName, Font.ITALIC, 10);
- //2017-7-27 @yn 修改流程分支条件字体样式
- LABEL_FONT = new Font(labelFontName, Font.BOLD, 14);
(4)如何自定义表达式解析器
加入三个类:
扩展缺省的流程节点默认工厂类 ActivityBehaviorFactoryExt.java
- import javax.annotation.Resource;
- import org.activiti.bpmn.model.ExclusiveGateway;
- import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
- import org.activiti.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
- import org.springframework.stereotype.Component;
- /**
- * @author nuohy
- * @Description: 扩展缺省的流程节点默认工厂类,实现对Activiti节点的执行的默认行为的更改
- * @date 2017年7月22日
- */
- @Component(value="activityBehaviorFactoryExt")
- public class ActivityBehaviorFactoryExt extends DefaultActivityBehaviorFactory {
- @Resource(name="exclusiveGatewayActivityBehaviorExt")
- private ExclusiveGatewayActivityBehaviorExt exclusiveGatewayActivityBehaviorExt;
- /**
- * 通过Spring容器注入新的分支条件行为执行类
- */
- public void setExclusiveGatewayActivityBehaviorExt(ExclusiveGatewayActivityBehaviorExt exclusiveGatewayActivityBehaviorExt) {
- this.exclusiveGatewayActivityBehaviorExt = exclusiveGatewayActivityBehaviorExt;
- }
- /**
- * 重写父类中的分支条件行为执行类
- */
- @Override
- public ExclusiveGatewayActivityBehavior createExclusiveGatewayActivityBehavior(ExclusiveGateway exclusiveGateway) {
- return exclusiveGatewayActivityBehaviorExt;
- }
- }
对网关的条件判断类,ExclusiveGatewayActivityBehaviorExt.java
- import java.util.List;
- import javax.annotation.Resource;
- import javax.script.ScriptException;
- import org.activiti.engine.ActivitiException;
- import org.activiti.engine.impl.Condition;
- import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
- import org.activiti.engine.impl.bpmn.parser.BpmnParse;
- import org.activiti.engine.impl.pvm.PvmTransition;
- import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
- import org.springframework.stereotype.Component;
- /**
- *
- * @author nuohy
- * @Description: 对网关的条件判断,优先使用扩展的配置
- * @date 2017年7月22日
- */
- @Component(value="exclusiveGatewayActivityBehaviorExt")
- @SuppressWarnings("serial")
- public class ExclusiveGatewayActivityBehaviorExt extends ExclusiveGatewayActivityBehavior{
- //解析脚本
- @Resource(name="conditionAnalysis")
- private ActivitiConditionAnalysis conditionAnalysis;
- @Override
- protected void leave(ActivityExecution execution) {
- PvmTransition outgoingSeqFlow = null;
- String defaultSequenceFlow = (String) execution.getActivity().getProperty("default");
- List<PvmTransition> list = execution.getActivity().getOutgoingTransitions();
- if(outgoingSeqFlow == null&&list!=null&&list.size()>0){
- for (PvmTransition seqFlow : list) {
- Condition condition = (Condition) seqFlow.getProperty(BpmnParse.PROPERTYNAME_CONDITION);
- try {
- //处理解析 表达式
- boolean evaluate = conditionAnalysis.analysis(execution.getVariables(),seqFlow, seqFlow.getId());
- if((condition == null && (defaultSequenceFlow == null || !defaultSequenceFlow.equals(seqFlow.getId()))) || evaluate){
- outgoingSeqFlow = seqFlow;
- }
- } catch (ScriptException e) {
- e.printStackTrace();
- }
- //源代码
- /*boolean evaluate = (condition != null && condition.evaluate(execution));
- if((condition == null && (defaultSequenceFlow == null || !defaultSequenceFlow.equals(seqFlow.getId()))) || evaluate){
- outgoingSeqFlow = seqFlow;
- }*/
- }
- }
- if (outgoingSeqFlow != null) {
- execution.take(outgoingSeqFlow);
- } else {
- if (defaultSequenceFlow != null) {
- PvmTransition defaultTransition = execution.getActivity().findOutgoingTransition(defaultSequenceFlow);
- if (defaultTransition != null) {
- execution.take(defaultTransition);
- } else {
- throw new ActivitiException("未发现默认流程线路'" + defaultSequenceFlow );
- }
- } else {
- //No sequence flow could be found, not even a default one
- throw new ActivitiException("没有找到网关出口 activiti.id:"+ execution.getActivity().getId() + " 流程不能继续进行!");
- }
- }
- }
- }
activiti 条件表达式 解析类,ActivitiConditionAnalysis.java (具体怎样解析,都在这里面写好实现,这里我用了ScriptEngine引擎)
- import java.util.Map;
- import javax.script.ScriptEngine;
- import javax.script.ScriptEngineManager;
- import javax.script.ScriptException;
- import org.activiti.engine.impl.pvm.PvmTransition;
- import org.springframework.stereotype.Component;
- /**
- *
- * @author nuohy
- * @Description: activiti 条件表达式 解析
- * @date 2017年7月22日
- */
- @Component(value = "conditionAnalysis")
- public class ActivitiConditionAnalysis {
- /**
- *
- * @param Variables - 流程变量
- * @param seqFlow - 分支条件
- * @param seqFlowId - flowid
- * @return
- * @throws ScriptException
- */
- public boolean analysis(Map<String, Object> Variables, PvmTransition seqFlow, String seqFlowId) throws ScriptException {
- String conditionText = (String) seqFlow.getProperty("conditionText");
- System.out.println("---------------进行一个替换---------------");
- //替换${}括号等无效字符
- conditionText = conditionText.replaceAll("\\$\\{", "").replaceAll("\\}", "");
- //替换变量数值
- if (Variables != null && Variables.size() > 0) {
- for (String key : Variables.keySet()) {
- conditionText = conditionText.replaceAll(key, Variables.get(key).toString());
- }
- }
- //开始boolean 计算
- ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
- ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("javascript");
- boolean result = (Boolean) scriptEngine.eval(conditionText);
- if (result) {
- System.out.println("true seqFlowId:" + seqFlowId + " 表达式为:" + conditionText);
- }else{
- System.out.println("false seqFlowId:" + seqFlowId + " 表达式为:" + conditionText);
- }
- return result;
- }
- }
最后需要在spring-activiti.xml中配置一下:
- <!-- spring负责创建流程引擎的配置文件 -->
- <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
- <!-- 数据源 -->
- <property name="dataSource" ref="myDataSource" />
- <!-- 配置事务管理器,统一事务 -->
- <property name="transactionManager" ref="transactionManager" />
- <!-- 设置建表策略,如果没有表,自动创建表 -->
- <property name="databaseSchemaUpdate" value="true" />
- <span style="color:#ff0000;"><!-- 用于更改流程节点的执行行为 -->
- <property name="activityBehaviorFactory" ref="activityBehaviorFactoryExt"/></span>
- <!-- 生成流程图的字体 解决图片中文不显示问题-->
- <property name="activityFontName" value="宋体"></property>
- <property name="labelFontName" value="宋体"></property>
- </bean>
比较匆忙,以后再补充吧!