Activiti 工作流引擎的初步使用

最近领导让我研究下工作流,于是查啊查就查到了Activiti,特么刚开始一直查的是Activity,查出来一堆Android的东西,我也是醉了。话不多说,下面就记录下这2天的研究成果吧。

所用环境

Maven工程
 JDK:jdk1.8.0_73
 IDE:eclipse Mars.2 Release (4.5.2)
 数据库:mysql 5.1.39-ndb-7.0.9-cluster-gpl
 SSM框架:(spring + spring-mvc)4.3.2.RELEASE + mybatis3.4.1
 Activiti:5.21.0

spring+mvc+mybatis整合就不贴了,网上一大堆了

eclipse安装流程设计插件

eclipse依次点击 Help -> Install New Software -> Add:
 Name:Activiti Designer
 Location:http://activiti.org/designer/update/
 点击OK选中插件安装即可

添加 Activiti 到项目中

  1. 在 pom.xml 中添加 Activiti 依赖
    5.21.0

     
         org.activiti
         activiti-engine
         ${activiti.version}
     
     
     
         org.activiti
         activiti-spring
         ${activiti.version}
     
     
     
         org.activiti
         activiti-rest
         ${activiti.version}
     
    

2.新建 applicationContext-activiti.xml,别忘了在主配置文件中将其import


    

        
        
            
            
            
            
            
              
              
        

        
        
            
        

        
        
        
        
        
        
        
    
    

3.启动项目,如果未出现错误,Activiti会在连接的数据库中自动新建25张表,如下:

Activiti 工作流引擎的初步使用_第1张图片
25张表.png

至此Activiti流程引擎添加完毕,下面开始使用Activiti,实现一次请假流程

设计流程

流程设计插件安装成功后会在eclipse新建向导中出现Activiti向导,如图

Activiti 工作流引擎的初步使用_第2张图片
Paste_Image.png

1.我们新建一个 Activiti Diagram 命名为 leave.bpmn,完成时如下图:

Activiti 工作流引擎的初步使用_第3张图片
Paste_Image.png

主要就是拖拖拉拉,从左至右控件分别为
StartEvent,UserTask,ExlusiveGateway,UserTask,EndEvent;连线都是SequenceFlow

属性可在Properties视图中设置,如果没有这个视图,可在 eclipse 中依次点击
Window->Show View->Other 搜索Properties点击OK即可

  • 流程属性:一般设置一个Id,Name,NameSpace就可以了,此处为分别为leaveProcess、Leave Process、http://www.mario.com; 这个Id在之后启动流程时会用到,不同流程Id不可相同
  • 开始事件(StartEvent):流程开始
  • 结束事件(EndEvent):流程结束
  • 用户任务(UserTask):主要用到Id,Name,Assignee
    Assignee为此任务的办理人,在查找任务时需要用到,三个任务分别指派给 apply,pm,boss
Paste_Image.png
Paste_Image.png
  • 排他网关(ExlusiveGateway):只会寻找唯一一条能走完的顺序流
  • 顺序流(SequenceFlow):主要用到Id,Name,Condition
    Condition用于指定该顺序流表达式 ,day的值会在调用执行任务方法时传入
Activiti 工作流引擎的初步使用_第4张图片
Paste_Image.png

除了使用可视化组件,我们也可以通过xml来设计流程,以上流程的xml定义如下:



  
    
    
    
    
    
    
      
    
    
    
      3}]]>
    
    
    
    
    
  
  
    
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
        
        
          
        
      
      
        
        
        
          
        
      
      
        
        
        
          
        
      
      
        
        
      
      
        
        
      
      
        
        
      
    
  

流程部署

有了流程图,我们就可以部署该流程了,Activiti提供多种部署方法(有自动部署,手动部署等),这里演示两种手动部署方法

Workflow.java代码片段

private static ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/**
 * 通过定义好的流程图文件部署,一次只能部署一个流程
 */
public static void deploy() {
    RepositoryService repositoryService = processEngine.getRepositoryService();
    Deployment deployment = repositoryService.createDeployment()
            .addClasspathResource("death/note/lawliet/web/workflow/leave.bpmn").deploy();
}
/**
 * 将多个流程文件打包部署,一次可以部署多个流程
 */
public void deployByZip() {
    InputStream is = this.getClass().getClassLoader().getResourceAsStream("diagrams/bpm.zip");
    ZipInputStream zip = new ZipInputStream(is);
    Deployment deployment = processEngine
            .getRepositoryService()
            .createDeployment()
            .addZipInputStream(zip)
            .deploy();
}

方便起见,通过一个Deploy按钮来部署流程

Activiti 工作流引擎的初步使用_第5张图片
Paste_Image.png

部署成功后,会分别在 act_ge_bytearray,act_re_deployment,act_re_procdef三张表插入相应数据,多次部署同一流程的话会增加版本号,以此获取最新的流程

启动流程

我们通过一个表单来用于请假申请

Activiti 工作流引擎的初步使用_第6张图片
Paste_Image.png

我们定义一个 Leave 对象用于保存请假信息,相应的数据表为 leave 。Result 对象用于封装一些返回信息。这里的 "leaveProcess" 就是在流程属性中定义的Id。启动成功后会返回一个 ProcessInstance 对象,这个对象主要是一些流程的基本信息,具体可以查看文档或源码。这里将返回的流程实例 id 存入到我们的 leave 表,以便以后可以通过这个 id 查询相关的流程。

@ResponseBody
@RequestMapping(value = "/save", method = RequestMethod.POST)
public Result save(@RequestBody Leave user) {
    Result result = new Result();
    ProcessInstance pi = Workflow.startInstanceByKey("leaveProcess");
    user.setInstaceId(pi.getId());
    leaveService.insert(user);
    result.info(true, 0, "保存成功");
    return result;
}

Workflow.java代码片段

public static ProcessInstance startInstanceByKey(String instanceByKey) {
    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceByKey);
    return instance;
}

流程启动成功后会在 act_hi_actinst,act_hi_identitylink,act_hi_procinst,act_hi_taskinst,act_ru_execution,act_ru_identitylink,act_ru_task 表中插入相应数据。我们比较关心的就是 act_ru_task 表了,它存储了任务的相关信息。

查看任务

流程启动完毕后,应该就是进入了请假申请任务,我们可以通过请假申请的办理人 apply 来查询该任务(当然还有其他方法),这里可以指定不同查询条件和过滤条件。返回 taskList 就是当前的任务列表了,Task是Activiti为我们定义好的接口对象,主要封装了任务的信息。
 这里由于 Activiti 的Task是接口对象无法转换为json,所以自定义了一个Task对象,来转换成json

@ResponseBody
@RequestMapping(value = "/data/{assignee}")
public List data(@PathVariable String assignee){
    List tasks = Workflow.findTaskByAssignee(assignee);
    
    List list = new ArrayList<>();
    for(Task task : tasks){
        death.note.lawliet.web.model.Task t = new death.note.lawliet.web.model.Task();
        t.setTaskId(task.getId());
        t.setName(task.getName());
        t.setAssignee(task.getAssignee());
        t.setExecutionId(task.getExecutionId());
        t.setProcessInstanceId(task.getProcessInstanceId());
        t.setProcessDefinitionId(task.getProcessDefinitionId());
        list.add(t);
    }
    return list;
}

Workflow.java代码片段

 public static List findTaskByAssignee(String assignee) {
    TaskService taskService = processEngine.getTaskService();
    List taskList = taskService.createTaskQuery().taskAssignee(assignee).list();
    return taskList;
}
Activiti 工作流引擎的初步使用_第7张图片
Paste_Image.png

查看流程图

我们可以通过流程定义ID(processDefinitionId)来获取流程图

@RequestMapping(value = "/shwoImg/{procDefId}")  
public void shwoImg(@PathVariable String procDefId,HttpServletResponse response){  
    try {  
        InputStream pic = Workflow.findProcessPic(procDefId);  
          
        byte[] b = new byte[1024];  
        int len = -1;  
        while((len = pic.read(b, 0, 1024)) != -1) {  
            response.getOutputStream().write(b, 0, len);  
        }  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
}  

Workflow.java代码片段

public static InputStream findProcessPic(String procDefId) throws Exception {
    RepositoryService repositoryService = processEngine.getRepositoryService();
    ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId)
            .singleResult();
    String diagramResourceName = procDef.getDiagramResourceName();
    InputStream imageStream = repositoryService.getResourceAsStream(procDef.getDeploymentId(), diagramResourceName);
    return imageStream;
}

然后通过processDefinitionId,executionId 来获取当前正在执行任务的位置坐标,以便用于标识流程图上的位置。ActivityImpl 对象封装了任务的位置信息,包括xy坐标,长和宽。自定义 Rect 对象用于转换json

@ResponseBody
@RequestMapping(value = "/showImg/{procDefId}/{executionId}")
public Rect showImg(@PathVariable String procDefId,@PathVariable String executionId ) {
    Rect rect = new Rect();
    try {
        ActivityImpl img = Workflow.getProcessMap(procDefId,executionId );
        rect.setX(img.getX());
        rect.setY(img.getY());
        rect.setWidth(img.getWidth());
        rect.setHeight(img.getHeight());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return rect;
}

Workflow.java代码片段

public static ActivityImpl getProcessMap(String procDefId, String executionId) throws Exception {
    ActivityImpl actImpl = null;
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //获取流程定义实体
    ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
            .getDeployedProcessDefinition(procDefId);
    RuntimeService runtimeService = processEngine.getRuntimeService();
    //获取执行实体
    ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(executionId)
            .singleResult();
    // 获取当前任务执行到哪个节点
    String activitiId = execution.getActivityId();
    // 获得当前任务的所有节点
    List activitiList = def.getActivities();
    for (ActivityImpl activityImpl : activitiList) {
        String id = activityImpl.getId();
        if (id.equals(activitiId)) {
            actImpl = activityImpl;
            break;
        }
    }
    return actImpl;
}

最终生成图片时分别调用2个 showImg 方法即可,红框可以根据返回的 Rect 来绘制,起始坐标根据自己的布局自行调整

LeavePicController.js片段

var pic.rect = {};
var pic.procDefId = $stateParams.procDefId;
$http.get('workflow/showImg/'+$stateParams.procDefId +'/'+$stateParams.executionId) 
          .success(function(data) {
              pic.rect.x = data.x;
              pic.rect.y = data.y;
              pic.rect.width = data.width;
              pic.rect.height = data.height;
          });

picture.html

Activiti 工作流引擎的初步使用_第8张图片
Paste_Image.png

流程审批

通过 taskId 就可以对当前执行的任务进行审批,这里的 day 应该从 leave 表中查询出来,方便起见就写死了,这个day也就是在顺序流的Condition中指定的变量,小于等于3就会流向项目经理(pm)审批任务了

@ResponseBody
@RequestMapping(value = "/check/{taskId}")
public Result check(@PathVariable String taskId) {
    Result result = new Result();
    Map map = new HashMap<>();
    map.put("day", 3);
    Workflow.completeTask(taskId,map);
    result.info(true, 0, "审批成功");
    return result;
}

Workflow.java代码片段

public static void completeTask(String taskid,Map map map) {
    TaskService taskService = processEngine.getTaskService();
    taskService.complete(taskid, map);
}

审批成功后,就需要通过办理人(pm)来查询任务了,并且请假办理(apply)中的任务被移除了

Activiti 工作流引擎的初步使用_第9张图片
Paste_Image.png
Activiti 工作流引擎的初步使用_第10张图片
Paste_Image.png

同时流程图也流向了下一节点

Activiti 工作流引擎的初步使用_第11张图片
Paste_Image.png

项目经理再执行一次审批方法,这个流程就算走完了

最后

以上就是一次相对简单的流程:部署,启动,查询任务,显示流程图,审批。Activiti流程引擎还有很多核心操作,包括驳回、会签、转办、中止、挂起等,等有空的时候再深入研究吧
 最后的最后,初来乍到,不喜勿喷或轻喷 - -!Thanks...

你可能感兴趣的:(Activiti 工作流引擎的初步使用)