工作流直譯就是工作的流程,但是怎樣設計與實現並應用到程序中還是有一些難度,不然為什么有些公司可以靠工作流起家呢?其中大量的邏輯充斥其中,稍不注意就會出錯。
本次在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>

當然,其中還有很多小坑,以后還需慢慢完善。
