flowable的多人会签和一票否决
项目结构:
接下来代码: 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的多人会签和一票否决的更多相关文章
- flowable 实现多实例-会签-动态配置人员 参考demo
会签 即多人执行当前任务 设置判断数 通过 例如:设置了是半数通过即可通过当前节点 如果当前是4人那就是2人即通过 如果是6人那就是三人即通过 如果是5人 即三人通过 看各位的判断值是如何书写 这个值 ...
- BL老师的建议,数学不好的,大数据一票否决--后赋从java转大数据
__________________________ 作者:我是蛋蛋链接:https://www.zhihu.com/question/59593387/answer/167235075来源:知乎著作 ...
- 基于camunda开源流程引擎如何实现会签及会签原理解析
一.背景 市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.由于jbpm.activiti.flowable这几个流程引擎出现的比较早, ...
- Activiti 工作流会签开发设计思路
http://man1900.iteye.com/blog/1607753 在流程业务管理中,任务是通常都是由一个人去处理的,而多个人同时处理一个任务,这种任务我们称之为会签任务.这种业务需求也很常见 ...
- flowable中传入审批人是list
package org.springblade.flow.engine.listener; import org.flowable.engine.delegate.DelegateExecution; ...
- PHP 高并发、抢票、秒杀 解决方案
对于抢票.秒杀这种业务,我说说自己对这种高并发的理解吧,这里提出个人认为比较可行的几个方案:方案一:使用队列来实现可以基于例如MemcacheQ等这样的消息队列,具体的实现方案这么表述吧比如有100张 ...
- 并发编程 - 进程 - 1.互斥锁/2.模拟抢票/3.互斥锁与join区别
1.互斥锁: 互斥锁:Lock 原理就是把并发变成串行,一个一个运行,不错乱,但效率低 保证多个进程修改一块数据时,大家是一个一个修改,不错乱 mutex.acquire() mutex.releas ...
- JDOJ 1928: 排队买票
JDOJ 1928: 排队买票 JDOJ传送门 Description 一场演唱会即将举行.现有n个歌迷排队买票,一个人买一张,而售票处规定,一个人每次最多只能买两张票.假设第i位歌迷买一张票需要时间 ...
- Activiti7 回退与会签
1. 回退(驳回) 回退的思路就是动态更改节点的流向.先遇水搭桥,最后再过河拆桥. 具体操作如下: 取得当前节点的信息 取得当前节点的上一个节点的信息 保存当前节点的流向 新建流向,由当前节点指向上 ...
随机推荐
- 遇见BUG如何区分前后端
定位前后端bug: 1.经验法: 软件测试人员应不断精进自己的技能,负责的项目多了,自然对功能的实现过程有了解,也就明白如何分类bug了. 例如: 网页上的某个图片的分辨率不对,如果我们了解实现过程, ...
- Razorpay支付对接,JAVA对接篇
Razorpay 作为印度本土的一家支付公司,类似中国的支付宝 微信,本篇记录一下对接印度第三方支付公司 准备工作: 注册公司 申请Razorpay账号 申请正式环境 Razorpay工作台: 获取k ...
- OllyDbg使用入门
OllyDbg的四个窗口: http://www.360doc.com/content/16/0913/07/16447955_590419156.shtml 反汇编窗口:显示被调试程序的反汇编代码, ...
- 一条 sql 的执行过程详解
写操作执行过程 如果这条sql是写操作(insert.update.delete),那么大致的过程如下,其中引擎层是属于 InnoDB 存储引擎的,因为InnoDB 是默认的存储引擎,也是主流的,所以 ...
- Django----Modelviewset继承
1.modelviewset 认证.权限.限流.序列化.分页.过滤.排序 modelviewset的应用场景是: 1.主要应用于数据接口 2.对数据库的增删改查 3.在视图函数中没有很多业务逻辑需要来 ...
- Java读书计划和分享
写在前面 为什么要写这些呢? 接触java已经有三年多了,感触颇多,比如从0到60,只要勤实践.勤思考,很快就可以入门,从60分到满分极致,则单单不是凭借工作年限或者什么就可以.曾经也有过一段迷茫时期 ...
- PyQt(Python+Qt)学习随笔:containers容器类部件QMdiArea多文档界面的QMdiSubWindow子窗口相关属性和操作方法
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 1.增加子窗口 QMdiArea中的子窗口类型是QMdiSubWind ...
- PyQt(Python+Qt)学习随笔:Designer中的QDialogButtonBox的StandardButtons标准按钮
在Qt Designer中,可以在界面中使用QDialogButtonBox来配置一组按钮进行操作,Qt中为QDialogButtonBox定义了一组常用的标准按钮,可以在Designer中直接在St ...
- tcp socket学习
更新一波学的socket编程,socket还是比较重要的,探测端口,连接服务底层都是socket编程.tcp有server 和 client.client和udp发送差不多. server端是建立了两 ...
- KM 算法
KM 算法 可能需要先去学学匈牙利算法等二分图相关知识. 模板题-洛谷P6577 [模板]二分图最大权完美匹配 给 \(n\) 和 \(m\) 与边 \(u_i,v_i,w_i(1\le i\le m ...