activiti 技术点整理

原文链接: https://my.oschina.net/u/3795437/blog/1806289

序言:近期业务涉及到各种奇葩的中国式审批,用activiti工作流来实现,网上教程坑太多,故写此文章总结备忘...

一、前期准备

1.maven依赖

5.21.0


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


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


    org.activiti
    activiti-explorer
    ${activiti.version}
    
        
            vaadin
            com.vaadin
        
        
            dcharts-widget
            org.vaadin.addons
        
        
            activiti-simple-workflow
            org.activiti
        
    


    org.activiti
    activiti-modeler
    ${activiti.version}


    org.activiti
    activiti-diagram-rest
    ${activiti.version}

 

二、定义流程

1.使用 activiti-modeler 绘制流程图(此插件集成方式请自行百度)

流程图创建参考此文章

activiti 技术点整理_第1张图片

2.部署流程

有多种方式(classpath、InputStream、字符串)部署,这里使用InputStream方式。

/**流程引擎(核心对象),默认加载类路径下命名为activiti.cfg.xml*/
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

public void deployementProcessDefinitionByInputStream() throws FileNotFoundException {
   //获取资源相对路径
   String bpmnPath = "diagrams/helloworld.bpmn";
   String pngPath = "diagrams/helloworld.png";

   //读取资源作为一个输入流
   FileInputStream bpmnfileInputStream = new FileInputStream(bpmnPath);
   FileInputStream pngfileInputStream = new FileInputStream(pngPath);

   Deployment deployment = processEngine.getRepositoryService()//获取流程定义和部署对象相关的Service
         .createDeployment()//创建部署对象
         .addInputStream("helloworld.bpmn",bpmnfileInputStream)
         .addInputStream("helloworld.png", pngfileInputStream)
         .deploy();//完成部署
   System.out.println("部署ID:"+deployment.getId());
   System.out.println("部署时间:"+deployment.getDeploymentTime());
}

3.使用流程

3.1 启动流程

/**
 * 启动流程
 *
 * @param procDefKey    流程定义KEY
 * @param businessTable 业务表表名
 * @param businessId    业务表编号
 * @param title         流程标题,显示在待办任务标题
 * @param vars          流程变量
 * @return 流程实例ID
 */
public String startProcess(String procDefKey, String businessTable, String businessId, String title, Map vars) {
    String userId = UserUtils.getUser().getId();

    // 用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中
    identityService.setAuthenticatedUserId(userId);

    // 设置流程变量
    if (vars == null) {
        vars = Maps.newHashMap();
    }

    // 设置流程标题
    if (StringUtils.isNotBlank(title)) {
        vars.put("title", title);
    }

    // 启动流程
    ProcessInstance procIns = runtimeService.startProcessInstanceByKey(procDefKey, businessTable + ":" + businessId, vars);

    // 更新业务表流程实例ID
    Act act = new Act();
    act.setBusinessTable(businessTable);// 业务表名
    act.setBusinessId(businessId);    // 业务表ID
    act.setProcInsId(procIns.getId());
    actDao.updateProcInsIdByBusinessId(act);
    return act.getProcInsId();
}

//启动流程后,将流程实例id (procInsId) 保存到业务表中

//dao层

   UPDATE ${businessTable} SET 
      proc_ins_id = #{procInsId}
   WHERE id = #{businessId}

3.2 流程变量

Map vars = Maps.newHashMap();
vars.put("pass", 1);//是否通过
...各种参数... 此处省略

3.3 流程是否结束

/**
 * 判断流程是否结束
 *
 * @param processInstanceId
 * @return
 */
public boolean isEnd(String processInstanceId) {
    ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
            .processInstanceId(processInstanceId)//使用流程实例ID查询
            .singleResult();
    if (pi == null) {
        return true;
    } else {
        return false;
    }
}

3.4 撤回流程(删除流程实例)

/**
 * 删除部署的流程实例
 * @param procInsId 流程实例ID
 * @param deleteReason 删除原因,可为空
 */
public void deleteProcIns(String procInsId, String deleteReason) {
   runtimeService.deleteProcessInstance(procInsId, deleteReason);
}

3.5 提交任务

/**
 * 提交任务, 并保存意见
 *
 * @param taskId    任务ID
 * @param procInsId 流程实例ID,如果为空,则不保存任务提交意见
 * @param comment   任务提交意见的内容
 * @param title     流程标题,显示在待办任务标题
 * @param vars      任务变量
 */
public void complete(String taskId, String procInsId, String comment, String title, Map vars) {
    // 添加意见
    if (StringUtils.isNotBlank(procInsId) && StringUtils.isNotBlank(comment)) {
        taskService.addComment(taskId, procInsId, comment);
    }

    // 设置流程变量
    if (vars == null) {
        vars = Maps.newHashMap();
    }

    // 设置流程标题
    if (StringUtils.isNotBlank(title)) {
        vars.put("title", title);
    }

    // 提交任务
    taskService.complete(taskId, vars);
}

注:1、通过 taskId 提交任务:complete只执行一次,再次执行时,该任务已不存在,则无法再次提交,提交失败
       2、任务已签收时,处理人与签收人需操持一致,否则任务提交失败

3.6 签收任务

/**
 * 签收任务
 *
 * @param taskId 任务ID
 * @param userId 签收用户ID(用户登录名)
 */
public void claim(String taskId, String userId) {
    taskService.claim(taskId, userId);
}

 

4.设计流程

4.1 用户任务

用户任务,即需要人来参与的任务,具体设计如下:

说明:activiti:assignee:设置任务处理人,指向流程变量“user"

流程变量中放入用户id即可
Map vars = Maps.newHashMap();
vars.put("user", "userIdxxxxx");

4.2 会签任务

4.2.1 并行任务办理

同一任务多人处理,且所有人处理完成后,流程再跳转到下一环节,具体设计如下:

在userTask节点内,添加multiInstanceLoopCharacteristics节点


    

说明:1、activiti:collection:设置用户集合,指向流程变量"users"(java中传入list数组)
           2、activiti:elementVariable:设置遍历用户集合的单个对象变量名称,注:勿使用流程变量中的常用变更名 
           3、activiti:assignee:根据 activiti:elementVariable 中的变量名 设置任务的 办理人
           4、isSequential = "false",表示任务实例并行执行,“true“,表示顺序执行(一个接一个)

4.2.2 一票否决

用户集合中,有一人不同意,则退出会签,流程实例进入下一环节


    
        
    

说明:1、在multiInstanceLoopCharacteristics节点中,增加completionCondition节点,设置会签退出的条件
           2、当流程变量pass 值为 0 时,退出会签环节,流程实例跳转到下一环节

4.2.3 优先处理

用户集合中,达到 指定比例 处理后,则退出会签,流程实例进入下一环节


  
    ${nrOfCompletedInstances/nrOfInstances >= 0.6 }
  

4.2.4 特殊业务

需要用到监听器(暂略)...

5. 获取待办/已办事项

5.1 获取待办

@Autowired
private TaskService taskService;
/**
 * 获取待办列表
 * 

* procDefKey 流程定义标识 * * @return */ public List todoList(Act act) { String userId = UserUtils.getUser().getId(); List result = new ArrayList(); // =============== 已经签收的任务 =============== TaskQuery todoTaskQuery = taskService.createTaskQuery().taskAssignee(userId).active() .includeProcessVariables().orderByTaskCreateTime().desc(); // 设置查询条件 if (StringUtils.isNotBlank(act.getProcDefKey())) { todoTaskQuery.processDefinitionKey(act.getProcDefKey()); }         //开始时间 if (act.getBeginDate() != null) { todoTaskQuery.taskCreatedAfter(act.getBeginDate()); }     //结束时间 if (act.getEndDate() != null) { todoTaskQuery.taskCreatedBefore(act.getEndDate()); } // 查询列表 List todoList = todoTaskQuery.list(); for (Task task : todoList) { Act e = new Act(); e.setTask(task); e.setVars(task.getProcessVariables()); e.setProcDef(ProcessDefCache.get(task.getProcessDefinitionId())); e.setStatus("todo"); result.add(e); } // =============== 等待签收的任务 =============== TaskQuery toClaimQuery = taskService.createTaskQuery().taskCandidateUser(userId) .includeProcessVariables().active().orderByTaskCreateTime().desc(); // 设置查询条件 if (StringUtils.isNotBlank(act.getProcDefKey())) { toClaimQuery.processDefinitionKey(act.getProcDefKey()); }     //开始时间 if (act.getBeginDate() != null) { toClaimQuery.taskCreatedAfter(act.getBeginDate()); }     //结束时间 if (act.getEndDate() != null) { toClaimQuery.taskCreatedBefore(act.getEndDate()); } // 查询列表 List toClaimList = toClaimQuery.list(); for (Task task : toClaimList) { Act e = new Act(); e.setTask(task); e.setVars(task.getProcessVariables()); e.setProcDef(ProcessDefCache.get(task.getProcessDefinitionId())); e.setStatus("claim"); result.add(e); } return result; }

5.2已办任务

/**
 * 获取已办任务
 *
 * @param page procDefKey 流程定义标识
 * @return
 */
public List historicList(Page page, Act act) {
    String userId = UserUtils.getUser().getId();

    HistoricTaskInstanceQuery histTaskQuery = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished()
            .includeProcessVariables().orderByHistoricTaskInstanceEndTime().desc();


    // 设置查询条件
    if (StringUtils.isNotBlank(act.getProcDefKey())) {
        histTaskQuery.processDefinitionKey(act.getProcDefKey());
    }
    if (act.getBeginDate() != null) {
        histTaskQuery.taskCompletedAfter(act.getBeginDate());
    }
    if (act.getEndDate() != null) {
        histTaskQuery.taskCompletedBefore(act.getEndDate());
    }

    // 查询列表
    List histList = histTaskQuery.listPage(page.getFirstResult(), page.getMaxResults());
    //处理分页问题
    List actList = Lists.newArrayList();
    for (HistoricTaskInstance histTask : histList) {
        Act e = new Act();
        e.setHistTask(histTask);
        e.setVars(histTask.getProcessVariables());
        //e.setProcDef(ProcessDefCache.get(histTask.getProcessDefinitionId()));
        e.setStatus("finish");
        actList.add(e);
    }
    return actList;
}

 

6. 流程图查看

生成png格式图片

public void graphHistoryProcessInstance(String processInstanceId, HttpServletResponse response) throws Exception {
    Command cmd = new HistoryProcessInstanceDiagramCmd(processInstanceId);
    InputStream is = managementService.executeCommand(cmd);
    response.setContentType("image/png");
    int len = 0;
    byte[] b = new byte[1024];

    while ((len = is.read(b, 0, 1024)) != -1) {
        response.getOutputStream().write(b, 0, len);
    }
    is.close();
}

7. 流转历史查看

 

/**
 * 获取流转历史列表
 *
 * @param procInsId 流程实例
 * @param startAct  开始活动节点名称
 * @param endAct    结束活动节点名称
 */
public List histoicFlowList(String procInsId, String startAct, String endAct) {
    List actList = Lists.newArrayList();
    List list = historyService.createHistoricActivityInstanceQuery().processInstanceId(procInsId)
            .orderByHistoricActivityInstanceStartTime().asc().orderByHistoricActivityInstanceEndTime().asc().list();
    boolean start = false;
    Map actMap = Maps.newHashMap();
    for (int i = 0; i < list.size(); i++) {
        HistoricActivityInstance histIns = list.get(i);
        // 过滤开始节点前的节点
        if (StringUtils.isNotBlank(startAct) && startAct.equals(histIns.getActivityId())) {
            start = true;
        }
        if (StringUtils.isNotBlank(startAct) && !start) {
            continue;
        }

        // 只显示开始节点和结束节点,并且执行环节不为空的任务
        if (StringUtils.isNotBlank(histIns.getActivityName()) || "startEvent".equals(histIns.getActivityType()) || "endEvent".equals(histIns.getActivityType())) {
            // 给节点增加一个序号
            Integer actNum = actMap.get(histIns.getActivityId());
            if (actNum == null) {
                actMap.put(histIns.getActivityId(), actMap.size());
            }

            Act e = new Act();
            e.setHistIns(histIns);
            // 获取流程发起人名称
            if ("startEvent".equals(histIns.getActivityType())) {
                List il = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId).orderByProcessInstanceStartTime().asc().list();
                if (il.size() > 0) {
                    if (StringUtils.isNotBlank(il.get(0).getStartUserId())) {
                        User user = UserUtils.get(il.get(0).getStartUserId());
                        if (user != null) {
                            e.setAssignee(histIns.getAssignee());
                            e.setAssigneeName(user.getName());
                        }
                    }
                }
            }
            // 获取任务执行人名称
            if (StringUtils.isNotEmpty(histIns.getAssignee())) {
                User user = UserUtils.get(histIns.getAssignee());
                if (user != null) {
                    e.setAssignee(histIns.getAssignee());
                    e.setAssigneeName(user.getName());
                }
            }
            // 获取意见评论内容
            if (StringUtils.isNotBlank(histIns.getTaskId())) {
                List commentList = taskService.getTaskComments(histIns.getTaskId());
                if (commentList.size() > 0) {
                    e.setComment(commentList.get(0).getFullMessage());
                }
            }
            actList.add(e);
        }

        // 过滤结束节点后的节点
        if (StringUtils.isNotBlank(endAct) && endAct.equals(histIns.getActivityId())) {
            boolean bl = false;
            Integer actNum = actMap.get(histIns.getActivityId());
            // 该活动节点,后续节点是否在结束节点之前,在后续节点中是否存在
            for (int j = i + 1; j < list.size(); j++) {
                HistoricActivityInstance hi = list.get(j);
                Integer actNumA = actMap.get(hi.getActivityId());
                if ((actNumA != null && actNumA < actNum) || StringUtils.equals(hi.getActivityId(), histIns.getActivityId())) {
                    bl = true;
                }
            }
            if (!bl) {
                break;
            }
        }
    }
    return actList;
}

 

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.*.common.persistence.BaseEntity;
import com.*.common.utils.StringUtils;
import com.*.common.utils.TimeUtils;
import com.*.modules.act.utils.Variable;

/**
 * 工作流Entity
 *
 */
public class Act extends BaseEntity {

    private static final long serialVersionUID = 1L;

    private String taskId;        // 任务编号
    private String taskName;    // 任务名称
    private String taskDefKey;    // 任务定义Key(任务环节标识)

    private String procInsId;    // 流程实例ID
    private String procDefId;    // 流程定义ID
    private String procDefKey;    // 流程定义Key(流程定义标识)

    private String businessTable;    // 业务绑定Table
    private String businessId;        // 业务绑定ID

    private String title;        // 任务标题

    private String status;        // 任务状态(todo/claim/finish)

    // private String procExecUrl;    // 流程执行(办理)RUL
    private String comment;    // 任务意见
    private String flag;        // 意见状态

    private Task task;            // 任务对象
    private ProcessDefinition procDef;    // 流程定义对象
    private ProcessInstance procIns;    // 流程实例对象
    private HistoricTaskInstance histTask; // 历史任务
    private HistoricActivityInstance histIns;    //历史活动任务

    private String assignee; // 任务执行人编号
    private String assigneeName; // 任务执行人名称

    private Variable vars;        // 流程变量
// private Variable taskVars;     // 流程任务变量

    private Date beginDate;    // 开始查询日期
    private Date endDate;    // 结束查询日期

    private List list; // 任务列表

    private int isView;//是否是查看已办任务


    public Act() {
        super();
    }

    public String getTaskId() {
        if (taskId == null && task != null) {
            taskId = task.getId();
        }
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    public String getTaskName() {
        if (taskName == null && task != null) {
            taskName = task.getName();
        }
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public String getTaskDefKey() {
        if (taskDefKey == null && task != null) {
            taskDefKey = task.getTaskDefinitionKey();
        }
        return taskDefKey;
    }

    public void setTaskDefKey(String taskDefKey) {
        this.taskDefKey = taskDefKey;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getTaskCreateDate() {
        if (task != null) {
            return task.getCreateTime();
        }
        return null;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getTaskEndDate() {
        if (histTask != null) {
            return histTask.getEndTime();
        }
        return null;
    }

    @JsonIgnore
    public Task getTask() {
        return task;
    }

    public void setTask(Task task) {
        this.task = task;
    }

    @JsonIgnore
    public ProcessDefinition getProcDef() {
        return procDef;
    }

    public void setProcDef(ProcessDefinition procDef) {
        this.procDef = procDef;
    }

    public String getProcDefName() {
        return procDef.getName();
    }

    @JsonIgnore
    public ProcessInstance getProcIns() {
        return procIns;
    }

    public void setProcIns(ProcessInstance procIns) {
        this.procIns = procIns;
        if (procIns != null && procIns.getBusinessKey() != null) {
            String[] ss = procIns.getBusinessKey().split(":");
            setBusinessTable(ss[0]);
            setBusinessId(ss[1]);
        }
    }


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    @JsonIgnore
    public HistoricTaskInstance getHistTask() {
        return histTask;
    }

    public void setHistTask(HistoricTaskInstance histTask) {
        this.histTask = histTask;
    }

    @JsonIgnore
    public HistoricActivityInstance getHistIns() {
        return histIns;
    }

    public void setHistIns(HistoricActivityInstance histIns) {
        this.histIns = histIns;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getBeginDate() {
        return beginDate;
    }

    public void setBeginDate(Date beginDate) {
        this.beginDate = beginDate;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getEndDate() {
        return endDate;
    }

    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public String getFlag() {
        return flag;
    }

    public void setFlag(String flag) {
        this.flag = flag;
    }

    public String getProcDefId() {
        if (procDefId == null && task != null) {
            procDefId = task.getProcessDefinitionId();
        }
        return procDefId;
    }

    public void setProcDefId(String procDefId) {
        this.procDefId = procDefId;
    }

    public String getProcInsId() {
        if (procInsId == null && task != null) {
            procInsId = task.getProcessInstanceId();
        }
        return procInsId;
    }

    public void setProcInsId(String procInsId) {
        this.procInsId = procInsId;
    }

    public String getBusinessId() {
        return businessId;
    }

    public void setBusinessId(String businessId) {
        this.businessId = businessId;
    }

    public String getBusinessTable() {
        return businessTable;
    }

    public void setBusinessTable(String businessTable) {
        this.businessTable = businessTable;
    }

    public String getAssigneeName() {
        return assigneeName;
    }

    public void setAssigneeName(String assigneeName) {
        this.assigneeName = assigneeName;
    }

    public String getAssignee() {
        if (assignee == null && task != null) {
            assignee = task.getAssignee();
        }
        return assignee;
    }

    public void setAssignee(String assignee) {
        this.assignee = assignee;
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }

    public Variable getVars() {
        return vars;
    }

    public void setVars(Variable vars) {
        this.vars = vars;
    }

    /**
     * 通过Map设置流程变量值
     *
     * @param map
     */
    public void setVars(Map map) {
        this.vars = new Variable(map);
    }
    

    /**
     * 获取流程定义KEY
     *
     * @return
     */
    public String getProcDefKey() {
        if (StringUtils.isBlank(procDefKey) && StringUtils.isNotBlank(procDefId)) {
            procDefKey = StringUtils.split(procDefId, ":")[0];
        }
        return procDefKey;
    }

    public void setProcDefKey(String procDefKey) {
        this.procDefKey = procDefKey;
    }

    /**
     * 获取过去的任务历时
     *
     * @return
     */
    public String getDurationTime() {
        if (histIns != null && histIns.getDurationInMillis() != null) {
            return TimeUtils.toTimeString(histIns.getDurationInMillis());
        }
        return "";
    }

    /**
     * 是否是一个待办任务
     *
     * @return
     */
    public boolean isTodoTask() {
        return "todo".equals(status) || "claim".equals(status);
    }

    /**
     * 是否是已完成任务
     *
     * @return
     */
    public boolean isFinishTask() {
        return "finish".equals(status) || StringUtils.isBlank(taskId);
    }

    @Override
    public void preInsert() {

    }

    @Override
    public void preUpdate() {

    }

    public int getIsView() {
        return isView;
    }

    public void setIsView(int isView) {
        this.isView = isView;
    }
}

转载于:https://my.oschina.net/u/3795437/blog/1806289

你可能感兴趣的:(activiti 技术点整理)