activiti5.16 项目实战笔记
Activiti 项目实战笔记
项目中用到的所有jar 包和插件 我都整理出来放在
Activiti 实战工具包
(一) eclipse 配置activiti 开发环境
首先在eclipse中装activiti插件方便开发过程中进行流程设计
我这里主要写离线安装方法,网络方便的话可以选择在线安装。。百度一大堆,更简单。
1,拿到工具包中的 activiti插件安装.zip 解压。
2,将解压好的activiti+文件夹里的3个jar文件复制到eclipse安装目录的plugins目录下。
3,删除eclipse安装目录下,configuration文件夹里的org.eclipse.update文件夹,重启eclipse。
(二) ssm 整合activiti
1,首先在项目中导入activiti 的相关jar 包,libs 文件夹中的所有jar包拷贝到项目的jar包目录lib下,然后build path 。其他spring MVC相关的jar包我就不说了。
2,接下来就添加配置文件
新增配置文件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">
<import resource="classpath:applicationContext.xml"/>
<!--
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
//连接数据库的配置
processEngineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/itcast0711activiti?useUnicode=true&characterEncoding=utf8");
processEngineConfiguration.setJdbcUsername("root");
processEngineConfiguration.setJdbcPassword("root");
/**
public static final String DB_SCHEMA_UPDATE_FALSE = "false";不能自动创建表,需要表存在
public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop";先删除表再创建表
public static final String DB_SCHEMA_UPDATE_TRUE = "true";如果表不存在,自动创建表
*/
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
-->
<!-- <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/itcast0711activiti?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>
然后在applicationContext.xml配置中新增配置如下:
<!-- 配置activiti的数据源 -->
<bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/activiti?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!-- activiti事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource2" />
</bean>
<!-- 加载activiti引擎 -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource2" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
<property name="activityFontName" value="微软雅黑"/>
<property name="labelFontName" value="微软雅黑"/>
<property name="mailServerHost" value="localhost"/>
<property name="mailServerPort" value="5025"/>
</bean>
<!-- 创建activiti提供的各种服务 -->
<!-- 工作流仓储服务 -->
<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="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<!-- 表彰-->
<bean id="formService" factory-bean="processEngine" factory-method="getFormService" />
<!-- 工作流唯一服务 -->
<bean id="IdentityService" factory-bean="processEngine" factory-method="getIdentityService"/>
(三) 生成activiti 数据库
数据库的生成我是用代码生成,其实不用我下面的这段代码生成23张表也没事。因为我在配置文件中配置了
<property name="databaseSchemaUpdate" value="true" />
这个属性就是 如果表不存在,自动创建表
启动的时候如果数据源库中没有activiti库以及表 则自动会生成activiti库以及23张表
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/**使用代码创建工作流需要的23张表*/
@Test
public void createTable(){
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
//连接数据库的配置
processEngineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8");
processEngineConfiguration.setJdbcUsername("root");
processEngineConfiguration.setJdbcPassword("123456");
/**
public static final String DB_SCHEMA_UPDATE_FALSE = "false";不能自动创建表,需要表存在
public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop";先删除表再创建表
public static final String DB_SCHEMA_UPDATE_TRUE = "true";如果表不存在,自动创建表
*/
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
//工作流的核心对象,ProcessEnginee对象
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
System.out.println("processEngine:"+processEngine);
}
(四) 整合activiti modeler 到项目
由于我们项目要求流程这块要业务人员自定义流程(当时内心崩溃!!),所以初步设计打算用activiti modeler 编辑器作为用户的自定义流程编辑器(我是不信业务人员能用这个画出正确的流程图,当然后面我进行了一系列的改版和优化 终于改成能让一个*都能画出流程来着,主要是我前端太菜了 不然我自己写个编辑器)。
以上都是吐槽
下面我们把编辑器整合到我们项目中
activiti 官方的项目包是这个 Activiti-develop.zip 解压后放在tomcat下面 修改数据库配置文件(具体的修改方法附录在下面 见附录1)即可启动进入activiti后台系统 ,这个系统也是整合了activiti modeler 编辑器可以在线编辑部署流程, 不过也没啥用, 我们已经把activiti整合到我们自己的项目中了。现在的目的是把activiti modeler编辑器也整合到我们自己的项目中。
下图是官方给的activiti后台管理系统
下图是activiti modeler 编辑器图片
接下来正式把modeler编辑器整合到我们自己的系统中
1,首先在web.xml中添加rest相关
<servlet >
<servlet-name> ExplorerRestletServlet</servlet-name >
<servlet-class> org.restlet.ext.servlet.ServerServlet</servlet-class >
<init-param>
<!-- Application class name -->
<param-name> org.restlet.application</param-name >
<param-value> com.isprint.ssf.controller.ExplorerRestApplication</param-value >
</init-param>
</servlet >
<servlet >
<servlet-name> RestletServlet</servlet-name >
<servlet-class> org.restlet.ext.servlet.ServerServlet</servlet-class >
<init-param>
<!-- Application class name -->
<param-name> org.restlet.application</param-name >
<param-value> org.activiti.rest.service.application.ActivitiRestServicesApplication </param-value>
</init-param>
</servlet >
<!-- Catch all service requests -->
<servlet-mapping>
<servlet-name> RestletServlet</servlet-name >
<url-pattern> /rest/*</ url-pattern>
</servlet-mapping >
<servlet-mapping>
<servlet-name> ExplorerRestletServlet</servlet-name >
<url-pattern> /modeler/service/*</url-pattern >
</servlet-mapping>
2,然后拿到我给的资料包中的modeler文件夹,把文件夹中相关的文件放到项目对应的目录下
如下:
3,添加支持activiti modeler 正常运行的jar
在modeler 文件夹中的lib 下存放了支持modeler编辑器的jar包 ,导入项目。
4,做完123步骤后 就算是配置完成了 ,接下来是进入activiti modeler的接口
参照官方系统中进入编辑器的接口 service/editor?id=7505
接口 url 是 service/editor id 是必带参数 参数值为当前编辑流程的modelId
也就是act_re_model 表中的ID_字段
注:不管是新建流程进入编辑器还是编辑流程进入编辑器 都必须带id 这个参数,新建流程进入编辑器只不过是先调用生成流程的接口 生成 id 然后再进入编辑器(后面写接口的时候再细说)
接下来就是使用activiti modeler 画流程图了。具体怎么使用这个编辑器以后有时间再补充。
5,由于编辑器中语言是英文的,使用不方便 所以替换语言包 实现中文显示
在资料包中找到stencilset_ch.json 改成 stencilset.json 替换配置文件中的
原来自带的stencilset.json。重启服务器 在进入编辑器就是中文了。
附录1 在压缩包中见 activiti-standalone-context.xml
在官方系统中的目录为 activiti-explorer\WEB-INF\activiti-standalone-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd">
<!-- This Spring config file is NOT used in Alfresco, as the Activiti engine is wired in a different way there -->
<!--<bean id="dbProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties" />-->
<!-- Allow other PropertyPlaceholderConfigurer to run as well -->
<!-- <property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti?autoReconnect=true" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="enableDatabaseEventLogging" value="true" />
<property name="customFormTypes">
<list>
<bean class="org.activiti.explorer.form.UserFormType"/>
<bean class="org.activiti.explorer.form.ProcessDefinitionFormType"/>
<bean class="org.activiti.explorer.form.MonthFormType"/>
</list>
</property>
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean" destroy-method="destroy">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<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="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" />
<bean id="activitiLoginHandler" class="org.activiti.explorer.ui.login.DefaultLoginHandler">
<property name="identityService" ref="identityService" />
</bean>
<bean id="demoDataGenerator" class="org.activiti.explorer.demo.DemoDataGenerator" init-method="init">
<property name="processEngine" ref="processEngine" />
<!-- Set following properties to false if certain demo data is not wanted -->
<property name="createDemoUsersAndGroups" value="true" />
<property name="createDemoProcessDefinitions" value="true" />
<property name="createDemoModels" value="true" />
<property name="generateReportData" value="true" />
</bean>
</beans>
(五) activiti使用中的常用api
1,activiti 创建流程实例的方法,此方法生成流程实例得到流程id然后再编辑器打开,如果项目中有设计自己的工单表,那在这个方法中跳转编辑器之前添加相应的数据到自己的表中。
这里面用到的各种服务 如activitiService 等等 都是可以通过注解的方法引入使用
@Autowired(required = false)
ActivitiService activitiService;
@RequestMapping(value = "/create", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
@ResponseBody
public void create(
@RequestParam(value ="name") String name,
@RequestParam(value ="key") String key,
@RequestParam(value = "description", required = false) String description,
HttpServletRequest request, HttpServletResponse response) {
try {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode modelObjectNode = objectMapper.createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION,
org.apache.commons.lang3.StringUtils
.defaultString(description));
Model newModel = repositoryService.newModel();
newModel.setMetaInfo(modelObjectNode.toString());
newModel.setName(name);
newModel.setKey(key);
repositoryService.saveModel(newModel);
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);
repositoryService.addModelEditorSource(newModel.getId(), editorNode
.toString().getBytes("utf-8"));
response.sendRedirect(request.getContextPath()
+ "/service/editor?id=" + newModel.getId());
} catch (Exception e) {
e.getStackTrace();
}
}
2,部署流程的方法
部署流程的方法有很多,网上大多是通过eclipse设计流程保存后拿到流程的bpmn文件和流程图片文件,上传到activiti数据库进行部署(这种方法如果你们有需要我后面再补充)。。我下面写的是和这不同的另一种方案,主要是解决业务人员在activiti modeler编辑器中自定义流程后部署。
由于编辑器中编辑保存流程后 会在activiti库中存储 整个流程的二进制数据
所以说白了我们就是要拿到这些二进制数据进行流程部署
以下就是根据modelId进行部署的方法
流程可以多次部署 多次部署后 启动流程时会根据 activiti 流程部署表中(数据库相关的我后面详细讲解)的version启动最新版本的流程
//根据流程model id部署流程
@RequestMapping(value = "/deploy", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
@ResponseBody
public String deploy(@RequestParam("modelId") String modelId,HttpServletResponse response) {
try {
Model modelData = repositoryService.getModel(modelId);
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;
BpmnJsonConverter bjc= new BpmnJsonConverter();
BpmnModel model = bjc.convertToBpmnModel(modelNode);
bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes,"utf-8")).deploy();
System.out.println("部署成功,部署ID=" + deployment.getId());
logger.info("根据模型部署流程成功:modelId="+modelId);
//redirectAttributes.addFlashAttribute("message", "部署成功,部署ID=" + deployment.getId());
} catch (Exception e) {
logger.info("根据模型部署流程失败:modelId="+modelId, e);
}
return "";
}
3,启动流程的方法
启动流程比较简单 只要流程部署号以后 就可以直接启动
流程部署一次 可以启动多次 每次启动都生成一条工作流流程数据
如果有自己设计工单表 可以在方法中加入生成工单的逻辑
/**根据流程定义的id启动一个流程实例,这个流程定义的Id来自act_re_procdef
* 启动一次流程实例act_ru_execution表增加一条数据
*/
@RequestMapping(value = "/start", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
@ResponseBody
public void startProcessInstance(@RequestParam("processDefinitionId")String processDefinitionId) {
//获取流程定义ID
ProcessInstance processInstance =
processEngine.getRuntimeService().startProcessInstanceById
(processDefinitionId );
System.out.println(processInstance.getId());
//自动选择最新版本的流程定义启动流程实例建议使用key来启动
// String processDefinitionKey = "qjlc";
// ProcessInstance processInstance = processEngine.getRuntimeService()
// .startProcessInstanceByKey(processDefinitionKey);
// System.out.println(processInstance.getId());
}
4,删除流程模板
这个简单 直接用
/**
* 根据模板id删除模板
* @param modelId
* @return
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
@ResponseBody
public String delete(@RequestParam("modelId") String modelId) {
repositoryService.deleteModel(modelId);
return "";
}
5,自定义任务处理人
流程任务处理人我没有用activiti的处理人和组的表,我直接用项目中我们自己的用户表,流程中处理人设置为用户id。但是流程设计的时候设置assignee属性又不适合于我们这种需要业务人员自定义流程的场景,毕竟业务人员怎么能知道用户id这种概念呢。后来我决定把流程任务需要配置的属性全部拿出来放到自己的流程配置页面统一去配置。怎么拿出来又是难题。。。后来看数据库流程从编辑器保存后,流程信息存在数据库中act_ge_bytearray表,以xml的形式存储流程,so 用dome4j解析xml修改xml中的属性后更新当前流程的旧的xml即可。
页面设计如下
代码如下
/**
* 根据modelId 加载流程的配置信息,,先把流程配置转换成json展示到前端
*/
@RequestMapping(value = "/loadProcessInfo", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
@ResponseBody
public Map<String,Object> loadProcessInfo(@RequestParam("modelId") String modelId,HttpServletRequest request, HttpServletResponse response){
Map<String,Object> result=new HashMap<String, Object>();
SAXReader saxReader = new SAXReader();
List<Map<String,Object>> data=new ArrayList<Map<String,Object>>();
List<String> idlist=new ArrayList<String>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try{
InputStream in= export(modelId);
Document document = saxReader.read(in);
Element rootElement = document.getRootElement();
Iterator it = rootElement.elementIterator("process");
while (it.hasNext()){//遍历所有节点名为 process的节点 默认只有一个process
Element book = (Element)it.next();
Iterator cit = book.elementIterator("userTask");
while (cit.hasNext()){//遍历所有节点名为userTask的节点
Map<String,Object> map=new HashMap<String, Object>();
Element child = (Element) cit.next();
Attribute idbute = child.attribute("id");//环节id
Attribute formKeybute = child.attribute("formKey");//工单类型
Attribute namebute = child.attribute("name");//环节名
Attribute assigneebute = child.attribute("assignee");//处理人
Attribute dueDatebute = child.attribute("dueDate");//到期时间
idlist.add(idbute.getValue());
map.put("id",idbute.getValue());
if (namebute == null) {
map.put("name","");
} else {
map.put("name",namebute.getValue());
}
if (formKeybute == null) {
map.put("formKey","");
} else {
map.put("formKey",formKeybute.getValue());
}
if (assigneebute == null) {
map.put("assignee","");
} else {
map.put("assignee",assigneebute.getValue());
}
if (dueDatebute == null) {
map.put("dueDate","");
} else {
map.put("dueDate",sdf.parse(dueDatebute.getValue()));
}
data.add(map);
}
}
if(idlist.size() != new HashSet<String>(idlist).size()){//长度不一样说明id有重复
//如果id有重复存在则不给配置人员信息
result.put("msg", "流程中个环节的id存在重复!请修改流程id后再来配置流程属性。");
result.put("data", null);
}else{
result.put("data", data);
result.put("msg", null);
}
return result;
}catch (Exception e){
e.printStackTrace();
}
result.put("msg", "程序加载错误");
return result;
}
public InputStream export(String modelId) {
try {
Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
InputStream in = new ByteArrayInputStream(bpmnBytes);
return in;
} catch (Exception e) {
e.printStackTrace();
//logger.info("导出失败:modelId="+modelId, e);
}
return null;
}
/**
* 前端拼接数据传到后端后更新旧的xml
*/
@RequestMapping(value = "/updateProcessInfo")
@ResponseBody
public String updateProcessInfo(HttpServletRequest request,
HttpServletResponse response) {
String id=request.getParameter("id");
String modelId=request.getParameter("modelId");
String data1=request.getParameter("data1");
SAXReader saxReader = new SAXReader();
try {
InputStream in = export(modelId);
JSONArray json = JSONArray.fromObject(data1);
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
Document document = saxReader.read(in);
Element rootElement = document.getRootElement();
Iterator it = rootElement.elementIterator("process");
while (it.hasNext()) {// 遍历所有节点名为 process的节点 默认只有一个process
Element book = (Element) it.next();
Iterator cit = book.elementIterator("userTask");
while (cit.hasNext()) {// 遍历所有节点名为userTask的节点
Element child = (Element) cit.next();
Attribute idbute = child.attribute("id");
Attribute formKeybute = child.attribute("formKey");
Attribute namebute = child.attribute("name");
Attribute assigneebute = child.attribute("assignee");
Attribute dueDatebute = child.attribute("dueDate");
for(int i=0;i<json.size();i++) {
JSONObject json1=json.getJSONObject(i);
if (json1.getString("id").equals(idbute.getValue())) {// 如果数据map中id的值和流程任务的id一样说明当前map和当前task匹配
String formKey = json1.getString("formKey");
String name = json1.getString("name");
String assignee = json1.getString("assignee");
String dueDate = json1.getString("dueDate");
if(formKey!=null&&!"".equals(formKey)){
name=proWorkTypeService.queryWorkTypeName(Integer.parseInt(formKey));
}
if (namebute == null) {
child.addAttribute("name", name);
} else {
namebute.setValue(name);
}
if (formKeybute == null) {
child.addAttribute("activiti:formKey", formKey);
} else {
formKeybute.setValue(formKey);
}
if (assigneebute == null) {
child.addAttribute("activiti:assignee", assignee);
} else {
assigneebute.setValue(assignee);
}
if (dueDatebute == null) {
child.addAttribute("activiti:dueDate", dueDate);
} else {
dueDatebute.setValue(dueDate);
}
}
}
}
}
byte[] edit = document.asXML().getBytes("utf-8");
// 改完之后 重新把新的xml保存到流程信息
InputStream Input = new ByteArrayInputStream(edit);
byte[] bpmnjson = xmlFormatToJson(Input);
repositoryService.addModelEditorSource(modelId, bpmnjson);
//修改任务的配置状态,,如果id==null 说明是配置模板成员不用修改任务状态
if(id!=null&&!"".equals(id)){
Map<String,Object> param=new HashMap<String, Object>();
param.put("isDeploy", "1");
param.put("id", id);
int flag=proWorkTypeService.updateTaskIsDeploy(param);
}
return "0";
} catch (Exception e) {
e.printStackTrace();
return "1";
}
}
/**
* 把xml格式转换成json格式
* @param input
* @return
*/
public byte[] xmlFormatToJson(InputStream input){
byte[] bpmnjson = null;
try {
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
XMLStreamReader xml=xmlFactory.createXMLStreamReader(input);
BpmnModel bpmm= xmlConverter.convertToBpmnModel(xml);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
ObjectNode objn=jsonConverter.convertToJson(bpmm);
bpmnjson=objn.toString().getBytes("utf-8");
return bpmnjson;
} catch (Exception e) {
e.printStackTrace();
}
return bpmnjson;
}
前端根据用户对各个属性的选择组装成json传到后端,格式如下
data1: [{"name":"需求分析","id":"link1","formKey":"0","assignee":"151","dueDate":"2018-06-13"},{"name":"审核","id":"shenhe","formKey":"1001","assignee":"151","dueDate":"2018-06-14"}]
6,展示流程图以及动态的带高亮红线的流程图
/**
* 读取带跟踪的图片 动态流程图
*/
//@Auth(verifyLogin=false)
@RequestMapping(value = "/getpng", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
// @ResponseBody
public void getpng(@RequestParam("processInstanceId") String processInstanceId,HttpServletResponse response) throws Exception {
//获取历史流程实例
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
processEngineConfiguration = processEngine.getProcessEngineConfiguration();
Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
//高亮环节id集合
List<String> highLightedActivitis = new ArrayList<String>();
//高亮线路id集合
List<String> highLightedFlows = getHighLightedFlows(definitionEntity,highLightedActivitList);
for(HistoricActivityInstance tempActivity : highLightedActivitList){
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
//中文显示的是口口口,设置字体就好了
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,highLightedFlows,"宋体","宋体",null,1.0);
//单独返回流程图,不高亮显示
// InputStream imageStream = diagramGenerator.generatePngDiagram(bpmnModel);
// 输出资源内容到相应对象
byte[] b = new byte[1024];
int len;
while ((len = imageStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
/**
* 获取需要高亮的线
* @param processDefinitionEntity
* @param historicActivityInstances
* @return
*/
public List<String> getHighLightedFlows( ProcessDefinitionEntity processDefinitionEntity,List<HistoricActivityInstance> historicActivityInstances){
List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
for (int i = 0; i < historicActivityInstances.size() - 1; i++) {// 对历史流程节点进行遍历
ActivityImpl activityImpl = processDefinitionEntity
.findActivity(historicActivityInstances.get(i)
.getActivityId());// 得到节点定义的详细信息
List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需开始时间相同的节点
ActivityImpl sameActivityImpl1 = processDefinitionEntity
.findActivity(historicActivityInstances.get(i + 1)
.getActivityId());
// 将后面第一个节点放在时间相同节点的集合里
sameStartTimeNodes.add(sameActivityImpl1);
for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
HistoricActivityInstance activityImpl1 = historicActivityInstances
.get(j);// 后续第一个节点
HistoricActivityInstance activityImpl2 = historicActivityInstances
.get(j + 1);// 后续第二个节点
if (activityImpl1.getStartTime().equals(
activityImpl2.getStartTime())) {
// 如果第一个节点和第二个节点开始时间相同保存
ActivityImpl sameActivityImpl2 = processDefinitionEntity
.findActivity(activityImpl2.getActivityId());
sameStartTimeNodes.add(sameActivityImpl2);
} else {
// 有不相同跳出循环
break;
}
}
List<PvmTransition> pvmTransitions = activityImpl
.getOutgoingTransitions();// 取出节点的所有出去的线
for (PvmTransition pvmTransition : pvmTransitions) {
// 对所有的线进行遍历
ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition
.getDestination();
// 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示
if (sameStartTimeNodes.contains(pvmActivityImpl)) {
highFlows.add(pvmTransition.getId());
}
}
}
return highFlows;
}
/**
* 导出model的图片,静态流程图
*/
//@Auth(verifyLogin=false)
@RequestMapping(value = "/exportPNG", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
//@ResponseBody
public void exportPNG(@RequestParam("modelId") String modelId, HttpServletResponse response) {
try {
Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel,
"png",
Collections.<String>emptyList(), Collections.<String>emptyList(),
"WenQuanYi Micro Hei", "WenQuanYi Micro Hei",
null, 1.0);
OutputStream out = response.getOutputStream();
for (int b = -1; (b = inputStream.read()) != -1; ) {
out.write(b);
}
out.close();
inputStream.close();
} catch (Exception e) {
logger.info("导出失败:modelId="+modelId, e);
}
}
7,导出流程xml
/**
* 导出model的xml文件
*/
@RequestMapping(value = "/exportXML", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
@ResponseBody
public void export(@RequestParam("modelId") String modelId, HttpServletResponse response) {
try {
Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
IOUtils.copy(in, response.getOutputStream());
String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
response.flushBuffer();
} catch (Exception e) {
logger.info("导出失败:modelId="+modelId, e);
}
}
7,流程回退到指定环节
public void rollBackToAssignWoekFlow(String processInstanceId, String destTaskkey) {
// 取得当前任务.当前任务节点
processInstanceId="process:51:277509";
destTaskkey = "xuqiufenxi";
// HistoricTaskInstance currTask =
// historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
Map<String, Object> variables;
ExecutionEntity entity = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(processInstanceId)
.singleResult();
ProcessDefinitionEntity definition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(entity.getProcessDefinitionId());
variables = entity.getProcessVariables();
// 当前活动环节
ActivityImpl currActivityImpl = definition.findActivity(entity.getActivityId());
// 目标活动节点
ActivityImpl nextActivityImpl = ((ProcessDefinitionImpl) definition).findActivity(destTaskkey);
if (currActivityImpl != null) {
// 所有的出口集合
List<PvmTransition> pvmTransitions = currActivityImpl.getOutgoingTransitions();
List<PvmTransition> oriPvmTransitions = new ArrayList<PvmTransition>();
for (PvmTransition transition : pvmTransitions) {
oriPvmTransitions.add(transition);
}
// 清除所有出口
pvmTransitions.clear();
// 建立新的出口
List<TransitionImpl> transitionImpls = new ArrayList<TransitionImpl>();
TransitionImpl tImpl = currActivityImpl.createOutgoingTransition();
tImpl.setDestination(nextActivityImpl);
transitionImpls.add(tImpl);
List<Task> list = taskService.createTaskQuery().processInstanceId(entity.getProcessInstanceId())
.taskDefinitionKey(entity.getActivityId()).list();
for (Task task : list) {
taskService.complete(task.getId(), variables);
historyService.deleteHistoricTaskInstance(task.getId());
}
for (TransitionImpl transitionImpl : transitionImpls) {
currActivityImpl.getOutgoingTransitions().remove(transitionImpl);
}
for (PvmTransition pvmTransition : oriPvmTransitions) {
pvmTransitions.add(pvmTransition);
}
}
}
8,传统的部署流程定义
/**部署流程定义*/
@Test
public void deploymentProcessDefinition(){
Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
.createDeployment()//创建一个部署对象
.name("helloworld入门程序")//添加部署的名称
.addClasspathResource("diagram/task.bpmn")//从classpath的资源中加载,一次只能加载一个文件
.addClasspathResource("diagram/task.png")//从classpath的资源中加载,一次只能加载一个文件
.deploy();//完成部署
System.out.println("部署ID:"+deployment.getId());//1
System.out.println("部署名称:"+deployment.getName());//helloworld入门程序
}
9,启动流程实例
/**启动流程实例*/
@Test
public void startProcessInstance(){
//流程定义的key
String processDefinitionKey = "task";
ProcessInstance pi = processEngine.getRuntimeService()//与正在执行的流程实例和执行对象相关的Service
.startProcessInstanceByKey(processDefinitionKey);//使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动
System.out.println("流程实例ID:"+pi.getId());//流程实例ID 101
System.out.println("流程定义ID:"+pi.getProcessDefinitionId());//流程定义ID helloworld:1:4
}
10,查询当前人的个人任务
/**查询当前人的个人任务*/
@Test
public void findMyPersonalTask(){
String assignee = "kermit";
String processDefinitionId="process555:5:58781";
List<Task> list = processEngine.getTaskService()//与正在执行的任务管理相关的Service
.createTaskQuery()//创建任务查询对象
/**查询条件(where部分)*/
.taskAssignee(assignee)//指定个人任务查询,指定办理人
// .taskCandidateUser("fozzie")//组任务的办理人查询
.processDefinitionId(processDefinitionId)//使用流程定义ID查询
// .processInstanceId(processInstanceId)//使用流程实例ID查询
// .executionId(executionId)//使用执行对象ID查询
/**排序*/
.orderByTaskCreateTime().asc()//使用创建时间的升序排列
/**返回结果集*/
// .singleResult()//返回惟一结果集
// .count()//返回结果集的数量
// .listPage(firstResult, maxResults);//分页查询
.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("任务key:"+task.getTaskDefinitionKey());
System.out.println("########################################################");
}
}
}
11,提交工单 完成我的任务
/**完成我的任务*/
@Test
public void completeMyPersonalTask(){
//任务ID
String taskId = "58786";
processEngine.getTaskService()//与正在执行的任务管理相关的Service
.complete(taskId);
System.out.println("完成任务:任务ID:"+taskId);
}
12,查询流程状态
/**查询流程状态(判断流程正在执行,还是结束)*/
@Test
public void isProcessEnd(){
String processInstanceId = "17529";
ProcessInstance pi = processEngine.getRuntimeService()//表示正在执行的流程实例和执行对象
.createProcessInstanceQuery()//创建流程实例查询
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
if(pi==null){
System.out.println("流程已经结束");
}
else{
System.out.println("流程没有结束");
}
}
13,查询历史任务
@Test
public void findHistoryTask(){
String taskAssignee = "gonzo";
List<HistoricTaskInstance> list = processEngine.getHistoryService()//与历史数据(历史表)相关的Service
.createHistoricTaskInstanceQuery()//创建历史任务实例查询
.taskAssignee(taskAssignee)//指定历史任务的办理人
.list();
if(list!=null && list.size()>0){
for(HistoricTaskInstance hti:list){
System.out.println(hti.getId()+" "+hti.getName()+" "+hti.getProcessInstanceId()+" "+hti.getStartTime()+" "+hti.getEndTime()+" "+hti.getDurationInMillis());
System.out.println("################################");
}
}
}
14,查询流程定义
@Test
public void findProcessDefinition(){
List<ProcessDefinition> list = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
.createProcessDefinitionQuery()//创建一个流程定义查询
/*指定查询条件,where条件*/
//.deploymentId(deploymentId)//使用部署对象ID查询
//.processDefinitionId(processDefinitionId)//使用流程定义ID查询
//.processDefinitionKey(processDefinitionKey)//使用流程定义的KEY查询
//.processDefinitionNameLike(processDefinitionNameLike)//使用流程定义的名称模糊查询
/*排序*/
.orderByProcessDefinitionVersion().asc()//按照版本的升序排列
//.orderByProcessDefinitionName().desc()//按照流程定义的名称降序排列
.list();//返回一个集合列表,封装流程定义
//.singleResult();//返回唯一结果集
//.count();//返回结果集数量
//.listPage(firstResult, maxResults)//分页查询
if(list != null && list.size()>0){
for(ProcessDefinition processDefinition:list){
System.out.println("流程定义ID:"+processDefinition.getId());//流程定义的key+版本+随机生成数
System.out.println("流程定义名称:"+processDefinition.getName());//对应HelloWorld.bpmn文件中的name属性值
System.out.println("流程定义的key:"+processDefinition.getKey());//对应HelloWorld.bpmn文件中的id属性值
System.out.println("流程定义的版本:"+processDefinition.getVersion());//当流程定义的key值相同的情况下,版本升级,默认从1开始
System.out.println("资源名称bpmn文件:"+processDefinition.getResourceName());
System.out.println("资源名称png文件:"+processDefinition.getDiagramResourceName());
System.out.println("部署对象ID:"+processDefinition.getDeploymentId());
System.out.println("################################");
}
}
}
15,查询历史活动
@Test
public void findHistoryActiviti(){
String processInstanceId = "110008";
String processDefinitionId = "processjiaccc:1:110007";
List<HistoricActivityInstance> list = processEngine.getHistoryService()//
.createHistoricActivityInstanceQuery()//创建历史活动实例的查询
.processDefinitionId(processDefinitionId)
//.processInstanceId(processInstanceId)//
.orderByHistoricActivityInstanceStartTime().asc()//
.list();
if(list!=null && list.size()>0){
for(HistoricActivityInstance hai:list){
System.out.println(hai.getId()+" "+hai.getProcessInstanceId()+" "+hai.getActivityType()+" "+hai.getStartTime()+" "+hai.getEndTime()+" "+hai.getDurationInMillis());
System.out.println("#####################");
}
}
}
15 查询历史
@Test
public void findHistoryProcessInstance(){
String processInstanceId = "110008";
HistoricProcessInstance hpi = processEngine.getHistoryService()//与历史数据(历史表)相关的Service
.createHistoricProcessInstanceQuery()//创建历史流程实例查询
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
System.out.println(hpi.getId()+" "+hpi.getProcessDefinitionId()+" "+hpi.getStartTime()+" "+hpi.getEndTime()+" "+hpi.getDurationInMillis());
}
@Test
public void queryTaskExecute(){
List<Task> tlist1 = processEngine.getTaskService()//与正在执行的任务管理相关的Service
.createTaskQuery()//创建任务查询对象
/**查询条件(where部分)*/
//.taskId("112504")
.processDefinitionId("cccccc:16:205036")//使用流程定义ID查询
.orderByTaskCreateTime().asc()//使用创建时间的升序排列
.list();//返回列表
for(Task task:tlist1){
System.out.println(task.getAssignee());
System.out.println(task.getCreateTime());
System.out.println();
}
}
@Test
public void queryTaskUser(){
List<IdentityLink> tlist1 = processEngine.getTaskService()//与正在执行的任务管理相关的Service
.getIdentityLinksForTask("205040");
for(IdentityLink task:tlist1){
System.out.println(task.getUserId());
System.out.println(task.getTaskId());
}
}
@Test
public void getWorkExamineState(){
//先通过当前任务id获取task对象
List<Task> tlist = processEngine.getTaskService()//与正在执行的任务管理相关的Service
.createTaskQuery()//创建任务查询对象
/**查询条件(where部分)*/
.taskId("112504")//使用task_id查询
//.processDefinitionId("processjiaccc:1:110007")//使用流程定义ID查询
.orderByTaskCreateTime().asc()//使用创建时间的升序排列
.list();//返回列表
//因为task_id是唯一的,所以一定只有一条数据。获取当前任务对象
Task task=tlist.get(0);
//根据process_definition_id查询当前流程的历史task数据
List<HistoricActivityInstance> list = processEngine.getHistoryService()//
.createHistoricActivityInstanceQuery()//创建历史活动实例的查询
.processDefinitionId("processjiaccc:1:110007")//根据流程部署定义id查询
//.processInstanceId(processInstanceId)//根据流程启动实例id查询
.orderByHistoricActivityInstanceStartTime().asc()//
.list();
if (list != null && list.size() > 0) {
for (HistoricActivityInstance hai : list) {
//如果历史任务中任务的结束时间为当前任务的创建时间,则此任务为当前任务的上一个环节
if(task.getCreateTime().equals( hai.getEndTime())){
System.out.println(hai.getId() + " " + hai.getProcessInstanceId() + " " + hai.getActivityType()
+ " " + hai.getStartTime() + " " + hai.getEndTime() + " " + hai.getDurationInMillis());
System.out.println("#####################");
}
}
}
}
/**查询历史活动
* 问题:HistoricActivityInstance对应哪个表
* 问题:HistoricActivityInstance和HistoricTaskInstance有什么区别*/
@Test
public void findHisActivitiList(){
String processInstanceId = "87508";
List<HistoricActivityInstance> list = processEngine.getHistoryService()
.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.list();
if(list != null && list.size()>0){
for(HistoricActivityInstance hai : list){
System.out.println(hai.getId()+" "+hai.getActivityName()+" "+hai.getStartTime().toString()
+" "+hai.getEndTime().toString()+" "+hai.getTaskId()+" "+hai.getAssignee());
}
}
}
/**查询历史任务
* 问题:HistoricTaskInstance对应哪个表*/
@Test
public void findHisTaskList(){
String processInstanceId = "1801";
List<HistoricTaskInstance> list = processEngine.getHistoryService()
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.list();
if(list!=null && list.size()>0){
for(HistoricTaskInstance hti:list){
System.out.println(hti.getId()+" "+hti.getName()+" "+hti.getClaimTime());
}
}
}
/**查询历史流程变量*/
@Test
public void findHisVariablesList(){
String processInstanceId = "1801";
List<HistoricVariableInstance> list = processEngine.getHistoryService()
.createHistoricVariableInstanceQuery()
.processInstanceId(processInstanceId)
.list();
if(list != null && list.size()>0){
for(HistoricVariableInstance hvi:list){
System.out.println(hvi.getId()+" "+hvi.getVariableName()+" "+hvi.getValue());
}
}
}
(六) activiti 数据库各表和字段详解
所有23张表如下 ,下面我对比较关键的几张表做一些说明
1,act_re_deployment 部署信息表
2, act_re_model 流程设计模型部署表
3,act_re_procdef 流程定义数据表
4, act_ru_execution运行时流程执行实例表
5,act_ru_identitylink运行时流程人员表,主要存储任务节点与参与者的相关信息
6,act_ru_task运行时任务节点表
流程启动后会插入启动后开始下一环节的任务数据,后面任务提交后会删除原来的数据再插入新的下一环节的任务数据,如果是分支并行结构则有几个分支产生几条数据。
ID_ 任务主键id
EXECUTION_ID_ 执行id 关联act_ru_execution 表中的 ID_
PROC_INST_ID_ 流程启动实例Id
PROC_DEF_ID_ 流程部署定义Id
ASSIGNEE_ 流程属性 处理人
FORM_KEY_ 流程属性 表单key
DUE_DATE_ 流程属性 结束时间
7,act_ru_variable运行时流程变量数据表
8,act_hi_actinst 历史节点表
9,act_hi_attachment历史附件表
10, act_hi_comment历史意见表
11, act_hi_identitylink历史流程人员表
12,act_hi_detail历史详情表,提供历史变量的查询
13,act_hi_procinst历史流程实例表
14,act_hi_taskinst历史任务实例表
15,act_hi_varinst历史变量表
16,act_id_group用户组信息表
17,act_id_info用户扩展信息表
18,act_id_membership用户与用户组对应信息表
19,act_id_user用户信息表
20,act_ge_bytearray二进制数据表
21,act_ge_property属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录
下一篇: 基于protues的51单片机交通灯仿真