一.简介
**会签:**在一个流程中的某一个 Task 上,这个 Task 需要多个用户审批,当多个用户全部审批通过,或者多个用户中的某几个用户审批通过,就算通过。
 例如:之前的请假流程,假设这个请假流程需要组长和经理都审批了,才算审批通过,那么就需要设置这个 Task 是会签节点。
**或签:**意思就是 A 的请假流程提交给 B、C、D,但是并不需要 B/C/D 同时审批通过,只需要 B/C/D 中的任意一个审批即可,这就是或签,注意,我这里的表述,只需要 B/C/D 任意一个审批即可,这个审批即可以是审批通过,也可以是审批拒绝,反正只要审批,这个 UserTask 就算完成了。
二.会签流程图
首先来画一下这个请假流程图,这个流程图基本上还是和之前的一样,截图如下:
 
三.请假处理
1.前端提交请假流程
接下来看下前端如何提交请假申请,页面如下:
 
 对应的 HTML 代码如下:
<h1>提交请假申请</h1>
<table>
    <tr>
        <td>请输入请假天数:</td>
        <td>
            <el-input type="text" v-model="afl.days"/>
        </td>
    </tr>
    <tr>
        <td>请输入请假理由:</td>
        <td>
            <el-input type="text" v-model="afl.reason"/>
        </td>
    </tr>
    <tr>
        <td>审批人:</td>
        <td>
            <el-select v-model="afl.approveUsers" style="width: 226px" placeholder="请选择审批人" multiple>
                <el-option
                        v-for="item in users"
                        :key="item.id"
                        :label="item.username"
                        :value="item.username"/>
            </el-select>
        </td>
    </tr>
</table>
<el-button type="primary" @click="submit">提交请假申请</el-button>
跟之前不同的是,这里的下拉框是多选的,当用户提交请假申请的时候,可以选择多个审批人,多个审批人的值将保存在 afl.approveUsers 变量中。
 再来看提交请假方法,代码如下:
submit() {
    let _this = this;
    axios.post('/ask_for_leave', this.afl)
        .then(function (response) {
            if (response.data.status == 200) {
                //提交成功
                _this.$message.success(response.data.msg);
                _this.search();
            } else {
                //提交失败
                _this.$message.error(response.data.msg);
            }
        })
        .catch(function (error) {
            console.log(error);
        });
},
请求的参数截图如下:
 
 看下这里提交的三个请求参数:
- approveUsers:这是审批当前流程的三个用户,当这三个用户都审批通过后,请假流程就通过了。
- days:这是请假的天数。
- reason:这是请假理由。
2.服务端处理请假请求
服务端如何处理这个请假请求,代码如下:
@Transactional
public RespBean askForLeave(AskForLeaveVO askForLeaveVO) {
    Map<String, Object> variables = new HashMap<>();
    askForLeaveVO.setName(SecurityContextHolder.getContext().getAuthentication().getName());
    variables.put("name", askForLeaveVO.getName());
    variables.put("days", askForLeaveVO.getDays());
    variables.put("reason", askForLeaveVO.getReason());
    variables.put("userTasks", askForLeaveVO.getApproveUsers());
    try {
        runtimeService.startProcessInstanceByKey("holidayRequest", askForLeaveVO.getName(), variables);
        return RespBean.ok("已提交请假申请");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return RespBean.error("提交申请失败");
}
可以看到,从前端一共传递过来三个参数,但是执行这个流程需要四个参数,其中一个 name 表示当前登录的用户名,也就是这个请假是谁发起的。另外三个参数就是前端传来的参数。
3.服务端返回待审批数据
接下来看下服务端如何返回待审批数据,代码如下:
/**
 *待审批列表
 */
public RespBean leaveList() {
    String identity = SecurityContextHolder.getContext().getAuthentication().getName();
    //找到所有分配给你的任务
    List<Task> tasks = taskService.createTaskQuery().taskAssignee(identity).list();
    //重新组装返回的数据,为每个流程增加任务 id,方便后续执行批准或者拒绝操作
    List<Map<String, Object>> list = new ArrayList<>();
    for (int i = 0; i < tasks.size(); i++) {
        Task task = tasks.get(i);
        Map<String, Object> variables = taskService.getVariables(task.getId());
        variables.put("id", task.getId());
        list.add(variables);
    }
    return RespBean.ok("加载成功", list);
}
页面如下:
 
 这个整体上分了两步:
- 首先查询出来当前用户所有待审批的 Task。
- 查询出来这些 Task 上的 variables,这是一个 Map 集合,然后我们再手动加上 id 这个参数。
 最后将组装好的 list 弄成一个 JSON 返回即可。
4.服务端批准 OR 拒绝
服务端批准或者拒绝请假流程,代码如下:
public RespBean askForLeaveHandler(ApproveRejectVO approveRejectVO) {
    try {
        Task task = taskService.createTaskQuery().taskId(approveRejectVO.getTaskId()).singleResult();
        boolean approved = approveRejectVO.getApprove();
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("approved", approved);
        variables.put("approveUser#" + task.getAssignee(), SecurityContextHolder.getContext().getAuthentication().getName());
        taskService.complete(task.getId(), variables);
        return RespBean.ok("操作成功");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return RespBean.error("操作失败");
}
批准或者拒绝,最主要的参数就是 approved,true 表示批准,false 表示拒绝。
另一方面,由于现在是会签,我们需要知道目前谁已经审批了,谁还没审批,所以这里额外多加了一个参数 approveUser#XXX,表示审批这个节点的用户名(也就是当前登录用户)。
注意这个参数的 key 我没有固定,主要是因为这个节点会有多个人审批,如果固定的话,后面审批的人会覆盖掉前面的人,所以这个节点的 key 设置成动态的了,approveUser# 后面加上处理这个节点的用户名。
![[iOS]消息传递和消息转发](https://img-blog.csdnimg.cn/e705fff4262d4c14aad8936fd54572b7.png#pic_center)

















