1. 概述

流程任务是流程平台让用户参与审批的环节,基于bpmn的配置中,其节点的类型为人工任务,该任务会产生待办事项推送到用户的审批事项中心。平台通过关联任务的单据,实现流程与业务的关联处理。

平台通过任务解决如下内容:

  1. 任务关联单据与字段及数据权限
  2. 任务审批人
  3. 任务办理,包括同意,反对,加签,抄送,追回,转办,沟通,任务回退(上一步,驳回发起人)
  4. 任务事件,包括(创建事件、结束事件)
  5. 任务办理的外围触发动作切入口

2. 表设计

平台基于activiti中增加一些表设计,包括:

2.1. bpm_task 任务表

是否主键 字段名 字段描述 数据类型 长度 可空 备注
TASK_ID_ 任务ID VARCHAR(64) 64   任务ID
  ACT_TASK_ID_ 流程任务ID VARCHAR(64) 64 流程任务ID
  TREE_ID_ 分类ID VARCHAR(64) 64 分类ID
  NAME_ 任务名称 VARCHAR(100) 100 任务名称
  KEY_ 任务Key VARCHAR(64) 64 任务Key
  BILL_TYPE_ 流程类型 VARCHAR(64) 64 流程类型
  BILL_NO_ 流程单号 VARCHAR(64) 64 流程单号
  BUS_KEY_ 业务主键 VARCHAR(64) 64 业务主键
  DESCP_ 任务描述 VARCHAR(255) 255 任务描述
  SUBJECT_ 事项标题 VARCHAR(512) 512 事项标题
  OWNER_ 任务所属人 VARCHAR(64) 64 任务所属人
  ASSIGNEE_ 任务执行人 VARCHAR(64) 64 任务执行人
  ACT_INST_ID_ 流程实例ID VARCHAR(64) 64 流程实例ID
  ACT_DEF_ID_ ACT流程定义ID VARCHAR(64) 64 ACT流程定义ID
  DEF_ID_ 流程定义ID VARCHAR(64) 64 流程定义ID
  INST_ID_ 流程扩展实例ID VARCHAR(64) 64 流程扩展实例ID
  STATUS_ 任务状态 VARCHAR(64) 64 任务状态
  PRIORITY_ 任务优先级 VARCHAR(64) 64 任务优先级
  EXPIRED_TIME_ 任务过期时间 DATETIME   任务过期时间
  FORWARD_TIMES_ 转发次数 INT   转发次数
  TASK_TYPE_ 任务类型 VARCHAR(64) 64 任务类型,FLOW_TASK=流程任务,MAN_TASK=人工任务(创建)
  PARENT_ID_ 父任务ID VARCHAR(64) 64 父任务ID
  FROM_TASK_KEY_ 上一任务KEY VARCHAR(255) 255 上一任务KEY
  FROM_TASK_NAME_ 上一任务名称 VARCHAR(255) 255 上一任务名称
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

【说明】

  • activiti原生有一actru_task表,但实际流程业务中存在一些非activiti的流程任务,因此平台扩展一表 bpm_task表,其中映射到act_ru_task表中,通过act_task_id关联。

  • 非Activiti的任务,其不存在acttask_id,同时任务类型为MAN_TASK.

2.2. bpm_task_user 任务用户表

bpm_task_user表为流程任务的用户关系表,对于任务的候选用户或组则存于此表

是否主键 字段名 字段描述 数据类型 长度 可空 备注
ID_ 主键 VARCHAR(64) 64   主键
  TASK_ID_ 任务ID VARCHAR(64) 64   任务ID
  USER_ID_ 用户ID VARCHAR(64) 64 用户ID
  GROUP_ID_ 用户组ID VARCHAR(64) 64 用户组ID
  USER_TYPE_ 用户类型 VARCHAR(64) 64 用户类型,USER=用户,GROUP=用户组
  PART_TYPE_ 参与类型 VARCHAR(64) 64 参与类型,执行人=ASSIGNEE,抄送人=COPY,候选人=CANDIDATE
  IS_READ_ 是否已读 VARCHAR(20) 20 是否已读, YES,NO
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

2.3. bpm_task_transfer 流程任务转移记录

是否主键 字段名 字段描述 数据类型 长度 可空 备注
ID_ 主键 VARCHAR(64) 64   主键
  TREE_ID_ 分类ID VARCHAR(64) 64 分类ID
  INST_ID_ 流程实例ID VARCHAR(64) 64 流程实例ID
  OWNER_ID_ 所有人ID VARCHAR(64) 64 所有人ID
  SUBJECT_ 任务标题 VARCHAR(128) 128 任务标题
  TASK_ID_ 任务ID VARCHAR(64) 64 任务ID
  TO_USER_ID_ 转办人ID VARCHAR(64) 64 转办人ID
  TYPE_ 类型 VARCHAR(20) 20 类型(trans,agent)
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

2.4. bpm_sign_data 流程任务会签数据

是否主键 字段名 字段描述 数据类型 长度 可空 备注
DATA_ID_ 主键 VARCHAR(64) 64   主键
  ACT_DEF_ID_ 流程定义ID VARCHAR(64) 64 流程定义ID
  ACT_INST_ID_ 流程实例ID VARCHAR(64) 64 流程实例ID
  NODE_ID_ 节点ID VARCHAR(64) 64 节点ID
  USER_ID_ 用户ID VARCHAR(64) 64 用户ID
  VOTE_STATUS_ 投票意见 VARCHAR(40) 40 投票意见
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

2.5. bpm_ru_path 流程节点运行路径

bpm_ru_path记录了流程实例在运行的过程中的每个节点执行过程,通过该记录可以获取任务的回退路径等信息。

是否主键 字段名 字段描述 数据类型 长度 可空 备注
PATHID 主键 VARCHAR(64) 64   主键
  INST_ID_ 流程实例ID VARCHAR(64) 64   流程实例ID
  DEF_ID_ 流程定义Id VARCHAR(64) 64  
  ACT_DEF_ID_ Act定义ID VARCHAR(64) 64   Act定义ID
  ACT_INST_ID_ Act实例ID VARCHAR(64) 64   Act实例ID
  NODE_ID_ 节点ID VARCHAR(255) 255   节点ID
  NODE_NAME_ 节点名称 VARCHAR(255) 255 节点名称
  NODE_TYPE_ 节点类型 VARCHAR(50) 50 节点类型
  START_TIME_ 开始时间 DATETIME     开始时间
  END_TIME_ 结束时间 DATETIME   结束时间
  ASSIGNEE_ 处理人ID VARCHAR(64) 64 处理人ID
  TO_USER_ID_ 代理人ID VARCHAR(64) 64 代理人ID
  USER_IDS_ 原执行人IDS VARCHAR(300) 300 原执行人IDS
  MULTIPLE_TYPE_ 是否为多实例 VARCHAR(20) 20 是否为多实例
  EXECUTION_ID_ 活动执行ID VARCHAR(64) 64 活动执行ID
  PARENT_ID_ 父ID VARCHAR(64) 64 父ID
  LEVEL_ 层次 INT   层次
  OUT_TRAN_ID_ 跳出路线ID VARCHAR(255) 255 跳出路线ID
  TOKEN_ 路线令牌 VARCHAR(255) 255 路线令牌
  JUMP_TYPE_ 跳转方式 VARCHAR(50) 50 跳到该节点的方式,正常跳转,自由跳转,回退跳转
  NEXT_JUMP_TYPE_ 下一步跳转方式 VARCHAR(50) 50 下一步跳转方式
  REF_PATH_ID_ 引用路径ID VARCHAR(64) 64 引用路径ID,当回退时,重新生成的结点,需要记录引用的回退节点,方便新生成的路径再次回退。
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

3. 类设计

类说明:

  1. BpmTaskController 流程任务对外请求服务控制类
  2. BpmTask 流程任务扩展业务实体
  3. BpmTaskService 流程任务逻辑服务类
  4. BpmTaskMapper 流程任务数据库映射类
  5. BpmInstServiceImpl 流程实例业务逻辑服务类
  6. BpmTaskUserServiceImpl 流程任务人员业务逻辑服务类
  7. TaskService Activiti原生的任务服务类
  8. BpmDefService 流程定义业务逻辑服务类
  9. BpmCheckHistoryServiceImpl 审批历史业务逻辑服务类
  10. BpmRuPathServiceImpl 流程执行路径业务逻辑服务类
  11. ActRepService Activiti库操作服务类
  12. BpmInstLogServiceImpl 流程实例操作日志服务类
  13. BpmSignDataServiceImpl 流程会签数据业务逻辑服务类
  14. FormDataService 单据数据服务类
  15. BpmTaskSkipService 流程任务跳过服务类
  16. GroovyEngine 脚本引擎类
  17. ProcessHandlerExecutor 流程前置与后置处理器执行类
  18. BpmTaskTransferServiceImpl 流程任务转移业务逻辑处理类
  19. BpmMultiTaskService 流程多实体任务业务逻辑服务类
  20. IOrgService 组织架构服务类
  21. BpmTaskUserMapper 流程任务映射服务类
  22. RuntimeService Activiti运行服务类
  23. TaskExecutorService 流程任务执行人的业务逻辑服务类

4. 任务关联单据

任务关联的单据的数据来源来自流程定义中的单据配置,可参考7.3.1.的流程定义扩展一节点的单据扩展部分,当用户进入任务审批窗口时,只需要查询到当前节点的配置,可通过以下接口获取:

  1. UserTaskConfig config = (UserTaskConfig) bpmDefService.getNodeConfig(bpmTask.getActDefId(), bpmTask.getKey());

UserTaskConfig中可获取FormConfig,以支持获取不同的流程任务节点参数。

5.前端单据加载

  1. <rx-forms ref="rxForms" />
  1. BpmtaskApi.getAllDetail(this.taskId).then(res=>{
  2. this.bpmTaskDetail=res;
  3. Object.assign(self,res);
  4. var contextData={
  5. type:"usetask",
  6. curUserId:res.curUser.userId,
  7. curUserName:res.curUser.fullName,
  8. nodeId:self.bpmTask.key,
  9. nodeName:self.bpmTask.name,
  10. instId:self.instId,
  11. defId:self.bpmInst.defId,
  12. instNo:self.bpmInst.billNo,
  13. taskId:self.taskId,
  14. opinionHistorys:res.bpmCheckHistories
  15. };
  16. self.$refs.rxForms.setData(res.formData.data,false,contextData);
  17. });

6. 任务用例处理

6.1. 办理人、所属人、候选人

任务审批人是指任务办理人,流程任务在创建过程中,会根据以下几种情况找到对应的任务审批人,并且把任务分配给任务人进行办理:

  • 流程节点配置的人员
  • 上一步指定下一步的执行人员
  • 回退时找回之前审批人

流程所属人任务在创建过程中,默认只分给一个人时,这个用户即为任务的所属人,同时也是这个任务的执行人,若一个任务同时分配给多个人时,这些用户都能看到该任务,并且可以抢占这些任务进行办理,我们称这批用户为候选用户。

若一个任务A 所属人是张三、执行人也是张三,但通过转办,可把任务转给李四,但任务所属人还是张三。李四在待办事项中看到该任务。张三可查看到转出去的任务的办理情况。

6.2. 我的待办事项查询

查询我的待办事项时,需要查询关联bpm_task与bpm_task_user表,为了查询的效率,可以先把用户所属的用户组查询出来,然后传入至以下关联查询SQL中即可。

  1. /**
  2. * 按条件查询所有的个人待办
  3. * @return
  4. * @throws Exception
  5. */
  6. @ApiOperation(value="按条件查询所有的个人待办", notes="按条件查询所有的个人待办")
  7. @PostMapping(value="/myTasks")
  8. public JsonPageResult myTasks(@RequestBody QueryData queryData) throws Exception{
  9. JsonPageResult result=JsonPageResult.getSuccess("");
  10. QueryFilter filter= QueryFilterBuilder.createQueryFilter(queryData);
  11. handleFilter(filter);
  12. IUser curUser=ContextUtil.getCurrentUser();
  13. String userId=curUser.getUserId();
  14. List<String> groupIds=curUser.getRoles();
  15. IPage<BpmTask> page= bpmTaskService.getByUserId(userId,groupIds,filter);
  16. result.setPageData(page);
  17. List<BpmTask> list=page.getRecords();
  18. //在任务中显示任务执行人与候选用户
  19. for(BpmTask task:list){
  20. Set<TaskExecutor> executors=bpmTaskUserService.getTaskExecutors(task);
  21. task.setTaskExecutors(executors);
  22. }
  23. return result;
  24. }
  1. <select id="getByUserId" resultType="com.redxun.bpm.core.entity.BpmTask" parameterType="java.util.Map">
  2. select v.* from (
  3. select t.* from BPM_TASK t left join bpm_def d on t.def_id_= d.def_id_ where ASSIGNEE_=#{userId} and t.STATUS_!='COMPLETED' and t.STATUS_!='LOCKED'
  4. UNION
  5. select t.* from bpm_task t left join bpm_def d on t.def_id_= d.def_id_ left join bpm_task_user u on
  6. t.TASK_ID_=u.TASK_ID_
  7. where t.ASSIGNEE_ is null AND t.STATUS_!='COMPLETED' and t.STATUS_!='LOCKED' and (u.USER_ID_=#{userId}
  8. <if test="@rx.Ognl@isNotEmpty(groupIds)">or u.GROUP_ID_ in
  9. <foreach collection="groupIds" item="id" separator="," open="(" close=")">#{id}</foreach>
  10. </if>
  11. )
  12. ) v where 1=1
  13. <if test="@rx.Ognl@isNotEmpty(w.whereSql)">
  14. and ${w.whereSql}
  15. </if>
  16. <if test="@rx.Ognl@isNotEmpty(w.orderBySql)">
  17. ORDER BY ${w.orderBySql}
  18. </if>
  19. </select>

6.3. 任务审批人配置与计算

不同流程不同节点的任务审批其审批节点不一样,平台提供不同的人员查找策略,用来支撑不同的人员查找,并且分配给任务,生成任务所属人、执行人与候选人。

6.3.1. 流程任务级的节点配置

  1. "userConfigs": [
  2. {
  3. "uid": "QXXDHCL4IQC1R1WKY6ZZ39WHIP6UIQ8B",
  4. "condition": "",
  5. "name": "人员配置",
  6. "configList": [
  7. {
  8. "display": "发起人",
  9. "calcType": "yes",
  10. "logic": "or",
  11. "type": "starter",
  12. "config": "发起人",
  13. "currentComponet": "starterEdit"
  14. },
  15. {
  16. "display": "郭靖,黄蓉",
  17. "calcType": "yes",
  18. "logic": "or",
  19. "type": "user",
  20. "config": "1273526648723415041,1273526443793915905",
  21. "currentComponet": "userEdit"
  22. },
  23. {
  24. "display": "财务部,研发部",
  25. "calcType": "yes",
  26. "logic": "or",
  27. "type": "group",
  28. "config": "1273526190164353025,1273526098128740353",
  29. "currentComponet": "groupEdit"
  30. }
  31. ]
  32. }
  33. ],

6.3.2. 流程任务人员计算实现接口

平台只需要实现接口ITaskExecutorCalc,该接口只有两方法,如下:

  1. import com.redxun.bpm.activiti.config.UserConfig;
  2. import com.redxun.dto.bpm.TaskExecutor;
  3. import java.util.Collection;
  4. import java.util.Map;
  5. /**
  6. * 流程任务节点计算实现类
  7. * @author csx
  8. */
  9. public interface ITaskExecutorCalc {
  10. ExecutorType getType();
  11. /**
  12. * 计算用的执行人接口
  13. * @param userConfig 当前节点的人员配置
  14. * @param vars 流程变量
  15. * @return
  16. */
  17. Collection<TaskExecutor> getExecutors(UserConfig userConfig, Map<String,Object> vars);
  18. }

其中TaskExecutor为任务执行者,代表用户或组,一般可通过OsUserDto或OsGroupDto实现转化处理。

说明:

  • FormJsonExecutorCalc 执行人来自来自表单数据
  • GroupByUserGroupRelExexecutorCalc 用户组来自用户与组关系运算
  • GroupExecutorCalc 执行人来自用户组
  • GroupPropertiesExecutorCalc 用户组来自扩展属性计算
  • GroupRankTypeRelEexecutorCalc 用户来自发起人所在部门往上查找符合等级的部门的关系用户
  • GroupScriptExecutorCalc 用户组来自人员脚本运算
  • PreNodeUserExecutorCalc 用户来自其他节点的审批人
  • StartUserExecutorCalc 执行人来自发起人
  • UserByUserGroupRelExecutorCalc 用户来自用户与组关系运算
  • UserExecutorCalc 执行人来自用户
  • UserPropertiesExecutorCalc 执行人来自
  • UserRelExecutorCalc 执行人来自用户关系运行
  • VarExecutorCalc 执行人来自流程变量

其他人员的查找策略,可具体实现可参考以上一扩展实现,如:

  1. package com.redxun.bpm.activiti.user.impl;
  2. import com.redxun.bpm.activiti.config.UserConfig;
  3. import com.redxun.bpm.activiti.user.ExecutorType;
  4. import com.redxun.bpm.activiti.user.ITaskExecutorCalc;
  5. import com.redxun.dto.bpm.TaskExecutor;
  6. import com.redxun.dto.user.OsGroupDto;
  7. import com.redxun.dto.user.OsRelInstDto;
  8. import com.redxun.dto.user.OsUserDto;
  9. import com.redxun.feign.org.OrgClient;
  10. import net.sf.json.JSONObject;
  11. import org.apache.commons.lang.StringUtils;
  12. import org.springframework.stereotype.Component;
  13. import javax.annotation.Resource;
  14. import java.util.*;
  15. @Component
  16. public class GroupByUserGroupRelExecutorCalc implements ITaskExecutorCalc {
  17. @Resource
  18. OrgClient orgClient;
  19. @Override
  20. public ExecutorType getType() {
  21. return new ExecutorType("groupByUserGroupRel","用户组来自用户与组关系运算",11);
  22. }
  23. @Override
  24. public Collection<TaskExecutor> getExecutors(UserConfig userConfig, Map<String, Object> vars) {
  25. Set<TaskExecutor> idInfos=new LinkedHashSet<>();
  26. if(StringUtils.isEmpty(userConfig.getConfig())) {
  27. return idInfos;
  28. }
  29. JSONObject jsonObj=JSONObject.fromObject(userConfig.getConfig());
  30. String varType=jsonObj.getString("varType");
  31. String userIdVar=jsonObj.getString("userId");
  32. //从变量中取得该用户的实际值,其他来源有:上一任务审批人、发起人、变量。
  33. String userId=(String)vars.get(userIdVar);
  34. //获得关系Key及需要查找的关系方
  35. String relTypeKey=jsonObj.getString("relTypeKey");
  36. String orgDimId = jsonObj.getString("orgDimId");
  37. //查找一方
  38. List<OsRelInstDto> osRelInsts = new ArrayList<>();
  39. if("user".equals(varType)) { //基于用户关系来查找
  40. if(StringUtils.isNotEmpty(orgDimId)){
  41. osRelInsts=orgClient.getByRelTypeKeyParty2AndDim1(relTypeKey, userId,orgDimId);
  42. }else {
  43. osRelInsts=orgClient.getByRelTypeKeyParty2(relTypeKey, userId);
  44. }
  45. for(OsRelInstDto inst:osRelInsts){
  46. OsGroupDto osGroup=orgClient.getGroupById(inst.getParty1());
  47. if(osGroup!=null) {
  48. idInfos.add(TaskExecutor.getGroup(osGroup.getGroupId(),osGroup.getName()) );
  49. }
  50. }
  51. }else if("org".equals(varType)) { // 基于组关系来查找
  52. if(StringUtils.isNotEmpty(orgDimId)){
  53. osRelInsts=orgClient.getByRelTypeKeyParty1AndDim1(relTypeKey, userId,orgDimId);
  54. }else {
  55. osRelInsts=orgClient.getByRelTypeKeyParty1(relTypeKey, userId);
  56. }
  57. for(OsRelInstDto inst:osRelInsts){
  58. OsUserDto user = orgClient.getUserById(inst.getParty2());
  59. if(user!=null) {
  60. idInfos.add(TaskExecutor.getUser(user.getUserId(), user.getFullName()));
  61. }
  62. }
  63. }
  64. return idInfos;
  65. }
  66. }

6.4. 任务办理

任务办理是一个实现流程任务往下流转的一个过程,其需要完成的工作内容包括:

  1. 单据数据的保存与更新
  2. 流程往下流转
  3. 审批历史保存
  4. 流转记录保存
  5. 相关人员的消息通知

其中调用的时序图如下所示:

主要类的实现,请参考BpmTaskService的completeTask方法的实现

  1. /*
  2. * 完成任务处理、撤回,回退任务处理入口
  3. * @param cmd
  4. * @return
  5. */
  6. @GlobalTransactional
  7. public JsonResult completeTask(ProcessNextCmd cmd) {
  8. JsonResult result=new JsonResult();
  9. BpmTask bpmTask=get(cmd.getTaskId());
  10. BpmInst bpmInst=bpmInstService.getById(bpmTask.getInstId());
  11. result=checkTask(bpmTask,bpmInst);
  12. if(!result.isSuccess()){
  13. return result;
  14. }
  15. //传入,为后续的任务执行直接获取该定义,而不用进行转换获取。
  16. cmd.setDefId(bpmTask.getDefId());
  17. //放置线程变量,在后续可获取到
  18. cmd.setInstId(bpmTask.getInstId());
  19. //设置上一节点ID
  20. cmd.setPreNodeId(bpmTask.getKey());
  21. //加上审批的流程变更
  22. ProcessConfig processConfig=bpmDefService.getProcessConfig(bpmTask.getActDefId());
  23. UserTaskConfig userTaskConfig=(UserTaskConfig) bpmDefService.getNodeConfig(bpmTask.getActDefId(),bpmTask.getKey());
  24. cmd.addTransientVar(BpmConst.PROCESS_CONFIG,processConfig);
  25. cmd.addTransientVar(BpmConst.USERTASK_CONFIG,userTaskConfig);
  26. cmd.addTransientVar(BpmConst.BPM_INST,bpmInst);
  27. cmd.addTransientVar(BpmConst.BPM_APPROVE_TASK,bpmTask);
  28. //是否允许审批
  29. JsonResult jsonResult = bpmInstService.getAllowApprove(userTaskConfig,cmd.getFormData(),cmd.getVars());
  30. if(!jsonResult.isSuccess()){
  31. return jsonResult;
  32. }
  33. if(!TaskOptionType.SKIP.equals(cmd.getCheckType())){
  34. //处理表单数据
  35. formDataService.handFormData(cmd,userTaskConfig.getDataSetting(),"approve");
  36. }
  37. //处理任务跳转规则。
  38. String targetNodeId= handJumpRules(bpmTask, userTaskConfig);
  39. if(StringUtils.isNotEmpty(targetNodeId) && StringUtils.isEmpty(cmd.getDestNodeId())){
  40. cmd.setDestNodeId(targetNodeId);
  41. }
  42. //处理任务前置数据
  43. processHandlerExecutor.handTaskBeforeHandler(userTaskConfig,bpmTask,bpmInst.getBusKey());
  44. //创建审批历史。
  45. bpmCheckHistoryService.createHistory(bpmTask, cmd.getCheckType(),cmd.getOpinionName(), cmd.getOpinion(),cmd.getOpFiles());
  46. //若为回退,包括 上一步、撤回、驳回发起人等
  47. ITaskHandler taskHandler= TaskHandlerContext.getJumpType(cmd.getCheckType());
  48. taskHandler.handTask(bpmTask,cmd,userTaskConfig);
  49. //任务完成处理器。
  50. processHandlerExecutor.handTaskAfterHandler(userTaskConfig,bpmTask.getKey(),bpmInst.getBusKey());
  51. //处理任务跳过。
  52. bpmTaskSkipService.handSkipTask(cmd);
  53. //添加审批任务日志
  54. bpmInstLogService.addTaskLog(bpmTask.getInstId(),bpmTask.getTaskId(),bpmTask.getName(),bpmTask.getKey(),"审批任务");
  55. result.setMessage("成功完成处理任务!");
  56. return result;
  57. }

6.4.1. 任务前置、后置处理器

流程任务在执行以上completeTask方法时,平台提供两个处理器接口允许开发人员在外进行围的数据扩展调用处理。

用户只需要实现该接口,并只需要实现以下两个方法:

  1. /**
  2. * 当前任务处理前置处理器
  3. *
  4. * @param cmd 当前任务执行的上下文命令参数
  5. * @param nodeId 当前节点Id
  6. * @param busKey 业务主键
  7. */
  8. default void taskAfterHandle(IExecutionCmd cmd, String nodeId, String busKey) {
  9. }
  10. /**
  11. * 当前任务处理前置后置处理器
  12. *
  13. * @param cmd 当前任务执行的上下文命令参数
  14. * @param task 当前节点
  15. * @param busKey 业务主键
  16. */
  17. default void taskPreHandle(IExecutionCmd cmd, BpmTask task, String busKey) {
  18. }

在流程设计器中,选择中流程节点,即可在节点的编程扩展中可以选择对应的处理器:

6.5. 任务加签

任务加签是指任务执行人可对多实例的任务进行动态增加新的节点任务,并且基于此节点任务进行审批处理,不受主任务的影响。

//TODO

6.6. 任务追回

6.7. 任务追回处理

任务追回是指任务发出去后,还可以通过追回把新的任务追回至本节点,希望再重新往下执行。

因BPMN的流程是按顺序往下执行的,一旦产生了新的任务后,我们只能变更流程执行顺序,比较理想的办法是重新产生新的回退到当前的任务节点,原执行的节点锁定,流程的执行顺序还是按流程节点的原顺序往下执行。

如:

若当前任务处理于C节点,用户在他的已办事项A中可以进行追回,这时会产生待办A,同时C待办被锁住不允许进行审批,直至待办A任务处理完成后,C待办才恢复正常处理。

6.7.1. 追回时序图

6.8. 任务转办

6.8.1. 业务描述

任务转办是指把本属于自己的任务转交给他人办理的过程,它需要完成以下步骤:

  1. 只需要修改任务的执行人,一般来说是修改任务表bpmtask表中的assignee_字段即可
  2. 产生转办历史
  3. 产生任务转移信息
  4. 发送相关消息通知转办人

6.8.2. 处理界面

6.8.3. 任务转办的时序图

6.9. 任务沟通

6.9.1. 业务描述

业务的沟通指的是任务办理方发起沟通,产生了新的沟通任务给沟通人,沟通人员可以进行意见的填写与单据的数据处理,沟通任务的处理与不处理均不影响原有的流程任务的执行,被沟通的人还允许进行下一步的沟通。

6.9.2. 处理界面

发起沟通

沟通办理

6.9.3. 发起沟通时序图

6.10. 任务回退

TODO

7. 任务事件

Activiti的流程引擎提供了任务级的事件监听处理,通过平台配置的GlobalEventListener触发调用不同的事件监听处理器调用,最终实现流程任务的事件触发处理。

7.1. 任务创建

Activiti的任务是基于流程的任务办理,它不能脱离流程定义来创建,即任务的产生一定要通过流程引擎解析流程定义的XML的节点定义来产生,但BPM为了适应中国式的流程实施,需要扩展外围一些非流程的审批任务,因此需要在该事件统一创建扩展的任务实例,后续查询待办任务只需要查询该扩展的任务表BPM_TASK表即可。

因此我们在该事件中,实现了以下事项的工作,即创建待办事项,进行待办事项的人员分配,消息通知,生成流程的执行路径等。

  1. /**
  2. * 任务创建事件监听处理器
  3. * @author csx
  4. * @CreateTime 2019-03-03
  5. */
  6. @Slf4j
  7. public class TaskCreatedEventHandler extends BaseTaskHandler<ActivitiEntityEvent> {
  8. @Override
  9. public EventType getEventType() {
  10. return EventType.TASK_CREATED;
  11. }
  12. @Override
  13. public void handle(ActivitiEntityEvent eventEntity) {
  14. BpmDefService bpmDefService=SpringUtil.getBean(BpmDefService.class);
  15. IExecutionCmd cmd=ProcessHandleUtil.getProcessCmd();
  16. TaskEntity taskEntity=(TaskEntityImpl) eventEntity.getEntity();
  17. //获取流程的全局配置
  18. ProcessConfig processConfig= (ProcessConfig) cmd.getTransientVar(BpmConst.PROCESS_CONFIG);
  19. if(BeanUtil.isEmpty(processConfig)){
  20. processConfig = bpmDefService.getProcessConfig(eventEntity.getProcessDefinitionId());
  21. }
  22. ExecutionEntity execution = taskEntity.getExecution();
  23. //获取当前任务的配置
  24. UserTaskConfig userTaskConfig= (UserTaskConfig) bpmDefService.getNodeConfig(execution.getProcessDefinitionId(),execution.getCurrentActivityId());
  25. //1. 设置任务执行人。
  26. setAsignee(taskEntity,userTaskConfig);
  27. //2. 执行事件
  28. handEvent(taskEntity);
  29. //3. 发布事件供外部应用监听使用
  30. TaskCreateApplicationEvent ev = new TaskCreateApplicationEvent(taskEntity, userTaskConfig,processConfig);
  31. SpringUtil.publishEvent(ev);
  32. }
  33. //...

7.2. 任务完成

同样,我们监听Activiti的完成事件,并在该事件中通过增加触发完成后一些公共事件,并且清空当前完成后的流程任务实例数据。

  1. /**
  2. * 流程任务完成事件
  3. * @author csx
  4. * @CreateTime 2019-03-03
  5. */
  6. public class TaskCompletedEventHandler extends BaseTaskHandler<ActivitiEntityEvent> {
  7. @Override
  8. public EventType getEventType() {
  9. return EventType.TASK_COMPLETED;
  10. }
  11. @Override
  12. public void handle(ActivitiEntityEvent eventEntity) {
  13. BpmDefService bpmDefService=SpringUtil.getBean(BpmDefService.class);
  14. IExecutionCmd cmd= ProcessHandleUtil.getProcessCmd();
  15. //TODO 删除任务,任务处理人,catch 并抛出处理的异常,如调用外部接口返回的错误
  16. TaskEntityImpl taskEntity = (TaskEntityImpl) eventEntity.getEntity();
  17. UserTaskConfig userTaskConfig= (UserTaskConfig) cmd.getTransientVar(BpmConst.USERTASK_CONFIG);
  18. ProcessConfig processConfig= (ProcessConfig) cmd.getTransientVar(BpmConst.PROCESS_CONFIG);
  19. if(BeanUtil.isEmpty(processConfig)){
  20. processConfig = bpmDefService.getProcessConfig(eventEntity.getProcessDefinitionId());
  21. }
  22. if(BeanUtil.isEmpty(userTaskConfig)){
  23. ExecutionEntity execution = taskEntity.getExecution();
  24. userTaskConfig= (UserTaskConfig) bpmDefService.getNodeConfig(execution.getProcessDefinitionId(),execution.getCurrentActivityId());
  25. }
  26. BpmTask bpmTask= (BpmTask) cmd.getTransientVar(BpmConst.BPM_APPROVE_TASK);
  27. //1. 处理任务完成相关的数据
  28. handTaskComplete(bpmTask,cmd);
  29. //2. 处理事件一些公共数据处理,如前置与后置的公共事件调用
  30. handEvent(taskEntity);
  31. //3. 往外抛出处理事件
  32. TaskCompleteApplicationEvent ev = new TaskCompleteApplicationEvent(taskEntity, userTaskConfig,processConfig);
  33. SpringUtil.publishEvent(ev);
  34. }
  35. /**
  36. * 删除人员,记录审批意见。
  37. * @param bpmTask
  38. * @param cmd
  39. */
  40. private void handTaskComplete(BpmTask bpmTask,IExecutionCmd cmd){
  41. BpmTaskService bpmTaskService = SpringUtil.getBean(BpmTaskService.class);
  42. //删除树形任务(一般沟通任务都会删除)
  43. bpmTaskService.deleteBpmTaskCascade(bpmTask.getTaskId());
  44. //删除根据ACT_TASK_派生的任务,这种情况会删除会签任务。
  45. bpmTaskService.delByActTaskId(bpmTask.getActTaskId());
  46. }
  47. }
文档更新时间: 2020-10-12 09:25   作者:csx