flowable之多个部门多角色会办

客户需求

1.公文办理要求同时送给主办部门,会办部门,厅领导。

2.会办部门支持多个部门并行办理。

3.主办部门支持多个部门并行办理,并且主办部门人员可以增加会办部门。

4.厅领导支持多个领导审批。

流程图

收文流程

主办部门子流程

会办部门子流程

流程难点

 1.拟办人要求与部门关联

        一般流程中可以把办理任务设置为一个固定角色,然后这个角色关联多个用户来实现或签。但这里需求要在角色上加一个维度-拟办人角色与部门相关,即每个部门都可以有一个拟办人,拟办人通过后,会流转到本部门的领导那里审批。所以送上xml配置:

    <bpmn2:userTask xmlns:flowable="http://flowable.org/bpmn" id="Activity_0el2prr" name="办公室拟办" flowable:dataType="ROLES" flowable:assignee="${assignee}" flowable:candidateGroups="ROLE131-${bgsId}" flowable:text="拟办人">
      <bpmn2:extensionElements>
        <flowable:properties>
          <flowable:property name="type" value="bgs" />
        </flowable:properties>
      </bpmn2:extensionElements>
      <bpmn2:multiInstanceLoopCharacteristics flowable:collection="${multiInstanceHandler.getUserIds(execution)}" flowable:elementVariable="assignee">
        <bpmn2:completionCondition>${nrOfCompletedInstances &gt; 0}</bpmn2:completionCondition>
      </bpmn2:multiInstanceLoopCharacteristics>
    </bpmn2:userTask>

        如果流程引擎不能配置出xml,建议直接复制以上xml到流程图中,然后手动调整代码。

 2.秘书组送多个主办/会办部门

        如果主办部门只有一个,可以把主办子流程节点全部画出来,但现实需求是主办部门不固定,要求支持N个,那就没办法全部画出来了。会办部门也是要求支持多个,更复杂的是主办部门还要能增加会办部门。所以解决思路只能朝着怎么能在主流程中调用子流程的方向思考了。

        找找资料,最后发现flowable流程引擎提供了callActivity[调用活动]-允许主流程自动执行子流程,所以问题就简单了。把主办部门流程、会办部门流程单独画出来,主流程中用callActivity来代替子流程。

调用活动+子流程选择后,任务节点边框加粗且有[+]

    <bpmn2:callActivity id="callReceiveDocHb" name="会办子流程" calledElement="receive_doc_hb" flowable:calledElementType="key" flowable:inheritBusinessKey="true" flowable:inheritVariables="true" flowable:sameDeployment="true">
      <bpmn2:multiInstanceLoopCharacteristics flowable:collection="deptIdList" flowable:elementVariable="deptId">
        <bpmn2:completionCondition>${nrOfCompletedInstances == nrOfInstances}</bpmn2:completionCondition>
      </bpmn2:multiInstanceLoopCharacteristics>
    </bpmn2:callActivity>
    <bpmn2:sequenceFlow id="Flow_0spx6dr" name="送会办" sourceRef="Gateway_0pn01ra" targetRef="callReceiveDocHb">
      <bpmn2:extensionElements>
        <flowable:properties>
          <flowable:property name="deptIdList" value="" />
        </flowable:properties>
      </bpmn2:extensionElements>
      <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${execution.getVariable('deptIdList').size() &gt; 0}</bpmn2:conditionExpression>
    </bpmn2:sequenceFlow>
    <bpmn2:callActivity id="callReceiveDocHb" name="会办子流程" calledElement="receive_doc_hb" flowable:calledElementType="key" flowable:inheritBusinessKey="true" flowable:inheritVariables="true" flowable:sameDeployment="true">
      <bpmn2:multiInstanceLoopCharacteristics flowable:collection="deptIdList" flowable:elementVariable="deptId">
        <bpmn2:completionCondition>${nrOfCompletedInstances == nrOfInstances}</bpmn2:completionCondition>
      </bpmn2:multiInstanceLoopCharacteristics>
    </bpmn2:callActivity>
    <bpmn2:sequenceFlow id="Flow_0spx6dr" name="送会办" sourceRef="Gateway_0pn01ra" targetRef="callReceiveDocHb">
      <bpmn2:extensionElements>
        <flowable:properties>
          <flowable:property name="deptIdList" value="" />
        </flowable:properties>
      </bpmn2:extensionElements>
      <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${execution.getVariable('deptIdList').size() &gt; 0}</bpmn2:conditionExpression>
    </bpmn2:sequenceFlow>

3.厅领导可以有多个人员

        这里可以在同意接口 /complete 中传入 assigneeList (勾选多个userId)

    <bpmn2:userTask xmlns:flowable="http://flowable.org/bpmn" id="Activity_0fgj1zi" name="厅领导批示" flowable:dataType="ROLES" flowable:assignee="${assignee}">
      <bpmn2:extensionElements>
        <flowable:properties>
          <flowable:property name="type" value="leader" />
        </flowable:properties>
      </bpmn2:extensionElements>
      <bpmn2:multiInstanceLoopCharacteristics flowable:collection="assigneeList" flowable:elementVariable="assignee">
        <bpmn2:completionCondition>${nrOfCompletedInstances &gt; 0}</bpmn2:completionCondition>
      </bpmn2:multiInstanceLoopCharacteristics>
    </bpmn2:userTask>

核心代码

MultiInstanceHandler.java

@Component("multiInstanceHandler")
@Slf4j
public class MultiInstanceHandler {

    public HashSet<String> getUserIds(DelegateExecution execution) {
        HashSet<String> candidateUserIds = new LinkedHashSet<>();
        FlowElement flowElement = execution.getCurrentFlowElement();
        Map<String, Object> variables = execution.getVariables();
        Objects.requireNonNull(variables, "can not find variables !");

        if (ObjectUtil.isNotEmpty(flowElement) && flowElement instanceof UserTask) {
            UserTask userTask = (UserTask) flowElement;
            log.debug("userTask = {}", userTask.getName());
            String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
            if ("USERS".equals(dataType) && CollUtil.isNotEmpty(userTask.getCandidateUsers())) {
                candidateUserIds.addAll(userTask.getCandidateUsers());
            } else if (CollUtil.isNotEmpty(userTask.getCandidateGroups())) {
                List<String> groups = userTask.getCandidateGroups()
                    .stream().map(item -> item.substring(4)).collect(Collectors.toList());
                if ("ROLES".equals(dataType)) {
                    log.debug("candidateGroups = {}", userTask.getCandidateGroups());
                    SysUserRoleMapper userRoleMapper = SpringUtil.getBean(SysUserRoleMapper.class);
                    groups.forEach(item -> {
                        // ROLE120-${deptId}
                        if(item.indexOf("-") > 0){
                            for(String key : variables.keySet()){
                                if(variables.get(key)!=null){
                                    item = item.replace("${"+key+"}", String.valueOf(variables.get(key)));
                                }
                            }
                            String roleId = item.substring(0, item.indexOf("-"));
                            //支持传入多个部门(要求多个部门id使用,分隔)
                            String deptIds = item.substring(item.indexOf("-") + 1);
                            for(String deptId : deptIds.split(",")){
                                if(StrUtil.isNotBlank(deptId)){
                                    List<String> userIds = userRoleMapper.selectUserIds(roleId, deptId).stream().map(String::valueOf).collect(Collectors.toList());
                                    if(CollUtil.isEmpty(userIds)){
                                        SysRoleMapper roleMapper = SpringUtil.getBean(SysRoleMapper.class);
                                        SysRole sysRole = roleMapper.selectById(roleId);
                                        SysDeptMapper deptMapper = SpringUtil.getBean(SysDeptMapper.class);
                                        SysDept sysDept = deptMapper.selectById(deptId);
                                        throw new ServiceException(String.format("[%s]部门下的[%s]角色未分配用户!", sysDept.getDeptName(), sysRole.getRoleName()));
                                    }
                                    log.info("selectUserIds userIds = {} by roleId={}, deptId={}", userIds, roleId, deptId);
                                    candidateUserIds.addAll(userIds);
                                }
                            }
                        }else {
                            List<String> userIds = userRoleMapper.selectUserIdsByRoleId(item).stream().map(String::valueOf).collect(Collectors.toList());
                            if(CollUtil.isEmpty(userIds)){
                                SysRoleMapper roleMapper = SpringUtil.getBean(SysRoleMapper.class);
                                SysRole sysRole = roleMapper.selectById(item);
                                throw new ServiceException(String.format("[%s]角色未分配用户!", sysRole.getRoleName()));
                            }
                            log.info("selectUserIdsByRoleId userIds = {} by roleId={}", userIds, item);
                            candidateUserIds.addAll(userIds);
                        }
                    });
                } else if ("DEPTS".equals(dataType)) {
                    User2deptMapper user2deptMapper = SpringUtil.getBean(User2deptMapper.class);
                    groups.forEach(item -> {
                        List<String> userIds = user2deptMapper.selectUserIdList(item)
                                .stream().map(String::valueOf).collect(Collectors.toList());
                        if(CollUtil.isEmpty(userIds)){
                            SysDeptMapper deptMapper = SpringUtil.getBean(SysDeptMapper.class);
                            SysDept sysDept = deptMapper.selectById(item);
                            throw new ServiceException(String.format("[%s]部门未分配用户!", sysDept.getDeptName()));
                        }
                        log.info("selectUserIdsByDeptId userIds = {} by deptId={}", userIds, item);
                        candidateUserIds.addAll(userIds);
                    });
                }
            }
        }
        return candidateUserIds;
    }
}

ProcessConstants.java

/**
 * 流程常量信息
 */
public class ProcessConstants {

    public static final String SUFFIX = ".bpmn";

    /**
     * 动态数据
     */
    public static final String DATA_TYPE = "dynamic";

    /**
     * 单个审批人
     */
    public static final String USER_TYPE_ASSIGNEE = "assignee";


    /**
     * 候选人
     */
    public static final String USER_TYPE_USERS = "candidateUsers";


    /**
     * 审批组
     */
    public static final String USER_TYPE_ROUPS = "candidateGroups";

    /**
     * 单个审批人
     */
    public static final String PROCESS_APPROVAL = "approval";

    /**
     * 会签人员
     */
    public static final String PROCESS_MULTI_INSTANCE_USER = "userList";

    /**
     * nameapace
     */
    public static final String NAMASPASE = "http://flowable.org/bpmn";

    /**
     * 会签节点
     */
    public static final String PROCESS_MULTI_INSTANCE = "multiInstance";

    /**
     * 自定义属性 dataType
     */
    public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType";

    /**
     * 自定义属性 userType
     */
    public static final String PROCESS_CUSTOM_USER_TYPE = "userType";


    /**
     * 流程跳过
     */
    public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";

}

    @ApiOperation(value = "同意任务")
    @PostMapping(value = "/complete")
    public Result complete(@Valid @RequestBody WfTaskBo bo) {
        if(StrUtil.isBlank(bo.getComment())){
            bo.setComment("通过");
        }
        Task task = flowTaskService.getTask(bo.getTaskId());
        if (Objects.isNull(task)) {
            throw new ServiceException("任务不存在");
        }
        //完成任务
        flowTaskService.complete(bo, task);
        //更新公文当前所在节点
        String title = flowTaskService.updateNode(bo, task.getProcessInstanceId());
        //给相关用户发送消息通知
        SysMsgType msgType = "leader".equals(bo.getType()) ? SysMsgType.ps : SysMsgType.gw;
        messageHandler.doHandle(msgType.name(), title, bo.getTaskId());
        return Result.ok();
    }



    /**
     * 完成任务
     *
     * @param taskBo 请求实体参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void complete(WfTaskBo taskBo, Task task) {
        String instanceId = task.getProcessInstanceId();
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            if (StrUtil.isNotEmpty(taskBo.getComment())) {
                taskService.addComment(taskBo.getTaskId(), instanceId, FlowComment.DELEGATE.getType(), taskBo.getComment());
            }
            taskService.resolveTask(taskBo.getTaskId());
        } else {
            if (StrUtil.isNotEmpty(taskBo.getComment())) {
                //taskService.addComment(taskBo.getTaskId(), instanceId, FlowComment.NORMAL.getType(), taskBo.getComment());
                taskService.addComment(taskBo.getTaskId(), instanceId, taskBo.getType(), taskBo.getComment());
            }
            if(StrUtil.isNotBlank(taskBo.getOwner())){//更新代理人
                taskService.setOwner(taskBo.getTaskId(), taskBo.getOwner());
            }
            /*if(StrUtil.isNotBlank(taskBo.getAssignee())){//更新办理人
                taskService.setAssignee(taskBo.getTaskId(), taskBo.getAssignee());
            }*/
            Map<String, Object> variables = new HashMap<>();
            if(taskBo.getFlag()!=null){
                variables.put("flag", Integer.parseInt(taskBo.getFlag()));
            }
            if(taskBo.getGateFlag()!=null){
                variables.put("gateFlag", Integer.parseInt(taskBo.getGateFlag()));
            }
            if(StrUtil.isNotBlank(taskBo.getAssignee())){
                variables.put("assignee", taskBo.getAssignee());
            }
            if(taskBo.getAssigneeList()!=null && taskBo.getAssigneeList().size()>0){
                variables.put("assigneeList", taskBo.getAssigneeList());
            }
            if(taskBo.getHandleDeptIdList()!=null && taskBo.getHandleDeptIdList().size()>0){
                variables.put("handleDeptIdList", taskBo.getHandleDeptIdList());
            }
            if(taskBo.getDeptIdList()!=null && taskBo.getDeptIdList().size()>0){
                variables.put("deptIdList", taskBo.getDeptIdList());
            }
            if (CollUtil.isNotEmpty(taskBo.getMap())) {
                variables.putAll(taskBo.getMap());
            }
            log.info("variables = {}", variables);
            /*if ("1".equals(gw.getIsTransfer())) {
                log.info(">>>> getIsTransfer = ", gw.getIsTransfer());
                boolean localScope = false;
                taskService.complete(taskBo.getTaskId(), variables, localScope);
            } else {
            }*/
            taskService.complete(taskBo.getTaskId(), variables);

            //更新待办任务的parentTaskId
            HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().processInstanceId(taskBo.getProcInstId()).unfinished();
            List<HistoricTaskInstance> list = query.list();
            if(list!=null && list.size()>0){
                for (HistoricTaskInstance instance : list) {
                    HistoricTaskInstanceEntity entity = (HistoricTaskInstanceEntity)instance;
                    entity.setParentTaskId(taskBo.getTaskId());
                }
            }
        }
    }

WfTaskBo.java

@Getter
@Setter
public class WfTaskBo {
    @ApiModelProperty("任务Id")
    private String taskId;

    @ApiModelProperty("任务名称")
    private String taskName;

    @ApiModelProperty("用户Id")
    private String userId;

    @ApiModelProperty("任务意见")
    private String comment;

    @ApiModelProperty("流程实例Id")
    private String procInstId;

    @ApiModelProperty("节点")
    private String targetKey;

    @ApiModelProperty("参数map:{'handleDeptId': 128, 'deptId': '125'}")
    private Map<String, Object> map;

    @ApiModelProperty("办理人(接收人)")
    private String assignee;

    @ApiModelProperty(value = "实际办理人(用于代理人)")
    private String owner;

    @ApiModelProperty("多个审批人")
    private List<String> assigneeList;

    @ApiModelProperty("网关标识")
    private String gateFlag;

    @NotBlank(message = "flag can not be blank !")
    @ApiModelProperty("同意标识:1同意,2驳回,0结束")
    private String flag;

    @ApiModelProperty("业务id")
    @NotBlank(message = "dataId can not be blank !")
    private String dataId;

    private List<String> taskIds;

    @ApiModelProperty("是否为转办理(0办理 1转办理)")
    private String isTransfer;

    @ApiModelProperty("办理类型(zb主办,hb会办,bg办公室审,leader领导审)")
    private String type;

    @ApiModelProperty("主办部门ids")
    private List<String> handleDeptIdList;

    @ApiModelProperty("会办部门ids")
    private List<String> deptIdList;
}

流程图 xml

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星梦天河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值