项目结构:

接下来代码:

Duorenhuiqian.bpmn20.xml:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
<process id="countersign" name="Duorenhuiqian" isExecutable="true">
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<userTask id="UserTask1" name="U1" flowable:assignee="${assignee}" flowable:formFieldValidation="true">
<extensionElements>
<!--<flowable:executionListener event="start" class="com.cars.ngtdms.cooperation.flowable.listener.CountersignListener"></flowable:executionListener>-->
<flowable:executionListener event="start" delegateExpression="${countersignListener}"></flowable:executionListener>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
<multiInstanceLoopCharacteristics isSequential="false" flowable:collection="assigneeList" flowable:elementVariable="assignee">
<completionCondition>${multiInstance.accessCondition(execution)}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="sid-50622098-77B0-4413-A1D4-088B47DEC95F" sourceRef="startEvent1" targetRef="UserTask1"></sequenceFlow>
<userTask id="UserTask2" name="U2" flowable:assignee="WXF" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<endEvent id="sid-814B8939-308D-4003-8B35-B750DC8F8A5C"></endEvent>
<exclusiveGateway id="getWay" name="getWay"></exclusiveGateway>
<userTask id="UserTask3" name="U3" flowable:assignee="PXY" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<endEvent id="sid-4C2BFAF2-08AC-4F89-A964-046EA583F0B5"></endEvent>
<sequenceFlow id="sid-CE25CF8A-5BF2-4227-A747-98F6A053283E" sourceRef="UserTask3" targetRef="sid-4C2BFAF2-08AC-4F89-A964-046EA583F0B5"></sequenceFlow>
<sequenceFlow id="sid-0F397F15-5685-4DF0-B7E0-75C3AB43F6D8" sourceRef="UserTask2" targetRef="sid-814B8939-308D-4003-8B35-B750DC8F8A5C"></sequenceFlow>
<sequenceFlow id="sid-74E664CC-0FC9-49A6-A47C-BCFA1198F074" sourceRef="UserTask1" targetRef="getWay"></sequenceFlow>
<sequenceFlow id="repply" name="通过" sourceRef="getWay" targetRef="UserTask2">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="reject" name="否决" sourceRef="getWay" targetRef="UserTask3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='否决'}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_countersign">
<bpmndi:BPMNPlane bpmnElement="countersign" id="BPMNPlane_countersign">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="135.0" y="290.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="UserTask1" id="BPMNShape_UserTask1">
<omgdc:Bounds height="80.0" width="100.0" x="255.0" y="265.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="UserTask2" id="BPMNShape_UserTask2">
<omgdc:Bounds height="80.0" width="100.0" x="495.0" y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-814B8939-308D-4003-8B35-B750DC8F8A5C" id="BPMNShape_sid-814B8939-308D-4003-8B35-B750DC8F8A5C">
<omgdc:Bounds height="28.0" width="28.0" x="750.0" y="146.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="getWay" id="BPMNShape_getWay">
<omgdc:Bounds height="40.0" width="40.0" x="525.0" y="285.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="UserTask3" id="BPMNShape_UserTask3">
<omgdc:Bounds height="80.0" width="100.0" x="495.0" y="405.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-4C2BFAF2-08AC-4F89-A964-046EA583F0B5" id="BPMNShape_sid-4C2BFAF2-08AC-4F89-A964-046EA583F0B5">
<omgdc:Bounds height="28.0" width="28.0" x="645.0" y="431.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-50622098-77B0-4413-A1D4-088B47DEC95F" id="BPMNEdge_sid-50622098-77B0-4413-A1D4-088B47DEC95F">
<omgdi:waypoint x="164.94999923927443" y="305.0"></omgdi:waypoint>
<omgdi:waypoint x="255.0" y="305.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-74E664CC-0FC9-49A6-A47C-BCFA1198F074" id="BPMNEdge_sid-74E664CC-0FC9-49A6-A47C-BCFA1198F074">
<omgdi:waypoint x="354.9499999999567" y="305.10384615384606"></omgdi:waypoint>
<omgdi:waypoint x="525.4583333333242" y="305.4583333333333"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="reject" id="BPMNEdge_reject">
<omgdi:waypoint x="545.431654676259" y="324.51130481667866"></omgdi:waypoint>
<omgdi:waypoint x="545.1431899641577" y="405.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-0F397F15-5685-4DF0-B7E0-75C3AB43F6D8" id="BPMNEdge_sid-0F397F15-5685-4DF0-B7E0-75C3AB43F6D8">
<omgdi:waypoint x="594.9499999999925" y="160.0"></omgdi:waypoint>
<omgdi:waypoint x="750.0" y="160.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="repply" id="BPMNEdge_repply">
<omgdi:waypoint x="545.4310344827586" y="285.4310344827586"></omgdi:waypoint>
<omgdi:waypoint x="545.1372852233677" y="199.95"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-CE25CF8A-5BF2-4227-A747-98F6A053283E" id="BPMNEdge_sid-CE25CF8A-5BF2-4227-A747-98F6A053283E">
<omgdi:waypoint x="594.9499999997366" y="445.0"></omgdi:waypoint>
<omgdi:waypoint x="645.0" y="445.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
application.yml: spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/foujueflowable?characterEncoding=UTF-8
password: root
username: root
driver-class-name: com.mysql.jdbc.Driver
flowable:
#关闭定时任务job
async-executor-activate: false
scale: 0.5 assignee: person1,person2,person3,person4 yipiaofoujueren: pxy
ExpenseController:
package com.cars.ngtdms.cooperation.controller; import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.*; @Controller @RequestMapping(value = "expense")
@ResponseBody
public class ExpenseController {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessEngine processEngine; @Value("#{'${yipiaofoujueren}'.split(',')}")
private List yipiaofoujueren; /**
* 开启
* @return
*/
@RequestMapping(value = "add")
public String StartTask(){
runtimeService.startProcessInstanceByKey("countersign");
return "OK";
} /**
* 执行
* @return
*/
@RequestMapping(value = "do")
public List<String> DoTask(String name){
// List<Task> tasks = taskService.createTaskQuery().taskCandidateUser(name).list();
List<Task> tasks = taskService.createTaskQuery().taskAssignee(name).list();
List<String> list = new ArrayList<>();
System.out.println("[");
for (Task task:tasks
) { System.out.println(task.getId());
list.add("id:"+task.getId()+" name:"+task.getName()+" Assignee:"+task.getAssignee()); }
System.out.println("]");
return list;
} /**
* 完成
* @return
*/
@RequestMapping(value = "done")
@Transactional
public String DoneTask(String name,String taskId,String flg){
List<Task> list = taskService.createTaskQuery().taskAssignee(name).list();
List<String> taskList= new ArrayList<>();
String taskId1="";
for (Task task : list) {
taskList.add("id:"+task.getId()+"name"+task.getName()+"审批人:"+task.getAssignee());
taskId1 = task.getId();
System.out.println(taskId);
}
System.out.println("完成前"+taskList);
if ("n".equals(flg)){
if (yipiaofoujueren.contains(name)){
Map<String,Object> map = new HashMap<>();
Integer reject = (int)taskService.getVariable(taskId1,"reject");
map.put("reject",reject+1);
taskService.complete(taskId,map);
}else {
Map<String,Object> map = new HashMap<>();
Integer rejectPeson = (int)taskService.getVariable(taskId1,"rejectPeson");
map.put("rejectPeson",rejectPeson+1);
taskService.complete(taskId,map); }
} else if ("y".equals(flg)){
Map<String,Object> map = new HashMap<>();
Integer agreePeson = (int)taskService.getVariable(taskId1,"agreePeson");
map.put("agreePeson",agreePeson+1);
taskService.complete(taskId,map);
} List<Task> list1 = taskService.createTaskQuery().taskAssignee(name).list();
List<String> taskList1= new ArrayList<>();
for (Task task : list1) {
taskList1.add("id:"+task.getId()+"name"+task.getName()+"审批人:"+task.getAssignee());
}
System.out.println("完成后"+taskList1);
return "通过";
} /**
* 查看当前任务名
* @return
*/
@RequestMapping(value = "select")
public List<String> SelectTask(){
List<Task> list = taskService.createTaskQuery().list();
List<String> taskList=new ArrayList<>();
for (Task task : list) {
taskList.add(task.getId()+"-----------"+task.getName()); } return taskList;
} }
CountersignListener:
package com.cars.ngtdms.cooperation.flowable.listener; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import java.util.HashMap;
import java.util.List;
@Component
public class CountersignListener implements ExecutionListener {
@Value("#{'${assignee}'.split(',')}")
private List list;
@Override
public void notify(DelegateExecution delegateExecution) {
System.out.println("读取的审批人是:"+list);
HashMap<String, Object> map = new HashMap<>();
//定义的人员列表4人
//String[] person = { "person1", "person2", "person3","pxy" };
//map.put("assigneeList", Arrays.asList(person));
map.put("assigneeList", list);
map.put("reject", 0);
map.put("rejectPeson", 0);
map.put("agreePeson", 0);
delegateExecution.setVariables(map); }
}
ApplicationPxy:
package com.cars.ngtdms.cooperation; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
//@EnableDiscoveryClient
public class ApplicationPxy { public static void main(String[] args) {
SpringApplication.run(ApplicationPxy.class,args);
}
}
MultiInstanceCompleteTask:
package com.cars.ngtdms.cooperation; import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import java.io.Serializable; @Component(value = "multiInstance")
public class MultiInstanceCompleteTask implements Serializable{
/**
* 评估结果判定条件
* @param execution 分配执行实例
*/
@Value("${scale}")
private String scaleStr; public boolean accessCondition(DelegateExecution execution) {
Double scale = Double.valueOf(scaleStr);
System.out.println("比例是:"+scale);
System.out.println("I am running!!!!!!");
System.out.println("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT");
//已完成的实例数
int completedInstance = (int) execution.getVariable("nrOfCompletedInstances");
System.out.println("此时已完成的实例数为:"+completedInstance);
//否决判断,一票否决
if (execution.getVariable("reject") != null) {
int rejectCount = (int) execution.getVariable("reject");
System.out.println("reject:"+rejectCount);
if (rejectCount > 0) {
//输出方向为拒绝
execution.setVariable("outcome", "否决");
//一票否决其他实例没必要做,结束
return true;
}
}
//获取所有的实例数
int AllInstance = (int)execution.getVariable("nrOfInstances");
System.out.println("总实例数目:"+AllInstance);
//获取不同意的次数
int rejectPeson = (int)execution.getVariable("rejectPeson");
System.out.println("不同意的人的个数为:"+rejectPeson);
//获取同意人的次数
int agreePeson = (int)execution.getVariable("agreePeson");
System.out.println("同意的人的个数为:"+agreePeson); //所有实例任务未全部做完则继续其他实例任务
if (completedInstance != AllInstance) {
//加入不同意的人数大于设置比例*总人数
if (rejectPeson*1.00/AllInstance>(1-scale)){
execution.setVariable("outcome", "否决");
return true;
}
if (agreePeson*1.00/AllInstance>=scale){
execution.setVariable("outcome", "通过");
return true;
} return false;
} else {
//输出方向为赞同
execution.setVariable("outcome", "通过");
//所有都做完了没被否决,结束
return true;
} }
}
依赖: <!--flowable工作流依赖-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>

好了 这样就可以直接运行了,数据库的名字要设置对

然后开始解释:

多人会签意思是这一个任务需要很多人来进行审批,同意总数超过或者未超过多少比例的时候会执行对应的某个任务。

还有个就是一票否决,指定某个人,它反对就直接相当于全部反对

代码解释:

首先配置文件application:

然后xml文件:

标签的具体工作原理这里不再赘述,可以自行百度

然后就是监听器CountersignListener:

然后就是执行器的配置内容了MultiInstanceCompleteTask:

有点长久分开截屏写了

箭头指向的地方就是一票否决,在监听器里边对reject进行了初始化,这里通过获取reject来判断它的值如果大于零就直接否决,

我这里为了能体现否决和同意,我一共设置了三个任务,第一个任务是会签,第二个任务和第三个任务一个是同意一个是否决对应的任务,他们是通过路由进行相关联的,路由判断outcome是"否决"还是"通过"然后路由到第二个任务或者第三个任务

他这个方法呢 如果是返回的true,那么就直接结束第一个会签任务,如果返回的是false那么会继续执行第一个会签任务

接下来继续:

这里有个小BUG  如果一票否决的人还没有执行,那么但凡执行了上边的那个通过,那这个任务就直接跳过去了,他就不能一票否决了,其实这里可以进行判断,如果这里一票否决的人没有执行就让它返回false。

然后继续来看ExpenseController类:

好的 接下来第一个方法,根据审批人的ID来获取任务的ID:

第二个方法,根据审批人的名字、任务的id、flg来进行执行任务

这里要注意了!!!!这里是flg不是flag

审批人的名字就不用说了,任务id就是第一个方法获取的任务的id,flg呢就是来判断是否同意的,n代表不同意,y代表同意

这里比较简单,代码上也有注释,就解释一下逻辑处理这一点好了:

很好,然后第三个方法!:

它就是来查看当前xml就是这个flowable的当前任务,专业术语我忘记怎么说了,emmm  当前实例?不行 不行 忘记了

执行它可以到执行到哪个任务了

好了 没了 就这样了 emmmm  这里写的是审批人是相当于直接塞进去的,也可以手动的塞进去,就是add的那个方法,在进行加载xml的时候,创建一个map,然后序列化一下,放到那个方法的第二个参数的位置就行了。

flowable的多人会签和一票否决的更多相关文章

  1. flowable 实现多实例-会签-动态配置人员 参考demo

    会签 即多人执行当前任务 设置判断数 通过 例如:设置了是半数通过即可通过当前节点 如果当前是4人那就是2人即通过 如果是6人那就是三人即通过 如果是5人 即三人通过 看各位的判断值是如何书写 这个值 ...

  2. BL老师的建议,数学不好的,大数据一票否决--后赋从java转大数据

    __________________________ 作者:我是蛋蛋链接:https://www.zhihu.com/question/59593387/answer/167235075来源:知乎著作 ...

  3. 基于camunda开源流程引擎如何实现会签及会签原理解析

    一.背景 市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.由于jbpm.activiti.flowable这几个流程引擎出现的比较早, ...

  4. Activiti 工作流会签开发设计思路

    http://man1900.iteye.com/blog/1607753 在流程业务管理中,任务是通常都是由一个人去处理的,而多个人同时处理一个任务,这种任务我们称之为会签任务.这种业务需求也很常见 ...

  5. flowable中传入审批人是list

    package org.springblade.flow.engine.listener; import org.flowable.engine.delegate.DelegateExecution; ...

  6. PHP 高并发、抢票、秒杀 解决方案

    对于抢票.秒杀这种业务,我说说自己对这种高并发的理解吧,这里提出个人认为比较可行的几个方案:方案一:使用队列来实现可以基于例如MemcacheQ等这样的消息队列,具体的实现方案这么表述吧比如有100张 ...

  7. 并发编程 - 进程 - 1.互斥锁/2.模拟抢票/3.互斥锁与join区别

    1.互斥锁: 互斥锁:Lock 原理就是把并发变成串行,一个一个运行,不错乱,但效率低 保证多个进程修改一块数据时,大家是一个一个修改,不错乱 mutex.acquire() mutex.releas ...

  8. JDOJ 1928: 排队买票

    JDOJ 1928: 排队买票 JDOJ传送门 Description 一场演唱会即将举行.现有n个歌迷排队买票,一个人买一张,而售票处规定,一个人每次最多只能买两张票.假设第i位歌迷买一张票需要时间 ...

  9. Activiti7 回退与会签

    1.  回退(驳回) 回退的思路就是动态更改节点的流向.先遇水搭桥,最后再过河拆桥. 具体操作如下: 取得当前节点的信息 取得当前节点的上一个节点的信息 保存当前节点的流向 新建流向,由当前节点指向上 ...

随机推荐

  1. day100:MoFang:用户模型类的创建&Marshmallow模块&使用基本构造器Schema完成数据的序列化转换和反序列化转换

    目录 1.用户模型的创建 2.Marshmallow模块 3.MarshMallow基本构造器:Schema 1.基于Schema完成数据序列化转换 2.基于Schema完成数据反序列化转换 3.反序 ...

  2. 在 Spring Boot 配置 Kafka 安全认证

    spring: kafka: bootstrap-servers: IP:端口 listener: missing-topics-fatal: false properties: sasl: mech ...

  3. MySQL索引(二):建索引的原则

    在了解了索引的基础知识及B+树索引的原理后(如需复习请点这里),这一节我们了解一下有哪些建索引的原则,来指导我们去建索引. 建索引的原则 1. 联合索引 我们可能听一些数据库方面的专业人士说过:&qu ...

  4. moviepy音视频剪辑基类VideoClip的write_videofile方法codec、bitrate、preset、audio_bitrate、audio_nbytes等参数的作用

    ☞ ░ 前往老猿Python博文目录 ░ moviepy音视频剪辑模块的视频剪辑基类write_videofile方法用于将视频剪辑输出到文件,调用语法如下: write_videofile(self ...

  5. PyQt学习随笔:Model/View开发时从Model相关类派生自定义类需要注意的问题

    在<PyQt学习随笔:重写setData方法截获Model/View中视图数据项编辑的注意事项>介绍的方法,从Model相关类派生自定义类,通过重写setData方法以获取View中数据的 ...

  6. Scrum 冲刺 第六篇

    Scrum 冲刺 第六篇 每日会议照片 昨天已完成工作 队员 昨日完成任务 黄梓浩 完成app项目架构搭建 黄清山 完成部分个人界面模块数据库的接口 邓富荣 完成登录注册接口 钟俊豪 完成部分博客圈模 ...

  7. BJWC2011 禁忌

    题目链接 题解 多模式匹配首先建 AC 自动机,看到 \(len \le 10^9\) 想到矩阵乘法优化. 朴素 DP 关于分割的最大值,可以贪心,只要走到一个能匹配串的点立刻返回根继续匹配就行,一定 ...

  8. Java并发编程的艺术(四)——JMM、重排序、happens-before

    什么是JMM JMM就是Java内存模型.目的是为了屏蔽系统和硬件的差异,让同一代码在不同平台下能够达到相同的访问结果.规定了线程和内存之间的关系. 内存划分 JMM规定了内存主要划分为主内存和工作内 ...

  9. 转载:c# 获取CPU温度(非WMI,直接读取硬件)

    c#获取cpu温度 很早一个项目做远控,所以需要用到获取cpu温度,但是就是不知从何下手,无意中发现了Open Hardware Monitor,令我的项目成功完成 亲测20台清装xp sp2的机器, ...

  10. 配置nginx支持THINKPHP的PATH_INFO

    ThinkPHP支持通过PATHINFO和URL rewrite的方式来提供友好的URL,但是Nginx中默认是不支持PATHINFO的,所以我们需要修改nginx.conf文件. location ...