Activiti(二) springBoot2集成activiti,集成activiti在線設計器

  • 摘要

    本篇隨筆主要記錄springBoot2集成activiti流程引擎,而且嵌入activiti的在線設計器,能夠經過瀏覽器直接編輯出咱們須要的流程,不須要經過eclipse或者IDEA的actiBpm插件設計流程再直接部署到項目下,頁面保存流程後可直接發佈、發起流程。javascript

  • 所需軟件版本
    springBoot  2.0.1.RELEASE
    activiti 5.22.0    官網下載地址
    activiti-webapp-explorer2   個人github上的explorer項目

  • 集成方法
    activiti支持多種數據庫,自己還有個h2的內存數據庫,我這裏用的是Mysql
    一、pom.xml引入依賴,這裏主要是activiti的依賴包,${activiti.version}爲5.22.0

     <!--spring activiti start-->
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-spring-boot-starter-basic</artifactId>
                <version>${activiti.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-actuator</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>spring-security-config</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-spring-boot-starter-actuator</artifactId>
                <version>${activiti.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-rest</artifactId>
                <version>${activiti.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>spring-security-config</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-explorer</artifactId>
                <version>${activiti.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>com.vaadin</groupId>
                        <artifactId>vaadin</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.vaadin.addons</groupId>
                        <artifactId>dcharts-widget</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>activiti-simple-workflow</groupId>
                        <artifactId>org.activiti</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-diagram-rest</artifactId>
                <version>${activiti.version}</version>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-simple-workflow</artifactId>
                <version>${activiti.version}</version>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-spring</artifactId>
                <version>${activiti.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.xmlgraphics</groupId>
                <artifactId>batik-codec</artifactId>
                <version>1.7</version>
            </dependency>
            <dependency>
                <groupId>org.apache.xmlgraphics</groupId>
                <artifactId>batik-css</artifactId>
                <version>1.7</version>
            </dependency>
            <dependency>
                <groupId>org.apache.xmlgraphics</groupId>
                <artifactId>batik-svg-dom</artifactId>
                <version>1.7</version>
            </dependency>
            <dependency>
                <groupId>org.apache.xmlgraphics</groupId>
                <artifactId>batik-svggen</artifactId>
                <version>1.7</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>4.1.3.RELEASE</version>
            </dependency>
    
            <!--spring activiti end-->

     二、拷貝項目資源
      解壓activiti-webapp-explorer2,把webapp下面的diagram-viewer、editor-app、modeler.html複製到springboot項目下的static下,這是activiti的在線設計器,modeler.html就是設計的主界面,複製resources下stencilset.json到本身的resources下。
      


      解壓activiti-5.22.0的包,將libs下的activiti-modeler-5.22.0-sources.jar解壓出來,把org\activiti\rest\editor路徑下的main、model文件夾複製到springboot項目的源碼路徑下,裏面有三個類,主要用於讀取stencilset.json。




    三、修改拷貝過來的資源文件
      修改StencilsetRestResource.java、ModelEditorJsonRestResource.java、ModelSaveRestResource.java3個controller,加上@RequestMapping("/service")



      修改editor-app下的app-cfg.js,把contextRoot後面改爲
    /service,和controller裏面加的requestMapping要一致
    css

  • var ACTIVITI = ACTIVITI || {};
    
    ACTIVITI.CONFIG = {
        'contextRoot' : '/service',
    };

     

    4、新增service、controller
      service實現,這裏只放實現類,接口就不放了,本身能夠根據實現類本身添加接口方法,這個service實現了把流程和自定義表單結合的功能,completeTask方法經過java反射執行不一樣任務節點執行不一樣form
    html

    package com.djkj.activiti.service.Impl;
    
    import com.djkj.activiti.bean.ActivitiVariable;
    import com.djkj.activiti.common.BusinessTaskUtil;
    import com.djkj.activiti.service.ActivitiService;
    import com.djkj.activiti.service.ActivitiVariableService;
    import org.activiti.bpmn.model.BpmnModel;
    import org.activiti.engine.HistoryService;
    import org.activiti.engine.RepositoryService;
    import org.activiti.engine.RuntimeService;
    import org.activiti.engine.TaskService;
    import org.activiti.engine.delegate.DelegateExecution;
    import org.activiti.engine.history.HistoricActivityInstance;
    import org.activiti.engine.history.HistoricProcessInstance;
    import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
    import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
    import org.activiti.engine.impl.pvm.PvmTransition;
    import org.activiti.engine.impl.pvm.process.ActivityImpl;
    import org.activiti.engine.runtime.ProcessInstance;
    import org.activiti.engine.task.Task;
    import org.activiti.image.ProcessDiagramGenerator;
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import sun.misc.BASE64Encoder;
    
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.*;
    
    @Service
    public class ActivitiServiceImpl implements ActivitiService {
    
        @Autowired
        private RuntimeService runtimeService;
        @Autowired
        private TaskService taskService;
        @Autowired
        private HistoryService historyService;
        @Autowired
        private RepositoryService repositoryService;
        @Autowired
        private ProcessEngineConfigurationImpl processEngineConfiguration;
        @Autowired
        private ActivitiVariableService activitiVariableService;
    
        private static final Logger logger=Logger.getLogger(ActivitiServiceImpl.class);
    
        /**
         * 啓動流程
         */
        @Override
        public void startProcesses(String id,String business_key) {
            ProcessInstance pi = runtimeService.startProcessInstanceByKey(id, business_key);//流程圖id,業務表id
            System.out.println("流程啓動成功,流程id:"+pi.getId());
        }
    
        /**
         *
         * <p>描述: 根據用戶id查詢待辦任務列表</p>
         * @author 範相如
         * @date 2018年2月25日
         */
        @Override
        public List<Task> findTasksByUserId(String userId) {
            List<Task> resultTask = taskService.createTaskQuery().processDefinitionKey("process").taskCandidateOrAssigned(userId).list();
            return resultTask;
        }
    
        @Override
        public Task findTaskById(String taskId){
            List<Task> resultTask = taskService.createTaskQuery().taskId(taskId).list();
            if(resultTask!=null){
                return resultTask.get(0);
            }
            return null;
        }
    
        /**
         *
         * <p>描述:任務審批     (經過/拒接) </p>
         * @author 範相如
         * @date 2018年2月25日
         * @param taskId 任務id
         * @param userId 用戶id
         * @param result false OR true
         */
        @Override
        public void completeTask(String taskId,String userId,String result) {
            //獲取流程實例
            taskService.claim(taskId, userId);
            //獲取任務
            Task task=taskService.createTaskQuery().taskId(taskId).singleResult();
            //獲取流程實例ID
            String proInsId = task.getProcessInstanceId();
            //獲取流程實例
            ProcessInstance process = runtimeService.createProcessInstanceQuery().processInstanceId(proInsId).singleResult();
            //獲取業務外鍵
            String business_key = process.getBusinessKey();
            String[] array = business_key.split(":");
            String business_Id = array[1];
            //業務處理
            try {
                Class clazz=BusinessTaskUtil.class;
                Object obj = clazz.newInstance();
                Method method = clazz.getMethod("actBusiness_"+task.getFormKey(),String.class,String.class,String.class);
                method.invoke(obj,userId,business_Id,result);
            }catch (Exception e){
                e.printStackTrace();
                logger.error("執行業務方法錯誤!");
    
            }
            taskService.complete(taskId);
        }
    
        /**
         * 更改業務流程狀態#{ActivityDemoServiceImpl.updateBizStatus(execution,"tj")}
         * @param execution
         * @param status
         */
        @Override
        public void updateBizStatus(DelegateExecution execution, String status) {
            String bizId = execution.getProcessBusinessKey();
            //根據業務id自行處理業務表
            System.out.println("業務表["+bizId+"]狀態更改爲功,狀態更改成:"+status);
        }
    
    
        //流程節點權限用戶列表${ActivityDemoServiceImpl.findUsers(execution,sign)}
        @Override
        public List<String> findUsersForSL(DelegateExecution execution){
            return Arrays.asList("sly1","sly2");
        }
    
        //流程節點權限用戶列表${ActivityDemoServiceImpl.findUsers(execution,sign)}
        @Override
        public List<String> findUsersForSP(DelegateExecution execution){
            return Arrays.asList("spy1","uspy2");
        }
    
        /**
         *
         * <p>描述:  生成流程圖
         * 首先啓動流程,獲取processInstanceId,替換便可生成</p>
         * @author 範相如
         * @date 2018年2月25日
         * @param processInstanceId
         * @throws Exception
         */
        @Override
        public void queryProImg(String processInstanceId) throws Exception {
            //獲取歷史流程實例
            HistoricProcessInstance processInstance =  historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    
            //根據流程定義獲取輸入流
            InputStream is = repositoryService.getProcessDiagram(processInstance.getProcessDefinitionId());
            BufferedImage bi = ImageIO.read(is);
            File file = new File("demo2.png");
            if(!file.exists()) file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            ImageIO.write(bi, "png", fos);
            fos.close();
            is.close();
            System.out.println("圖片生成成功");
    
            List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("userId").list();
            for(Task t : tasks) {
                System.out.println(t.getName());
            }
        }
    
    
        /**
         * 流程圖高亮顯示
         * 首先啓動流程,獲取processInstanceId,替換便可生成
         * @throws Exception
         */
        @Override
        public String queryProHighLighted(String processInstanceId) throws Exception {
            //獲取歷史流程實例
            HistoricProcessInstance processInstance =  historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            //獲取流程圖
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
    
            ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
            ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
    
            List<HistoricActivityInstance> highLightedActivitList =  historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
            //高亮環節id集合
            List<String> highLightedActivitis = new ArrayList<String>();
    
            //高亮線路id集合
            List<String> highLightedFlows = getHighLightedFlows(definitionEntity,highLightedActivitList);
    
            for(HistoricActivityInstance tempActivity : highLightedActivitList){
                String activityId = tempActivity.getActivityId();
                highLightedActivitis.add(activityId);
            }
            //配置字體
            InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis, highLightedFlows,"宋體","微軟雅黑","黑體",null,2.0);
            BufferedImage bi = ImageIO.read(imageStream);
    //        File file = new File("demo2.png");
    //        if(!file.exists()) file.createNewFile();
    //        FileOutputStream fos = new FileOutputStream(file);
            ByteArrayOutputStream bos= new ByteArrayOutputStream();
            ImageIO.write(bi, "png", bos);
            byte[] bytes = bos.toByteArray();//轉換成字節
            BASE64Encoder encoder = new BASE64Encoder();
            String png_base64 =  encoder.encodeBuffer(bytes);//轉換成base64串
            png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");//刪除 \r\n
            bos.close();
            imageStream.close();
            return png_base64;
        }
        /**
         * 獲取須要高亮的線
         * @param processDefinitionEntity
         * @param historicActivityInstances
         * @return
         */
        private List<String> getHighLightedFlows(
                ProcessDefinitionEntity processDefinitionEntity,
                List<HistoricActivityInstance> historicActivityInstances) {
    
            List<String> highFlows = new ArrayList<String>();// 用以保存高亮的線flowId
            for (int i = 0; i < historicActivityInstances.size() - 1; i++) {// 對歷史流程節點進行遍歷
                ActivityImpl activityImpl = processDefinitionEntity
                        .findActivity(historicActivityInstances.get(i)
                                .getActivityId());// 獲得節點定義的詳細信息
                List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存後需開始時間相同的節點
                ActivityImpl sameActivityImpl1 = processDefinitionEntity
                        .findActivity(historicActivityInstances.get(i + 1)
                                .getActivityId());
                // 將後面第一個節點放在時間相同節點的集合裏
                sameStartTimeNodes.add(sameActivityImpl1);
                for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                    HistoricActivityInstance activityImpl1 = historicActivityInstances
                            .get(j);// 後續第一個節點
                    HistoricActivityInstance activityImpl2 = historicActivityInstances
                            .get(j + 1);// 後續第二個節點
                    if (activityImpl1.getStartTime().equals(
                            activityImpl2.getStartTime())) {
                        // 若是第一個節點和第二個節點開始時間相同保存
                        ActivityImpl sameActivityImpl2 = processDefinitionEntity
                                .findActivity(activityImpl2.getActivityId());
                        sameStartTimeNodes.add(sameActivityImpl2);
                    } else {
                        // 有不相同跳出循環
                        break;
                    }
                }
                List<PvmTransition> pvmTransitions = activityImpl
                        .getOutgoingTransitions();// 取出節點的全部出去的線
                for (PvmTransition pvmTransition : pvmTransitions) {
                    // 對全部的線進行遍歷
                    ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition
                            .getDestination();
                    // 若是取出的線的目標節點存在時間相同的節點裏,保存該線的id,進行高亮顯示
                    if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                        highFlows.add(pvmTransition.getId());
                    }
                }
            }
            return highFlows;
        }
    }
    View Code

     

      controller實現,我寫了兩個controller,主要當時寫的時候沒有寫在一塊兒,大家能夠把這兩部分的內容合併在一塊兒
    java

    package com.djkj.activiti.controller;
    
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import org.activiti.bpmn.converter.BpmnXMLConverter;
    import org.activiti.bpmn.model.BpmnModel;
    import org.activiti.editor.constants.ModelDataJsonConstants;
    import org.activiti.editor.language.json.converter.BpmnJsonConverter;
    import org.activiti.engine.ProcessEngine;
    import org.activiti.engine.RepositoryService;
    import org.activiti.engine.repository.Deployment;
    import org.activiti.engine.repository.Model;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.List;
    
    @Controller
    @RequestMapping("/models")
    public class MyActivitiController {
    
        private static final Logger LOGGER = Logger.getLogger(MyActivitiController.class);
    
        @Autowired
        private RepositoryService repositoryService;
        @Autowired
        private ObjectMapper objectMapper;
    
        @RequestMapping("/modelList")
        public String modelList(org.springframework.ui.Model model,HttpServletRequest request){
            LOGGER.info("-------------列表頁-------------");
            List<Model> models = repositoryService.createModelQuery().orderByCreateTime().desc().list();
            model.addAttribute("models",models);
            String info = request.getParameter("info");
            if(StringUtils.isNotEmpty(info)){
                model.addAttribute("info",info);
            }
            return "model/list";
        }
    
        @RequestMapping("/create")
        public void newModel(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
           try {
               //初始化一個空模型
               Model model = repositoryService.newModel();
    
               //設置一些默認信息
               String name = "new-process";
               String description = "";
               int revision = 1;
               String key = "process";
    
               ObjectNode modelNode = objectMapper.createObjectNode();
               modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
               modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
               modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);
    
               model.setName(name);
               model.setKey(key);
               model.setMetaInfo(modelNode.toString());
    
               repositoryService.saveModel(model);
               String id = model.getId();
    
               //完善ModelEditorSource
               ObjectNode editorNode = objectMapper.createObjectNode();
               editorNode.put("id", "canvas");
               editorNode.put("resourceId", "canvas");
               ObjectNode stencilSetNode = objectMapper.createObjectNode();
               stencilSetNode.put("namespace",
                       "http://b3mn.org/stencilset/bpmn2.0#");
               editorNode.put("stencilset", stencilSetNode);
               repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
    
               response.sendRedirect(request.getContextPath() + "/static/modeler.html?modelId=" + id);
           }catch (IOException e){
               e.printStackTrace();
               LOGGER.info("模型建立失敗!");
           }
    
        }
    
        @RequestMapping("/delete/{id}")
        public @ResponseBody String deleteModel(@PathVariable("id")String id){
            repositoryService.deleteModel(id);
            return "刪除成功!";
        }
    
    
        @RequestMapping("/deployment/{id}")
        public @ResponseBody String deploy(@PathVariable("id")String id) throws Exception {
    
            //獲取模型
            Model modelData = repositoryService.getModel(id);
            byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
    
            if (bytes == null) {
                return "模型數據爲空,請先設計流程併成功保存,再進行發佈。";
            }
    
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
    
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if(model.getProcesses().size()==0){
                return "數據模型不符要求,請至少設計一條主線流程。";
            }
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
    
            //發佈流程
            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
            return "流程發佈成功";
        }
    
        @RequestMapping("/start/{id}")
        public @ResponseBody String startProcess(@PathVariable("id") String id){
            try {
    
    
            }catch (Exception e){
                e.printStackTrace();
                return "流程啓動失敗!";
            }
            return "流程啓動成功";
        }
    
    
    }
    View Code

     

    顯示全部流程列表,以及流程的增刪改node

    package com.djkj.activiti.controller;
    
    
    import com.djkj.activiti.bean.ActivitiVariable;
    import com.djkj.activiti.bean.FormData;
    import com.djkj.activiti.service.ActivitiService;
    import com.djkj.activiti.service.ActivitiVariableService;
    import org.activiti.engine.HistoryService;
    import org.activiti.engine.TaskService;
    import org.activiti.engine.task.Task;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
    
    import javax.annotation.Resource;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Controller
    @RequestMapping("/activiti")
    public class ActivitiProcessController {
        @Resource
        private ActivitiService activitiServiceImpl;
        @Autowired
        private TaskService taskService;
        @Autowired
        private HistoryService historyService;
        @Autowired
        private ActivitiVariableService activitiVariableService;
    
    //    private static final String business = "testtask";
    
        //啓動流程---215001
        @RequestMapping("/start/{key}")
        public String startProcess(@PathVariable("key") String key, RedirectAttributesModelMap modelMap) {
    //        Map<String,Object> map = new HashMap<String,Object>();
    //        map.put("userId",userIdSl);
    //        map.put("businessKey",business_key);
    //        String business_key = key + ":" + key;
            ActivitiVariable activitiVariable = new ActivitiVariable();
            int resul=activitiVariableService.insert(activitiVariable);
            if(resul==1){
                modelMap.addFlashAttribute("info","流程啓動成功!");
                String business_key = key+":"+activitiVariable.getId();
                activitiServiceImpl.startProcesses(key,business_key);
            }else{
                modelMap.addFlashAttribute("info","流程啓動失敗!");
            }
            return "redirect:/models/modelList";
        }
    
        //獲取受理員任務列表
        @RequestMapping("/queryTaskSl")
        public String findTasksForSL(ModelMap modelMap,FormData formData) {
            List<Task> lists = activitiServiceImpl.findTasksByUserId(formData.getUserId());
            modelMap.addAttribute("tasks",lists);
            modelMap.addAttribute("userId",formData.getUserId());
            return "model/taskList";
        }
    
    
        @RequestMapping("/form")
        public String form(FormData formData,ModelMap modelMap){
            Task task=activitiServiceImpl.findTaskById(formData.getId());
            modelMap.addAttribute("data",formData);
            modelMap.addAttribute("task",task);
            if(StringUtils.isNotEmpty(task.getFormKey())){
                return "activitiForm/"+task.getFormKey();
            }
            return "model/form";
        }
    
    
        //受理員受理數據
        @RequestMapping("/completeTaskSl")
        public String completeTasksForSL(ModelMap modelMap,FormData formData) {
            activitiServiceImpl.completeTask(formData.getId(), formData.getUserId(), formData.getAttr1());//受理後,任務列表數據減小
            return findTasksForSL(modelMap,formData);
        }
    
    //    //獲取審批員任務列表
    //    @RequestMapping("/queryTaskSp")
    //    public void findTasksForSP() {
    //        List<Task> lists = activitiServiceImpl.findTasksByUserId(userIdSp);
    //        System.out.println("任務列表:"+lists);//任務列表:[Task[id=220004, name=審批]]
    //    }
    //
    //    //審批員經過審覈
    //    @RequestMapping("/completeTaskSp/{id}")
    //    public void completeTasksForSP(@PathVariable("id")String id) {
    //        activitiServiceImpl.completeTask(id, userIdSp, "true");//審批後,任務列表數據減小
    //    }
    
    
        //設置流程變量
        //設置流程變量【基本類型】
        public void setTasksVar() {
            List<Task> lists = activitiServiceImpl.findTasksByUserId("sly1");
            for(Task task : lists) {//不知爲什麼,變量保存成功,但數據表只有請假天數含有任務id,單獲取流程變量時,根據任務id都可獲取到(以下一測試)
                taskService.setVariable(task.getId(), "請假人", "sly1");
                taskService.setVariableLocal(task.getId(), "請假天數",3);
                taskService.setVariable(task.getId(), "請假日期", new Date());
            }
        }
    
    //    //獲取流程變量
    //    @Test
    //    public void getTasksVar() {
    //        List<Task> lists = activitiServiceImpl.findTasksByUserId("sly1");
    //        for(Task task : lists) {
    //            //獲取流程變量【基本類型】
    //            String person = (String) taskService.getVariable(task.getId(), "請假人");
    //            Integer day = (Integer) taskService.getVariableLocal(task.getId(), "請假天數");
    //            Date date = (Date) taskService.getVariable(task.getId(), "請假日期");
    //
    //            System.out.println("流程變量:"+person+"||"+day+"||"+date+"||");
    //        }
    //    }
    //
    //    //設置流程變量【實體】
    //    @Test
    //    public void setTasksVarEntity() {
    //        List<Task> lists = activitiServiceImpl.findTasksByUserId("sly1");
    //        for(Task task : lists) {
    //            Person p = new Person();
    //            p.setName("翠花");
    //            p.setId(20);
    //            p.setDate();;
    //            p.setNote("回去探親,一塊兒吃個飯123");
    //            taskService.setVariable(task.getId(), "人員信息(添加固定版本)", p);
    //
    //            System.out.println("設置流程變量成功!");
    //        }
    //    }
    //
    //    //獲取流程變量【實體】  實體必須序列化
    //    @Test
    //    public void getTasksVarEntity() {
    //        List<Task> lists = activitiServiceImpl.findTasksByUserId("sly1");
    //        for(Task task : lists) {
    //            // 2.獲取流程變量,使用javaBean類型
    //            Person p = (Person)taskService.getVariable(task.getId(), "人員信息(添加固定版本)");
    //            System.out.println(" 請假人:  "+p.getName()+"  請假天數:  "+p.getId()+"   請假時間:"+ p.getDate()+ "   請假緣由: "+p.getNote());
    //        }
    //    }
    //
    //
    //    //生成流程圖---232501
    //    @RequestMapping("/queryProImg")
    //    public void queryProImg() throws Exception {
    //        activitiServiceImpl.queryProImg("232501");
    //    }
    
        //生成流程圖(高亮)---232501
        @RequestMapping("/queryProHighLighted")
        public @ResponseBody String queryProHighLighted(String proInsId) throws Exception {
            String imageByteArray = activitiServiceImpl.queryProHighLighted(proInsId);
            return imageByteArray;
        }
    //
    //    /**
    //     * 查詢流程變量的歷史表,能夠根據變量名稱查詢該變量的全部歷史信息
    //     */
    //    @Test
    //    public void findHistoryProcessVariables(){
    //        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery()//建立一個歷史的流程變量查詢對象
    //                .variableName("請假天數")
    //                .list();
    //        if (list!=null &&list.size()>0) {
    //            for (HistoricVariableInstance hvi : list) {
    //                System.out.println(hvi.getId()+"     "+hvi.getProcessInstanceId()+"   "+hvi.getVariableName()
    //                        +"   "+hvi.getVariableTypeName()+"    "+hvi.getValue());
    //                System.out.println("########################################");
    //            }
    //        }
    //
    //    }
    //
    //
    //    /**
    //     *  歷史流程實例查詢
    //     *  http://blog.csdn.net/luckyzhoustar/article/details/48652783
    //     */
    //    @Test
    //    public void findHistoricProcessInstance() {
    //        // 查詢已完成的流程
    //        List<HistoricProcessInstance> datas = historyService
    //                .createHistoricProcessInstanceQuery().finished().list();
    //        System.out.println("使用finished方法:" + datas.size());
    //        // 根據流程定義ID查詢
    //        datas = historyService.createHistoricProcessInstanceQuery()
    //                .processDefinitionId("processDefinitionId").list();
    //        System.out.println("使用processDefinitionId方法: " + datas.size());
    //        // 根據流程定義key(流程描述文件的process節點id屬性)查詢
    //        datas = historyService.createHistoricProcessInstanceQuery()
    //                .processDefinitionKey("processDefinitionKey").list();
    //        System.out.println("使用processDefinitionKey方法: " + datas.size());
    //        // 根據業務主鍵查詢
    //        datas = historyService.createHistoricProcessInstanceQuery()
    //                .processInstanceBusinessKey("processInstanceBusinessKey").list();
    //        System.out.println("使用processInstanceBusinessKey方法: " + datas.size());
    //        // 根據流程實例ID查詢
    //        datas = historyService.createHistoricProcessInstanceQuery()
    //                .processInstanceId("processInstanceId").list();
    //        System.out.println("使用processInstanceId方法: " + datas.size());
    //        // 查詢沒有完成的流程實例
    //        historyService.createHistoricProcessInstanceQuery().unfinished().list();
    //        System.out.println("使用unfinished方法: " + datas.size());
    //    }
    //
    //    /**
    //     *  歷史任務查詢
    //     * @throws ParseException
    //     */
    //    @Test
    //    public void findHistoricTasks() throws ParseException {
    //        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //
    //        //歷史數據查詢
    //        List<HistoricTaskInstance> datas = historyService.createHistoricTaskInstanceQuery()
    //                .finished().list();
    //        System.out.println("使用finished方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .processDefinitionId("processDefinitionId").list();
    //        System.out.println("使用processDefinitionId方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .processDefinitionKey("testProcess").list();
    //        System.out.println("使用processDefinitionKey方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .processDefinitionName("testProcess2").list();
    //        System.out.println("使用processDefinitionName方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .processFinished().list();
    //        System.out.println("使用processFinished方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .processInstanceId("processInstanceId").list();
    //        System.out.println("使用processInstanceId方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .processUnfinished().list();
    //        System.out.println("使用processUnfinished方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .taskAssignee("crazyit").list();
    //        System.out.println("使用taskAssignee方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .taskAssigneeLike("%zy%").list();
    //        System.out.println("使用taskAssigneeLike方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .taskDefinitionKey("usertask1").list();
    //        System.out.println("使用taskDefinitionKey方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .taskDueAfter(sdf.parse("2020-10-11 06:00:00")).list();
    //        System.out.println("使用taskDueAfter方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .taskDueBefore(sdf.parse("2022-10-11 06:00:00")).list();
    //        System.out.println("使用taskDueBefore方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .taskDueDate(sdf.parse("2020-10-11 06:00:00")).list();
    //        System.out.println("使用taskDueDate方法查詢:" + datas.size());
    //        datas = historyService.createHistoricTaskInstanceQuery()
    //                .unfinished().list();
    //        System.out.println("使用unfinished方法查詢:" + datas.size());
    //    }
    //    /**
    //     *  歷史行爲查詢
    //     *  流程在進行過程當中,往往走一個節點,都會記錄流程節點的信息,包括節點的id,名稱、類型、時間等,保存到ACT_HI_ACTINST表中。
    //     * @throws ParseException
    //     */
    //    @Test
    //    public void findHistoricActivityInstance() {
    //        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //
    //        //查詢數據
    //        List<HistoricActivityInstance> datas = historyService.createHistoricActivityInstanceQuery()
    //                .activityId("endevent1").list();
    //        System.out.println("使用activityId查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery()
    //                .activityInstanceId(datas.get(0).getId()).list();
    //        System.out.println("使用activityInstanceId查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery()
    //                .activityType("intermediateSignalCatch").list();
    //        System.out.println("使用activityType查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery()
    //                .executionId("executionId").list();
    //        System.out.println("使用executionId查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery().finished().list();
    //        System.out.println("使用finished查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery()
    //                .processInstanceId("processInstanceId").list();
    //        System.out.println("使用processInstanceId查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery()
    //                .taskAssignee("crazyit").list();
    //        System.out.println("使用taskAssignee查詢:" + datas.size());
    //        datas = historyService.createHistoricActivityInstanceQuery().unfinished().list();
    //        System.out.println("使用unfinished查詢:" + datas.size());
    //    }
    //
    //    /**
    //     *  歷史流程明細查詢
    //     *  在流程進行的過程當中,會產生許多明細數據,只有將History設置爲最高級別的時候,纔會被記錄到ACT_HI_DETAIL表中。
    //     * @throws ParseException
    //     */
    //    @Test
    //    public void findHistoricDetail() {
    //        // 查詢歷史行爲
    //        HistoricActivityInstance act = historyService.createHistoricActivityInstanceQuery()
    //                .activityName("First Task").finished().singleResult();
    //        List<HistoricDetail> datas = historyService.createHistoricDetailQuery()
    //                .activityInstanceId(act.getId()).list();
    //        System.out.println("使用activityInstanceId方法查詢:" + datas.size());
    //        datas = historyService.createHistoricDetailQuery().excludeTaskDetails().list();
    //        System.out.println("使用excludeTaskDetails方法查詢:" + datas.size());
    //        datas = historyService.createHistoricDetailQuery().formProperties().list();
    //        System.out.println("使用formProperties方法查詢:" + datas.size());
    //        datas = historyService.createHistoricDetailQuery().processInstanceId("processInstanceId").list();
    //        System.out.println("使用processInstanceId方法查詢:" + datas.size());
    //        datas = historyService.createHistoricDetailQuery().taskId("taskId").list();
    //        System.out.println("使用taskId方法查詢:" + datas.size());
    //        datas = historyService.createHistoricDetailQuery().variableUpdates().list();
    //        System.out.println("使用variableUpdates方法查詢:" + datas.size());
    //    }
    
    }
    View Code

    啓動流程、用戶任務task查詢、業務處理、流程進度查看等等,歷史流程查看功能沒有驗證過,這部分代碼是註釋掉的



    5、頁面實現
      在templates放置顯示頁面,可本身根據喜愛放置,我這裏主要是跟controller裏返回的路徑對應


      model文件夾下放的是模型列表頁和用戶任務列表頁,form.html已經廢棄,改成上面的activitiForm裏面的form頁面,不一樣的頁面對應不一樣流程節點


      
    list.html模型列表頁
    jquery

  • <!DOCTYPE html>
    <html lang="zh" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="/static/js/jquery.min.js"></script>
        <script th:inline="javascript">
            function deployment(obj){
                var id=obj.attributes['objectid'].nodeValue;
                $.ajax({
                    url:"/models/deployment/"+id,
                    type:"GET",
                    success:function(res){
                        alert(res);
                    }
                });
    
            }
    
            function deleteProcess(obj){
                var id=obj.attributes['objectid'].nodeValue;
                $.ajax({
                    url:"/models/delete/"+id,
                    type:"GET",
                    success:function(res){
                        alert(res);
                    }
    
                });
    
            }
    
            function start(obj){
                var id=obj.attributes['objectid'].nodeValue;
                $.ajax({
                    url:'/models/start/'+id,
                    type:'GET',
                    success:function(res){
                        alert(res);
                    }
                });
    
    
            }
    
            function taskList(){
                $("#taskList").submit();
            }
        </script>
    </head>
    <body>
    <div class="info"><span th:text="${info}"></span></div>
    <a href="/models/create">新建</a>
    <form id="taskList" action="/activiti/queryTaskSl">
    <div>
        <div style="float:left;width:50%;">
            <select id="userId" name="userId">
                <option value="userzhou">userzhou</option>
                <option value="usertest">usertest</option>
            </select>
        </div>
        <div style="width:50%;">
            <button onclick="taskList();">任務列表</button>
        </div>
    </div>
    </form>
    
    <table class="table">
        <thead>
        <tr>
            <th>ID</th>
            <th>模型名稱</th>
            <th>key</th>
            <th>版本</th>
            <th>部署ID</th>
            <th>建立時間</th>
            <th>最後更新時間</th>
            <th>操做</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="data : ${models}">
            <td th:text="${data.id}"></td>
            <td><a th:href="@{/static/modeler.html(modelId=${data.id})}" class="font-blue" th:text="${data.name}"></a>
            </td>
            <td th:text="${data.key}"></td>
            <td th:text="${data.version}"></td>
            <td th:text="${data.deploymentId}"></td>
            <td th:text="${data.createTime}"> 2018-02-25 17:28:35</td>
            <td th:text="${data.lastUpdateTime}"> 2018-02-25 17:28:35</td>
            <td><a href="javascript:void(0);" onclick="deployment(this);" th:attrappend="objectId=${data.id}" lass="font-blue deployBtn">發佈</a>
                <a th:href="${'/activiti/start/'+data.key}" th:attrappend="objectId=${data.id}" lass="font-blue deployBtn">發起流程</a>
                <a th:href="@{/crm/model/export/{modelId}(modelId=${data.id})}" class="font-blue">導出</a>
                <a href="javascript:void(0);" onclick="deleteProcess(this);" th:attrappend="objectId=${data.id}" class="font-blue delBtn">刪除</a></td>
        </tr>
        </tbody>
    </table>
    </body>
    </html>
    View Code



    taskList.html任務列表頁

    git

    <!DOCTYPE html>
    <html lang="zh" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="/static/js/jquery.min.js"></script>
    </head>
    <body>
    <a href="/models/modelList"><button>返回模型列表頁</button></a>
    <div>
        <span th:text="'用戶:'+${userId}"></span>
        <table>
            <thead>
            <tr>
                <th>ID</th>
                <th>任務名稱</th>
                <th>受理人</th>
                <th>任務ID</th>
                <th>流程ID</th>
                <th>建立時間</th>
                <th>操做</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="data : ${tasks}">
                <td th:text="${data.id}">LeaveBill:1:4</td>
                <td th:text="${data.name}"></td>
                <td th:text="${data.assignee}">1</td>
                <td th:text="${data.taskDefinitionKey}"></td>
                <td th:text="${data.processDefinitionId}"></td>
                <td th:text="${data.createTime}"> 2018-02-25 17:28:35</td>
                <td>
                    <a th:href="${'/activiti/form?id='+data.id+'&userId='+data.assignee}" th:attrappend="objectId=${data.id}" lass="font-blue deployBtn">處理</a>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
    </body>
    </html>
    View Code



    applyForm.html任務列表頁
    github

    <!DOCTYPE html>
    <html lang="zh" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>申請頁面</title>
        <script src="/static/js/jquery.min.js"></script>
        <style>
            .title{
                text-align: center;
            }
        </style>
        <script>
            function showProcessImg(processInstanceId) {
                $.ajax({
                    url:'/activiti/queryProHighLighted',
                    type:'POST',
                    data:{
                        proInsId:processInstanceId
                    },
                    success:function(res){
                        $("#processImg").attr('src',"data:image/png;base64,"+res);
                    }
                })
            }
            function returnBack(userId){
                var form = $("<form></form>");
                form.attr("action","/activiti/queryTaskSl");
                form.attr("method","post");
    
                var input = $("<input type='text' name='userId'/>")
                input.attr("value",userId);
                form.append(input);
    
                form.appendTo("body");
    
                form.css("display","none");
    
                form.submit();
    
            }
        </script>
    </head>
    <body>
    <div class="title">申請表單</div>
    <a th:onclick="'javascript:returnBack(\''+${data.userId}+'\');'"><button>返回用戶任務列表</button></a>
    <a href="javascript:void(0);" th:onclick="'javascript:showProcessImg(\''+${task.processInstanceId}+'\')'"><button>查看流程圖</button></a>
    <form action="/activiti/completeTaskSl">
        <input type="hidden" id="id" name="id" th:value="${data.id}"/>
        <input type="hidden" id="userId" name="userId" th:value="${data.userId}"/>
        <input type="text" id="attr1" name="attr1"/>
        <button type="submit">提交</button>
        <button>取消</button>
    </form>
    <img src="" id="processImg"/>
    </body>
    </html>
    View Code

      

    approveForm.html任務列表頁
    web

    <!DOCTYPE html>
    <html lang="zh" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>審批頁面</title>
        <script src="/static/js/jquery.min.js"></script>
        <style>
            .title{
                text-align: center;
            }
        </style>
        <script>
            function showProcessImg(processInstanceId) {
                $.ajax({
                    url:'/activiti/queryProHighLighted',
                    type:'POST',
                    data:{
                        proInsId:processInstanceId
                    },
                    success:function(res){
                        $("#processImg").attr('src',"data:image/png;base64,"+res);
                    }
                })
            }
    
            function returnBack(userId){
                var form = $("<form></form>");
                form.attr("action","/activiti/queryTaskSl");
                form.attr("method","post");
    
                var input = $("<input type='text' name='userId'/>")
                input.attr("value",userId);
                form.append(input);
    
                form.appendTo("body");
    
                form.css("display","none");
    
                form.submit();
    
            }
        </script>
    </head>
    <body>
    <div class="title">審批表單</div>
    <a th:onclick="'javascript:returnBack(\''+${data.userId}+'\');'"><button>返回任務列表</button></a>
    <a href="javascript:void(0);" th:onclick="'javascript:showProcessImg(\''+${task.processInstanceId}+'\')'"><button>查看流程圖</button></a>
    <form action="/activiti/completeTaskSl">
        <input type="hidden" id="id" name="id" th:value="${data.id}"/>
        <input type="hidden" id="userId" name="userId" th:value="${data.userId}"/>
        <input type="text" id="attr1" name="attr1"/>
        <button type="submit">提交</button>
        <button>取消</button>
    </form>
    <img src="" id="processImg"/>
    </body>
    </html>
    View Code


  • 運行界面以下
    list.html列表頁


    新建頁面



    這裏主要注意連個地方,assignments表示當前節點的處理人的id,我本身定義了兩個用戶userzhou和usertest,form key填寫處理當前節點所用的form表單,也就是以前的applyForm和approveForm








  • 須要注意的地方

    一、去除activiti的security校驗,修改application啓動類
     @SpringBootApplication(scanBasePackages = {"com.djkj","org.activiti"} , exclude = { org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class, org.activiti.spring.boot.SecurityAutoConfiguration.class,}) 
    這是按照網上的寫的,但仍是存在校驗,最後沒辦法在act_id_user裏面加了一條數據,你們有解決的辦法能夠在下方給我評論,謝謝。

    二、訪問靜態資源的時候,可能會被dispatcherservlet 給攔截掉,我是添加了個資源處理器,攔截static靜態資源

    package com.djkj.activiti.common;
    
    import groovy.util.logging.Slf4j;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    @Slf4j
    @EnableWebMvc
    public class StaticResourceConfig implements WebMvcConfigurer {
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        }
    
    }
    View Code

    三、關閉編輯器的時候能夠自定義返回的頁面
      修改editor-app\configuration下的toolbar-default-actions.js,有倆個地方closeEditor方法和$scope.saveAndClose方法,一個是關閉一個是保存並關閉,能夠修改放回路徑,對應的是controller裏的路徑

      closeEditor: function(services) {
                window.location.href = "/models/modelList";
            },
    
    $scope.saveAndClose = function () {
            $scope.save(function() {
                window.location.href = "/models/modelList";
            });
        };
相關文章
相關標籤/搜索