在线办公系统之三 整合Activiti框架
- 工作流框架是什么?
- Activiti又是什么
- 工作流的个人理解
- Activiti 的配置
- Eclipse 中的画图插件
- 总结
工作流框架是什么?
工作流框架则是为了解决业务流程诞生的。对于同一件事件,从起始到结束中间会经历非常多的状态甚至事件回退等操作。通过业务代码的方式实现该套逻辑较为复杂,且不可复用。而工作流框架是针对此种情况(购物流程、请假流程等等)提取出来的通用解决方案,让开发省去事件流转状态的操作
现在绝大部分的工作流引擎都是根据2011年发布的BPMN2.0规范实现,BPMN2.0统一了业务流程图的标准,让各种工作流引擎的流程设计器可以通用。
Activiti又是什么
Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。
创始人Tom Baeyens是JBoss jBPM的项目架构师,以及另一位架构师Joram Barrez,一起加入到创建Alfresco这项首次实现Apache开源许可的BPMN 2.0引擎开发中来。
Activiti是一个独立运作和经营的开源项目品牌,并将独立于Alfresco开源ECM系统运行。 Activiti将是一种轻量级,可嵌入的BPM引擎,而且还设计适用于可扩展的云架构。 Activiti将提供宽松的Apache许可2.0,以便这个项目可以广泛被使用,同时促进Activiti BPM引擎和BPMN 2.0的匹配,该项目现正由OMG通过标准审定。 加入Alfresco Activiti项目的是VMware的SpringSource分支,Alfresco的计划把该项目提交给Apache基础架构,希望吸引更多方面的BPM专家和促进BPM的创新。
工作流的个人理解
可能结合上述两个点,还没怎么明白Activiti工作流的作用吧,其实我最大的体会就在于流程理解,还在学习的同学可能会不清楚,但是工作中的同志们肯定是深有体会的,
例如,你出差了,自己先垫付一部分费用,需要报销,这时候财务就会一脸冷漠的跟你说:“在系统上走个报销流程”
再比如,某某地举办动漫展,不想工作了,去参展,你得请假,然后人事给你来了一句:“系统上走个请假流程吧~!”
其实工作流的最大特点是,我们的申请,需要审批,它将对应的每个工作节点具象化,让对应工作中需要参与的人员都聚到一个流程图上,通过流程图,就能找到相应的审批人,进行批复,到达流程终点,你就能得到相应的工作批示回复。
这是个人项目后的拙见,当然,如果我们不使用工作流,可不可以?当然可以,在如今的社会,有一个软件,已经可以做到外插式的工作流软件,同时可以大量降低公司在这部分的开销,那就是钉钉。
那这个项目嵌入工作流到底有什么意义啊?
既然我们都可以用钉钉代替了,那我们反过来想,如果企业比较大,它会不会考虑用外插式的工作流软件呢?公司员工信息容易外漏。所以,钉钉市场体量的确越来越大,那是相对于企业量级来说的,当然,对于程序员来说,其实这是为了更好的了解业务,当你的练手项目融入Activiti之后,不停地练习,你会对CURD(增删改查)有一个新的体会。
Activiti 的配置
我们首先要抓住大纲:
工作流都得以你系统上有它实例化之后的数据库为准。
@Test
public void testJDBC(){
ProcessEngineConfiguration config=ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
String jdbcUrl="jdbc:mysql://localhost:3306/activitidb?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8";
String jdbcDriver="com.mysql.jdbc.Driver";
String jdbcUsername="root";
String jdbcPassword="root";
config.setJdbcUrl(jdbcUrl);
config.setJdbcDriver(jdbcDriver);
config.setJdbcUsername(jdbcUsername);
config.setJdbcPassword(jdbcPassword);
//默认
config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
ProcessEngine engine=config.buildProcessEngine();
System.out.println(engine);
}
一个工作流如何形成规范,其实整个项目下来之后,我的体会就可以变成一条线:
部署流程---->任务实例(发起)---->任务推进---->任务完成—>查看历史等操作。
工作流首先要简历对应的数据库表格,每一块都有具体的作用,具体可以自行搜索,这里不做赘述。
那我们在嵌入框架的时候,还是那句话,不要死记硬背,要知道工作流为我们解决什么问题,怎么解决!
工作流既然有自己的独特的数据库配置,那么它也必须创建数据库表和连接数据库咯~! 根据上述TEST代码,我们可以将对应的配置按照如下文件进行配置
- activiti.cfg.xml 数据库创建以及连接配置
<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">
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 连接数据的配置 -->
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/activitidb?useUnicode=true&characterEncoding=utf8"></property>
<property name="jdbcUsername" value="root"></property>
<property name="jdbcPassword" value="root"></property>
<!-- 没有表创建表 -->
<property name="databaseSchemaUpdate" value="true"></property>
</bean>
</beans>
那连接完毕之后,我们对应把数据库不同的表,划分一下区域
以下是个人理解
例如流程部署----->其实就是你发布一个怎样的流程,例如报销流程,请假流程,当用户需要走流程的时候,就按照你这个大纲来走
<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" />
那么这些Service是从哪里来的呢?这就涉及我们的框架其中一个核心,就是流程引擎,我们在SSM中,可以按照如下配置,然后让对应的Service进行实例化,当我们要进行怎样的操作,就可以用这些封装好的业务进行操作。
<?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="dataSource" />
<!-- 配置事务管理器,统一事务 -->
<property name="transactionManager" ref="transManager" />
<!-- 设置建表策略,如果没有表,自动创建表 -->
<property name="databaseSchemaUpdate" value="true" />
</bean>
<!-- 创建流程引擎对象 -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<!-- 由流程引擎对象,提供的方法,创建项目中使用的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>
Eclipse 中的画图插件
这里的画图插件上网查询Eclipse的Activiti插件就可以,对了,对应IDEA也有,但是如果真的使用起来,强烈推荐Eclipse,IDEA有很多BUG,亲测痛苦。
当然了,工作流的其中最大的优点在于他是将对应的工作节点,通过图进行节点明确,其中每个节点都可以放置监听类,表名对应的审批人员是谁,这样能够让整体程序更灵活。
package com.activiti.ssm.utils;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.apache.shiro.SecurityUtils;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import com.activiti.ssm.pojo.ActiveUser;
import com.activiti.ssm.pojo.Employee;
import com.activiti.ssm.service.EmployeeService;
public class TaskAssignHandler implements TaskListener {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateTask task) {
// 查询当前任务代办人的上级,通过硬编码获取employeeService
// 通过SPRING容器,获取对应的SERVICE实例
WebApplicationContext webApplicationContext= ContextLoader.getCurrentWebApplicationContext();
EmployeeService employeeService= (EmployeeService) webApplicationContext.getBean("employeeService");
// 通过对应的Subject获取ActivitiUser,其中已经封装好了对应获取上级ID方法
// 获取上级
ActiveUser user=(ActiveUser) SecurityUtils.getSubject().getPrincipal();
Employee Man=employeeService.findManagerByMid(user.getManagerId());
task.setAssignee(Man.getName());
}
}
还有更为复杂的流程,多分支结构,但是如何走向和控制呢?其实在每个流程走向添加流程变量就可以了,会按照实际传入的对象进行判断走向。
@Override
public void submitTask(Long id, String outcome, String taskId, String comment, String name) {
// 历史评论表进行添加批注
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
// 获取流程实例ID
String processInstanceId = task.getProcessInstanceId();
// ACTIVI底层-->认证--->CommentEntity comment=new Comment();
// comment.setuserID=xxx
// session中的emp有对应的ID
Authentication.setAuthenticatedUserId(name);
taskService.addComment(taskId, processInstanceId, comment);
// 完成任务
/**
* 2:如果连线的名称是“默认提交”,那么就不需要设置,如果不是,就需要设置流程变量
* 在完成任务之前,设置流程变量,按照连线的名称,去完成任务
* 流程变量的名称:outcome
* 流程变量的值:连线的名称
*/
Map<String, Object> variables = new HashMap<String, Object>();
if (outcome != null && !outcome.equals("默认提交")) {
variables.put("message", outcome);
// 3:使用任务ID,完成当前人的个人任务,同时流程变量
taskService.complete(taskId, variables);
} else {
taskService.complete(taskId);
}
// 获取流程实例 并且判断是否完成整个流程实例
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
.singleResult();
if (instance == null) {
Expensebill expensebill = expensebillMapper.selectByPrimaryKey(Integer.parseInt(id + ""));
// 将leavebill的状态修改为2,表示已经完成了
expensebill.setState(2);
expensebillMapper.updateByPrimaryKeySelective(expensebill);
}
}
其实整个项目下来,工作流在对应节点的处理方法千篇一律,但是最主要的还是确认好流程图,到了什么节点,需要做什么操作,这点比较关键。
总结
创建并数据库、流程引擎配置、画图、每个任务节点安放对应的审批人、流程线配置流程参数、对应流程引擎处理的方法。