工作流直译就是工作的流程,但是怎样设计与实现并应用到程序中还是有一些难度,不然为什么有些公司可以靠工作流起家呢?其中大量的逻辑充斥其中,稍不注意就会出错。
本次在Eclipse中使用了Activity插件,方便使用。
1.首先引入工作流相关的pom包,由于其中会有冲突,我将所有的都贴出来:
<properties>
<c3p0.version>0.9.1.2</c3p0.version>
<!-- spring版本号 -->
<spring.version>4.1.9.RELEASE</spring.version>
<!-- mybatis版本号 -->
<mybatis.version>3.2.6</mybatis.version>
<!-- log4j日志文件管理包版本 -->
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>1.2.17</log4j.version>
<cxf.version>2.2.3</cxf.version>
<activti.engine.version>6.0.0</activti.engine.version>
</properties>
<dependencies>
<!-- c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<!-- 表示开发的时候引入,发布的时候不会加载此包 -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
<exclusions>
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring mvc aop -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!-- mybatis/spring包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 导入java ee jar 包 -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<!-- 导入Mysql数据库链接jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<!-- JSTL标签类 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 日志文件管理包 -->
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 格式化对象,方便输出日志 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.41</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<!-- 映入JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- 上传组件包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.4.3</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- webservice -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- 工作流 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activti.engine.version}</version>
<exclusions>
<exclusion>
<groupId>de.odysseus.juel</groupId>
<artifactId>juel-spi</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activti.engine.version}</version>
</dependency>
</dependencies>
2. 写入相关的jdbc.properties和spring-mvc-activiti.xml
#this properties -- spring-mybatis.xml&config.xml ,so some properties are distinct.
mysql.driverclass=com.mysql.jdbc.Driver
mysql.jdbcurl=jdbc:mysql://localhost:3306/repairsystem?characterEncoding=utf-8
mysql.user=root
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/repairsystem?useUnicode=true&characterEncoding=utf-8
mysql.username=root
mysql.password=123456
mysql.initialSize=0
mysql.maxActive=20
mysql.maxIdle=20
mysql.minIdle=1
mysql.maxWait=60000
jdbc.activiti.driverClass=com.mysql.jdbc.Driver
jdbc.activiti.jdbcUrl=jdbc:mysql://localhost:3306/activitidb?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
jdbc.activiti.user=root
jdbc.activiti.password=123456
# basic confog
jdbc.minPoolSize=10
jdbc.maxPoolSize=10
jdbc.initialPoolSize=5
jdbc.acquireIncrement=5
# manage connection config
jdbc.maxIdleTime=1800
jdbc.maxConnectionAge=7200
jdbc.maxIdleTimeExcessConnections=0
# connection test config, timeunit second
jdbc.idleConnectionTestPeriod=600
jdbc.testConnectionOnCheckin=true
jdbc.testConnectionOnCheckout=false
jdbc.preferredTestQuery=SELECT 1 FROM DUAL
# recovery from database outages config, timeunit millisecond
jdbc.acquireRetryAttempts=5
jdbc.acquireRetryDelay=1000
jdbc.breakAfterAcquireFailure=false
# statement pool config TODO
jdbc.maxStatements=0
jdbc.maxStatementsPerConnection=0
jdbc.statementCacheNumDeferredCloseThreads=0
# connection leak config
jdbc.debugUnreturnedConnectionStackTraces=false
jdbc.unreturnedConnectionTimeout=0
# other config
jdbc.checkoutTimeout=10000
jdbc.numHelperThreads=3
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:property-placeholder location="classpath*:jdbc.properties" ignore-unresolvable="false" />
<bean id="transactionManager_activiti" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource_activiti"></property>
</bean>
<!-- activiti datasource -->
<bean id="dataSource_activiti" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.activiti.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.activiti.jdbcUrl}"/>
<property name="user" value="${jdbc.activiti.user}"/>
<property name="password" value="${jdbc.activiti.password}"/>
<property name="minPoolSize" value="${jdbc.minPoolSize}"/>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
<property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>
<property name="maxIdleTime" value="${jdbc.maxIdleTime}"/>
<property name="maxConnectionAge" value="${jdbc.maxConnectionAge}"/>
<property name="maxIdleTimeExcessConnections" value="${jdbc.maxIdleTimeExcessConnections}"/>
<property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"/>
<property name="testConnectionOnCheckin" value="${jdbc.testConnectionOnCheckin}"/>
<property name="testConnectionOnCheckout" value="${jdbc.testConnectionOnCheckout}"/>
<property name="preferredTestQuery" value="${jdbc.preferredTestQuery}"/>
<property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"/>
<property name="acquireRetryDelay" value="${jdbc.acquireRetryDelay}"/>
<property name="checkoutTimeout" value="${jdbc.checkoutTimeout}"/>
<property name="numHelperThreads" value="${jdbc.numHelperThreads}"/>
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource_activiti"/>
<property name="transactionManager" ref="transactionManager_activiti"/>
<property name="databaseSchemaUpdate" value="true"/>
<property name="asyncExecutorActivate" value="true"/>
<property name="deploymentResources" value="classpath*:activiti/*.bpmn"/>
<!-- <property name="jobExecutorActivate" value="false" /> -->
<property name="activityFontName" value="宋体"/>
<property name="labelFontName" value="宋体"/>
<property name="annotationFontName" value="宋体"/>
<!-- mail -->
<property name="mailServerHost" value="localhost"/>
<property name="mailServerUsername" value="kafeitu"/>
<property name="mailServerPassword" value="000000"/>
<property name="mailServerPort" value="2025"/>
<!-- 缓存支持
<property name="processDefinitionCache">
<bean class="me.kafeitu.demo.activiti.util.cache.DistributedCache" />
</property>-->
<!-- 自定义表单字段类型 -->
<property name="customFormTypes">
<list>
<!-- <bean class="me.kafeitu.demo.activiti.activiti.form.UsersFormType"/> -->
</list>
</property>
<!-- JPA -->
<!-- <property name="jpaEntityManagerFactory" ref="entityManagerFactory" /> -->
<!-- <property name="jpaHandleTransaction" value="false" /> -->
<!-- <property name="jpaCloseEntityManager" value="false" /> -->
<!-- 全局事件 -->
<property name="typedEventListeners">
<map>
<entry key="VARIABLE_CREATED" >
<list>
<!-- <ref bean="variableCreateListener"/> -->
</list>
</entry>
</map>
</property>
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<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"/>
</beans>
3. 准备工作中还需要添加一个activitiDB数据库,这个好像是activiti自带可以生成的。
package clack.activity;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.junit.Test;
public class GenDB {
public void createActivitiEngineByCode() {
/*
* *1.通过代码形式创建 - 取得ProcessEngineConfiguration对象 - 设置数据库连接属性 - 设置创建表的策略
* (当没有表时,自动创建表) - 通过ProcessEngineConfiguration对象创建 ProcessEngine 对象
*/
// 取得ProcessEngineConfiguration对象
ProcessEngineConfiguration engineConfiguration = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration();
// 设置数据库连接属性
engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true"
+ "&useUnicode=true&characterEncoding=utf8");
engineConfiguration.setJdbcUsername("root");
engineConfiguration.setJdbcPassword("123456");
// 设置创建表的策略 (当没有表时,自动创建表)
// public static final java.lang.String DB_SCHEMA_UPDATE_FALSE =
// "false";//不会自动创建表,没有表,则抛异常
// public static final java.lang.String DB_SCHEMA_UPDATE_CREATE_DROP =
// "create-drop";//先删除,再创建表
// public static final java.lang.String DB_SCHEMA_UPDATE_TRUE =
// "true";//假如没有表,则自动创建
engineConfiguration.setDatabaseSchemaUpdate("true");
// 通过ProcessEngineConfiguration对象创建 ProcessEngine 对象
ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
System.out.println("流程引擎创建成功!");
}
public void createActivitiEngine() {
ProcessEngineConfiguration engineConfiguration = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("spring-context-activiti.xml");
// 从类加载路径中查找资源 activiti.cfg.xm文件名可以自定义
ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
System.out.println("使用配置文件Activiti.cfg.xml获取流程引擎");
}
public static void main(String[] args) {
new GenDB().createActivitiEngineByCode();
}
}
4. 接着在activiti视图中创建一张自定义的流程图,并添加相应的监听类,用xml编辑器打开如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="apply" name="apply" isExecutable="true">
<startEvent id="startevent1" name="Start" activiti:initiator="userId">
<extensionElements>
<activiti:executionListener event="start" class="clack.activity.listener.MyExecutionListener"></activiti:executionListener>
</extensionElements>
</startEvent>
<userTask id="usertask1" name="组长审批" activiti:assignee="${userId}" xmlns:activiti="http://activiti.org/bpmn" activiti:class="clack.activity.task.TeamApplyServiceTask"></userTask>
<userTask id="usertask2" name="经理审批" activiti:assignee="${userId}" xmlns:activiti="http://activiti.org/bpmn" activiti:class="clack.activity.task.ManagerApplyServiceTask"></userTask>
<serviceTask id="servicetask1" name="人事归档" activiti:class="clack.activity.task.HumanResouceServiceTask"></serviceTask>
<endEvent id="endevent1" name="End">
<extensionElements>
<activiti:executionListener event="start" class="clack.activity.listener.MyExecutionListener"></activiti:executionListener>
</extensionElements>
</endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask2" targetRef="servicetask1"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_apply">
<bpmndi:BPMNPlane bpmnElement="apply" id="BPMNPlane_apply">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="270.0" y="260.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="410.0" y="250.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="650.0" y="250.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
<omgdc:Bounds height="55.0" width="105.0" x="880.0" y="250.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="1100.0" y="260.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="305.0" y="277.0"></omgdi:waypoint>
<omgdi:waypoint x="410.0" y="277.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="515.0" y="277.0"></omgdi:waypoint>
<omgdi:waypoint x="650.0" y="277.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="755.0" y="277.0"></omgdi:waypoint>
<omgdi:waypoint x="880.0" y="277.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="985.0" y="277.0"></omgdi:waypoint>
<omgdi:waypoint x="1100.0" y="277.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
其中对应的一些监听类及任务类如下:
package clack.activity.listener;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
/**
*@description executionListern主要用于流程的开始、结束和连线的监听
* 共有三个值:"start"、"end"、"take"。
* 其中start和end用于整个流程的开始和结束,take用于连线
*@auth panmingshuai
*@time 2018年4月5日下午8:59:16
*
*/
public class MyExecutionListener implements ExecutionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateExecution execution){
//得到现在事件阶段的值,用"start".endsWith(eventName)来判断
String eventName = execution.getEventName();
if("start".endsWith(eventName)){
System.out.println("-------------------流程开始-------------------");
} else if("end".equals(eventName)){
System.out.println("-------------------流程结束-------------------");
}
}
}
package clack.activity.task;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
public class TeamApplyServiceTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
// TODO Auto-generated method stub
}
}
package clack.activity.task;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
public class ManagerApplyServiceTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
// TODO Auto-generated method stub
// String eventName = execution.getEventName();
// if("start".endsWith(eventName)){
// System.out.println("-------------------流程开始-------------------");
// } else if("end".equals(eventName)){
// System.out.println("-------------------流程结束-------------------");
// }
}
}
package clack.activity.task;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
public class HumanResouceServiceTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
// TODO Auto-generated method stub
}
}
5. 接着处理部署方法,由于在一个流程中仅需要部署一次(部署多次没有意义),所以需要将部署方法剥离出来放到监听器中,在运行了一次后将其注释掉,相关的初始部署方法、监听器方法、以及web.xml中的改动如下:
package clack.init;
import java.io.File;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
public class ActivityInit {
public static ProcessEngine processEngine;
// 得到流程存储服务实例
public static RepositoryService repositoryService;
// 得到运行时服务组件
public static RuntimeService runtimeService;
// 得到历史服务组件
public static HistoryService historyService;
// 用户 分组信息服务 可以不使用
public static IdentityService identityService;
public static TaskService taskService;
static {
// ProcessEngineConfiguration engineConfiguration =
// ProcessEngineConfiguration
// .createProcessEngineConfigurationFromResource("spring-context-activiti.xml");
ProcessEngineConfiguration engineConfiguration = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration();
// 设置数据库连接属性
engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true"
+ "&useUnicode=true&characterEncoding=utf8");
engineConfiguration.setJdbcUsername("root");
engineConfiguration.setJdbcPassword("123456");
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setDatabaseSchemaUpdate("true");
// 通过ProcessEngineConfiguration对象创建 ProcessEngine 对象
processEngine = engineConfiguration.buildProcessEngine();
// 仓储服务
repositoryService = processEngine.getRepositoryService();
// 得到运行时服务组件
runtimeService = processEngine.getRuntimeService();
// 得到历史服务组件
historyService = processEngine.getHistoryService();
identityService = processEngine.getIdentityService();
taskService = processEngine.getTaskService();
}
// 1.参考模板 启动流程 会写入相关数据库,key为自定义编号
public static Deployment deployeeProcess(String bpmnName, String key) {
// 指定执行我们刚才部署的工作流程
// RepositoryService repositoryService =
// processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()// 创建一个部署的构建器
.addClasspathResource("clack/activity/" + bpmnName + ".bpmn")// 从类路径中添加资源,一次只能添加一个资源
.name("项目审批")// 设置部署的名称
.category("审批类别")// 设置部署的类别
.key(key).deploy();
org.springframework.web.context.ContextLoaderListener xx;
System.out.println("部署的id" + deployment.getId());
System.out.println("部署的名称" + deployment.getName());
return deployment;
}
}
package clack.activity.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import clack.init.ActivityInit;
public class ApplyListener implements ServletContextListener {
//1.静态的启动部分放在init中,该方法用于部署工作流,目前仅需要启动服务时配置一次,所以
//只需要第一次启动时加载ApplyListener中的类,过后注释掉。
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
//ActivityInit.deployeeProcess("apply", "user001");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>clack.activity.listener.ApplyListener</listener-class>
</listener>
<!--注意,由于activiti.xml应该在springmvc中启动,所以已经改名让SpringMVC访问读取-->
6. 然后就是Controller层与JSP页面的交互,大量的逻辑就是在这个文件中,里面还包含了同时将流程图例绘制到服务器上和本地磁盘上,耗时耗力,并在处理activiti数据库中不同的ID之间的对应与取值浪费了许多时间,
package clack.controller;
import java.io.File;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/apply")
public class ActivityController {
private String savePath = "activity";
public String getSavePath() {
return savePath;
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
@Autowired
public ProcessEngine processEngine;
// 得到流程存储服务实例
@Autowired
public RepositoryService repositoryService;
// 得到运行时服务组件
@Autowired
public RuntimeService runtimeService;
// 得到历史服务组件
@Autowired
public HistoryService historyService;
// 用户 分组信息服务 可以不使用
@Autowired
public IdentityService identityService;
@Autowired
public TaskService taskService;
// static {
//
// // ProcessEngineConfiguration engineConfiguration =
// // ProcessEngineConfiguration
// //
// .createProcessEngineConfigurationFromResource("spring-context-activiti.xml");
//
// ProcessEngineConfiguration engineConfiguration =
// ProcessEngineConfiguration
// .createStandaloneProcessEngineConfiguration();
// // 设置数据库连接属性
// engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
// engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true"
// + "&useUnicode=true&characterEncoding=utf8");
// engineConfiguration.setJdbcUsername("root");
// engineConfiguration.setJdbcPassword("root");
// engineConfiguration.setActivityFontName("宋体");
// engineConfiguration.setAnnotationFontName("宋体");
// engineConfiguration.setLabelFontName("宋体");
// engineConfiguration.setDatabaseSchemaUpdate("true");
//
// // 通过ProcessEngineConfiguration对象创建 ProcessEngine 对象
//
// processEngine = engineConfiguration.buildProcessEngine();
// // 仓储服务
// repositoryService = processEngine.getRepositoryService();
// // 得到运行时服务组件
// runtimeService = processEngine.getRuntimeService();
// // 得到历史服务组件
// historyService = processEngine.getHistoryService();
//
// identityService = processEngine.getIdentityService();
//
// taskService = processEngine.getTaskService();
//
// }
// 1.参考模板 启动流程 会写入相关数据库,key为自定义编号
//1.静态的启动部分放在init中,该方法用于部署工作流,目前仅需要启动服务时配置一次,所以
//只需要第一次启动时加载ApplyListener中的类,过后注释掉。
public Deployment deployeeProcess(String bpmnName, String key) {
// 指定执行我们刚才部署的工作流程
// RepositoryService repositoryService =
// processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()// 创建一个部署的构建器
.addClasspathResource("com/activity/" + bpmnName + ".bpmn")// 从类路径中添加资源,一次只能添加一个资源
.name("项目审批")// 设置部署的名称
.category("审批类别")// 设置部署的类别
.key(key).deploy();
org.springframework.web.context.ContextLoaderListener xx;
System.out.println("部署的id" + deployment.getId());
System.out.println("部署的名称" + deployment.getName());
return deployment;
}
// 2.启动流程 写入相关数据库,processDefinitionKey 为自定义流程模板名称
@RequestMapping(value = "/startProcess")
@ResponseBody
public List<String> startProcess(String appid, String businessKey, String processDefinitionKey,
HttpServletRequest request) {
// String processDefinitionKey = "leave";
// 取运行时服务
System.err.println("userId" + " " + appid + " " + businessKey + " " + processDefinitionKey);
// RuntimeService runtimeService = processEngine.getRuntimeService();
Map<String, Object> variables = new HashMap<String, Object>();
//userId为bpmn文件中xml编辑器中自定义的用户变量
variables.put("userId", appid);
// 设置流程启动用户
identityService.setAuthenticatedUserId(appid);
// 取得流程实例
ProcessInstance pi = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);// 通过流程定义的key
// 来执行流程
System.out.println("流程实例id:" + pi.getId());// 流程实例id
System.out.println("流程定义id:" + pi.getProcessDefinitionId());// 输出流程定义的id
String imgsrc = getActivitiProccessImage(pi.getId(), request);
System.err.println("pi:" + pi);
//list中存放的三个值分别为服务器上的图片生成名称、流程实例的Id、以及获取流程图像
List<String> list = new ArrayList<String>();
list.add(imgsrc);
list.add(pi.getId());
//getActivitiByPiId(pi.getId());
list.add(getActivitiByPiId(pi.getId()));
return list;
}
@RequestMapping(value = "/viewImage")
@ResponseBody
public void viewImage(String deploymentId) throws Exception {
// 创建仓库服务对对象
// RepositoryService repositoryService =
// processEngine.getRepositoryService();
// 从仓库中找需要展示的文件
List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
String imageName = null;
for (String name : names) {
if (name.indexOf(".png") >= 0) {
imageName = name;
}
}
if (imageName != null) {
System.out.println(imageName);
File f = new File("d:/" + imageName);
// 通过部署ID和文件名称得到文件的输入流
InputStream in = repositoryService.getResourceAsStream(deploymentId, imageName);
FileUtils.copyInputStreamToFile(in, f);
}
}
public String getDeployeeIdByPid(String piInstancetId) {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(piInstancetId).singleResult();
String deployeeId = "";
if (historicProcessInstance == null) {
System.out.println("未获取到");
} else {
// 获取流程定义
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());
String piId = processDefinition.getId();
deployeeId = processDefinition.getDeploymentId();
}
return deployeeId;
}
/**
* 获取流程图像,已执行节点和流程线高亮显示
*/
public String getActivitiByPiId(String piInstancetId) {
String imageName = null;
List<String> executedActivityIdList = new ArrayList<String>();
String newFileName = "";
System.out.println("piInstancetId" + piInstancetId);
// logger.info("[开始]-获取流程图图像");
try {
// 获取历史流程实例
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(piInstancetId).singleResult();
System.err.println("his:" + historicProcessInstance);
if (historicProcessInstance == null) {
// throw new BusinessException("获取流程实例ID[" + pProcessInstanceId
// + "]对应的历史流程实例失败!");
} else {
// 获取流程定义
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());
// 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = historyService
.createHistoricActivityInstanceQuery().processInstanceId(piInstancetId)
.orderByHistoricActivityInstanceId().asc().list();
// 已执行的节点ID集合
//List<String> executedActivityIdList = new ArrayList<String>();
System.out.println("aa:" + executedActivityIdList.size());
int index = 1;
// logger.info("获取已经执行的节点ID");
for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
executedActivityIdList.add(activityInstance.getTaskId());
System.out.println("a:"+activityInstance.getActivityId()+" "+activityInstance.getId()+" "+activityInstance.getTaskId());
// logger.info("第[" + index + "]个已执行节点=" +
// activityInstance.getActivityId() + " : "
// +activityInstance.getActivityName());
index++;
}
}
} catch (Exception e) {
// TODO: handle exception
}
return executedActivityIdList.get(executedActivityIdList.size()-1);
}
public String getActivitiProccessImage(String piInstancetId, HttpServletRequest request) {
String imageName = null;
String newFileName = "";
System.out.println("piInstancetId" + piInstancetId);
// logger.info("[开始]-获取流程图图像");
try {
// 获取历史流程实例
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(piInstancetId).singleResult();
System.err.println("his:" + historicProcessInstance);
if (historicProcessInstance == null) {
// throw new BusinessException("获取流程实例ID[" + pProcessInstanceId
// + "]对应的历史流程实例失败!");
} else {
// 获取流程定义
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());
// 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = historyService
.createHistoricActivityInstanceQuery().processInstanceId(piInstancetId)
.orderByHistoricActivityInstanceId().asc().list();
// 已执行的节点ID集合
List<String> executedActivityIdList = new ArrayList<String>();
System.out.println("aa:" + executedActivityIdList.size());
int index = 1;
// logger.info("获取已经执行的节点ID");
for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
executedActivityIdList.add(activityInstance.getActivityId());
// logger.info("第[" + index + "]个已执行节点=" +
// activityInstance.getActivityId() + " : "
// +activityInstance.getActivityName());
index++;
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 已执行的线集合
List<String> flowIds = new ArrayList<String>();
// 获取流程走过的线 (getHighLightedFlows是下面的方法)
flowIds = getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);
// System.out.println(flowIds.size()+" dddddddd");
// 获取固定图像名称
String deploymentId = getDeployeeIdByPid(piInstancetId);
List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
for (String name : names) {
if (name.indexOf(".png") >= 0) {
imageName = name;
}
}
System.out.println("imageName" + imageName);
if (imageName != null) {
String uploadPath = request.getRealPath(getSavePath());
System.out.println("path:" + uploadPath);
newFileName = "" + piInstancetId + ".png";
String path = uploadPath + "/";
System.err.println("path:" + path);
File fileparent = new File(path);
if (!fileparent.exists()) {
fileparent.mkdirs();
}
File img = new File(path + newFileName);
System.out.println("img:" + img);
File f = new File("d:/" + imageName);
// 获取流程图图像字符流
ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration()
.getProcessDiagramGenerator();
// 配置字体
InputStream in = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体",
"微软雅黑", "黑体", null, 2.0);
InputStream in1 = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体",
"微软雅黑", "黑体", null, 2.0);
// response.setContentType("image/png");
// OutputStream os = response.getOutputStream();
FileUtils.copyInputStreamToFile(in, f);
FileUtils.copyInputStreamToFile(in1, img);
in.close();
in1.close();
}
// int bytesRead = 0;
// byte[] buffer = new byte[8192];
// while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1)
// {
// os.write(buffer, 0, bytesRead);
// }
// os.close();
// imageStream.close();
// String deploymentId = getDeployeeIdByPid(piInstancetId);
// List<String> names =
// repositoryService.getDeploymentResourceNames(deploymentId);
// String imageName = null;
// for (String name : names) {
// if (name.indexOf(".png") >= 0) {
// imageName = name;
// }
// }
// if (imageName != null) {
// System.out.println(imageName);
// File f = new File("e:/" + imageName);
// // 通过部署ID和文件名称得到文件的输入流
// InputStream in =
// repositoryService.getResourceAsStream(deploymentId,
// imageName);
// FileUtils.copyInputStreamToFile(in, f);
// }
}
// logger.info("[完成]-获取流程图图像");
} catch (Exception e) {
System.err.println("eee:" + e.getMessage());
// throw new BusinessException("获取流程图失败!" + e.getMessage());
}
System.out.println("img2" + newFileName);
return newFileName;
}
public List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity,
List<HistoricActivityInstance> historicActivityInstances) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 24小时制
List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
// 对历史流程节点进行遍历
// 得到节点定义的详细信息
FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess()
.getFlowElement(historicActivityInstances.get(i).getActivityId());
List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以保存后续开始时间相同的节点
FlowNode sameActivityImpl1 = null;
HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一个节点
HistoricActivityInstance activityImp2_;
for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
activityImp2_ = historicActivityInstances.get(k);// 后续第1个节点
if (activityImpl_.getActivityType().equals("userTask")
&& activityImp2_.getActivityType().equals("userTask")
&& df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))) // 都是usertask,且主节点与后续节点的开始时间相同,说明不是真实的后继节点
{
} else {
sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess()
.getFlowElement(historicActivityInstances.get(k).getActivityId());// 找到紧跟在后面的一个节点
break;
}
}
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 (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {// 如果第一个节点和第二个节点开始时间相同保存
FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess()
.getFlowElement(activityImpl2.getActivityId());
sameStartTimeNodes.add(sameActivityImpl2);
} else {// 有不相同跳出循环
break;
}
}
List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows(); // 取出节点的所有出去的线
for (SequenceFlow pvmTransition : pvmTransitions) {// 对所有的线进行遍历
FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess()
.getFlowElement(pvmTransition.getTargetRef());// 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示
if (sameStartTimeNodes.contains(pvmActivityImpl)) {
highFlows.add(pvmTransition.getId());
}
}
}
return highFlows;
}
public String getPiIdbyTid(String taskId) {
System.err.println("taskId1:" + taskId);
Task task = taskService.createTaskQuery() // 创建任务查询
.taskId(taskId) // 根据任务id查询
.singleResult();
String piInstancetId = task.getProcessInstanceId(); // 获取流程定义id
return piInstancetId;
}
@RequestMapping(value = "/completeTaskByTaskId")
@ResponseBody
public List<String> completeTaskByTaskId(String taskId, HttpServletRequest request) {
System.out.println("taskId:" + taskId);
String piInstancetId = getPiIdbyTid(taskId);
taskService.complete(taskId);
String img = getActivitiProccessImage(piInstancetId, request);
System.err.println("img3" + img);
List<String> list=new ArrayList<String>();
list.add(img);
list.add(getActivitiByPiId(piInstancetId));
return list;
}
}
7. 最后,添加一个简单的JSP用ajax调用Controller中的方法便大功告成。
但是:其中最坑的一点是jsp页面读取图片(图片名称一样,只是走流程时图片发生了变化)需要等待几秒点击下一个按钮才会生效,不然图片在当前页面不会刷新,但是其实图片的样子已经改变,这其中的原理或许是因为我传的值不好,又或者是浏览器缓存问题,反正需要等待几秒,最后,放一张效果图。
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>" />
<meta charset="UTF-8">
<title>首页</title>
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<style type="text/css">
.startli,.teamli,.managerli{
display:none;
}
</style>
<script type="text/javascript" src="js/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(function() {
$('.btnStart').click(function() {
//alert($('.btnStart').attr("alt"));
$.ajax({
url : "apply/startProcess",
type : 'post',
data : {"appid":$('.btnStart').attr("alt"),"businessKey":$('.btnStart').attr("alt1"),"processDefinitionKey":$('.btnStart').attr("alt2")}, //提交给服务器的参数
dataType : 'json', //返回值类型,服务器out对象输出的内容
success : function(objs) { //回调函数 ,服务器有返回输出的时候调用的函数
//alert(objs[0]+"=========="+objs[1]);
$('.Zu').val(objs[2]);
$('.img').attr("src",'activity/'+objs[0]);
$('.startli').text("审批已开启,提交给组长中,请稍后...");
$('.startli').show();
}
});
});
$('.Zu1').click(function() {
//alert($('.Zu').val());
$.ajax({
url : "apply/completeTaskByTaskId",
type : 'post',
data : {"taskId":$('.Zu').val()}, //提交给服务器的参数
dataType : 'json', //返回值类型,服务器out对象输出的内容
success : function(objs) { //回调函数 ,服务器有返回输出的时候调用的函数
$('.Xj').val(objs[1]);
var attr=$('.img').attr('attr');
//alert(parseInt(attr+1));
var $parent=$('.img').parent();
$parent.html('');
var m="<img src='activity/"+objs[0]+"?attr="+new Date().getTime()+"'/>";
$parent.append(m);
$('.teamli').text('组长审批通过,提交给经理中,请稍后...');
$('.teamli').show();
}
});
});
$('.Xj1').click(function() {
$.ajax({
url : "apply/completeTaskByTaskId",
type : 'post',
data : {"taskId":$('.Xj').val()}, //提交给服务器的参数
dataType : 'json', //返回值类型,服务器out对象输出的内容
success : function(objs) { //回调函数 ,服务器有返回输出的时候调用的函数
//var attr=$('.img').attr('attr');
//alert(parseInt(attr+1));
//var $parent=$('.img').parent();
$(".imgDiv").html('');
var m="<img src='activity/"+objs[0]+"?attr="+new Date().getTime()+"'/>";
$(".imgDiv").append(m);
$('.managerli').text('经理审批通过,正在自动人事归档,请稍后...');
$('.managerli').show();
}
});
});
});
</script>
</head>
<body>
<h3>项目审批工作:</h3>
<ul>
<li><input name="btnStart" class="btnStart" alt="1"
alt1="user001" alt2="apply" type="hidden">
<button class="btnStart">开始审批</button></li>
<li class="startli"></li>
<li><button class="Zu1">组长审批</button>
<input name="Zu" class="Zu" type="hidden"></li>
<li class="teamli"></li>
<li><button class="Xj1">经理审批</button> <input name="Xj" class="Xj"
type="hidden"></li>
<li class="managerli"></li>
<li>人事归档</li>
</ul>
<div class="imgDiv"><img src="" class="img" id="img1" attr="1"></div>
</body>
</html>
当然,其中还有很多小坑,以后还需慢慢完善。