说说在 jBPM 工作流中如何实现【会签】功能
发布日期:2021-06-29 21:11:36 浏览次数:2 分类:技术文章

本文共 7518 字,大约阅读时间需要 25 分钟。

会签(会审),指的是在流程中某个业务需要经过多人表决,并且根据表决意见的汇总结果以及设定的规则,来决定流程的走向,它是审批流程中常见的需求。

会签可以分为两种:

  1. 单步 - 只使用一个活动来处理。
  2. 多步 - 由多个活动所组成的。

单步会签比较常见,也容易实现,主要的解决方案是在会签活动的主任务基础上,动态创建若干个子任务来实现,它的具体解决方案是:

  1. 编写专门用于会签活动的任务分配处理器(实现 AssignmentHandler),在这个处理器中,通过流程变量来获取参加会议的用户 id,并为这些用户动态地创建 “会签任务” 对象。
  2. 编写完成 “会签任务” 的命令,在命令中设定会签的业务逻辑。

会签的业务逻辑有以下 4 种情况:

情况 说明
一票否决制 参加会签的用户中,只要有一个人不同意,会签就结束,进入 “会签否决” 转移;如果所有人都同意,则进入 “会签通过” 转移。
一票通过制 逻辑与 “一票否决制”相反。
按比例否决制 全部参加会签的用户提交任务后,根据提交的意见,按比例来决定是否进入 “会签否决” 转移。
意见收集制 简单,全部会签用户通过任务表单提交任务后,收集这些意见,然后结束任务。

多步会签,相对较复杂,建议使用动态创建子流程的方式来实现。

至于更复杂的业务场景,比如将第三方业务系统接入会签,可以考虑使用 JMS 活动来发送消息,并监听第三方业务系统的应答模式,异步地实现会签需求。

会签流程定义

jPDL:

  • assignment-handler 定义了任务分配处理器,它会根据参与者(participants)动态地创建出相应的子任务。
  • 可以在 participants 设定为流程变量的值,这样就可以动态地决定参与会签的用户啦(通过上一步任务表单来选定会签的任务)
  • 会签被否决,则流程结束。
  • 全体通过会签,则进入【执行】活动。

会签活动的任务处理器:

public class JointSignAssignment implements AssignmentHandler {    //会签参与者 ID 列表(在流程定义中注入)    private List
participants; //任务服务 private final static TaskService taskService = Configuration.getProcessEngine() .getTaskService(); @Override public void assign(Assignable assignable, OpenExecution execution) throws Exception { String instanceId = execution.getProcessInstance().getId(); //获取会签活动任务对象 Task task = taskService.createTaskQuery().processInstanceId(instanceId) .activityName(execution.getName()).uniqueResult(); //创建会签子任务 createSubTasks3(task); } ...}

创建会签子任务有三种实现方法。

基于主任务:

private void createSubTasks(Task task) {	if (participants == null) {		return;	}	for (String participant : participants) {		//基于主任务,创建会签子任务		Task subTask = taskService.newTask(task.getId());		//设置会签参与者为子任务的可处理者		subTask.setAssignee(participant);		taskService.addTaskParticipatingUser(task.getId(), participant, Participation.CLIENT);	}}

这样做有问题,因为这个 taskService.newTask() 方法会立即持久化子任务及其历史,而此时的主任务还未提交。因此这样创建的子任务无法关联到主任务,会抛出持久化异常。

脱离主任务:

private void createSubTasks2(Task task) {	if (participants == null) {		return;	}	for (String participant : participants) {		//创建独立的会签子任务		Task subTask = taskService.newTask();		//设置会签参与者为子任务的可处理者		subTask.setAssignee(participant);		taskService.addTaskParticipatingUser(task.getId(), participant, Participation.CLIENT);	}}

这样也有问题,因为这样凭空创建的任务,虽然不会在持久化时出现异常,但它无法关联到主任务。这样创建的任务其实是孤立的,这在后续的会签操作、级联删除以及历史分析,都会出现很大的问题。

使用主任务的 Task 对象:

private void createSubTasks3(Task task) {	if (participants == null) {		return;	}	for (String participant : participants) {		//使用主任务的 Task 对象的 createSubTask 方法(不会持久化)来创建会签子任务		Task subTask = ((OpenTask)task).createSubTask();		//设置会签参与者为子任务的可处理者		subTask.setAssignee(participant);		//关联会签任务到主任务		taskService.addTaskParticipatingUser(task.getId(), participant, Participation.CLIENT);	}}

这样做既可以关联到主任务以及流程实例,又可以随着主任务一同被持久化。就选这种方法啦O(∩_∩)O哈哈~

基于【一票否决制】的会签任务提交命令类设计如下:

public class SubmitJoinSignTaskCmd implements Command
{ //传递会签意见的任务变量 public static final String VAR_SIGN = "sign"; //会签通过时的转移路径名称(由构造函数传入) private String passTransitionName; //会签否决时的转移路径名称(由构造函数传入) private String noPassTransitionName; //主任务 ID(由构造函数传入) private String mainTaskId; //主任务对象 private Task mainTask; //流程实例 ID private String instanceId; //会签任务对象(Setter 方法传入) private Task joinSignTask; public void setJoinSignTask(Task joinSignTask) { this.joinSignTask = joinSignTask; } public String getInstanceId() { return instanceId; } public SubmitJoinSignTaskCmd(String mainTaskId, String passTransitionName, String noPassTransitionName) { this.mainTaskId = mainTaskId; this.passTransitionName = passTransitionName; this.noPassTransitionName = noPassTransitionName; } @Override public Boolean execute(Environment environment) throws Exception { //获取任务服务 TaskService taskService = environment.get(TaskService.class); //获取主任务与流程实例 ID mainTask = taskService.getTask(mainTaskId); instanceId = mainTask.getExecutionId(); //获取当前会签任务 String joinSignTaskId = joinSignTask.getId(); //从当前会签任务的任务变量中获取 ”会签意见“ String sign = (String) taskService.getVariable(joinSignTaskId, VAR_SIGN); //规则如下:如果意见为“不同意”,则表示否决 if (sign != null && sign.equals("不同意")) { //存在否决意见,则会签活动结束(一票否决制) //结束会签任务 taskService.completeTask(joinSignTaskId); //为主任务增加一条会签意见记录(注释) taskService.addTaskComment(mainTaskId, "用户:" + joinSignTask.getAssignee() + ",会签意见:" + sign); //结束主任务,流向【否决】转移 taskService.completeTask(mainTaskId, noPassTransitionName); //会签结束 return true; } /** * 通过会签 */ //完成会签任务 taskService.completeTask(joinSignTaskId); //为主任务增加一条会签意见 taskService.addTaskComment(mainTaskId,"用户:"+joinSignTask.getAssignee()+ ";会签意见为:"+sign); //判定是否还有会签子任务 if(taskService.getSubTasks(mainTaskId).isEmpty()){//通过会签 //结束主任务,流向会签通过转移 taskService.completeTask(mainTaskId,passTransitionName); return true; }else{//会签活动还未结束 return false; } }}

单元测试:

//发起流程实例ProcessInstance processInstance=executionService.startProcessInstanceByKey("JointlySign");instanceId=processInstance.getId();//实例 ID//获取会签主任务Task task=taskService.createTaskQuery().processInstanceId(instanceId)		.activityName(processInstance.findActiveActivityNames().iterator().next()		).uniqueResult();//断言当前活动为会签assertTrue(processInstance.isActive("会签"));List
subTasks=taskService.getSubTasks(task.getId());//断言主任务产生了 3 条子任务assertEquals(3, subTasks.size());//获取主任务 IDString taskId=task.getId();//创建会签任务命令,指定会签通过转移以及会签否决转移cmd=new SubmitJoinSignTaskCmd(taskId,"to execute","to end");

至此又分为两种情况:

否决会签:

//获取会签用户 Deniro 的任务Task deniroTask=taskService.findPersonalTasks("Deniro").get(0);//通过变量来模拟否决会签Map
vars=new HashMap<>();vars.put(SubmitJoinSignTaskCmd.VAR_SIGN,"不同意");taskService.setVariables(deniroTask.getId(), vars);cmd.setJoinSignTask(deniroTask);//提交会签任务(执行自定义命令)boolean result= Configuration.getProcessEngine().execute(cmd);//断言会签活动已完成assertTrue(result);//断言流程实例结束assertProcessInstanceEnded(cmd.getInstanceId());

通过会签:

//获取会签用户 Deniro 的任务Task deniroTask=taskService.findPersonalTasks("Deniro").get(0);cmd.setJoinSignTask(deniroTask);//不设置否决意见,即通过//提交会签任务(执行自定义命令)boolean result= Configuration.getProcessEngine().execute(cmd);//断言会签任务未完成assertFalse(result);//获取会签用户 Jack 的任务Task jackTask=taskService.findPersonalTasks("Jack").get(0);cmd.setJoinSignTask(jackTask);//不设置否决意见,即通过//提交会签任务(执行自定义命令)result= Configuration.getProcessEngine().execute(cmd);//断言会签任务未完成assertFalse(result);//获取会签用户 Lucy 的任务Task lucyTask=taskService.findPersonalTasks("Lucy").get(0);cmd.setJoinSignTask(lucyTask);//不设置否决意见,即通过//提交会签任务(执行自定义命令)result= Configuration.getProcessEngine().execute(cmd);//断言会签活动已完成assertTrue(result);//断言流程实例到达【执行】活动ProcessInstance processInstance=executionService.findProcessInstanceById		(instanceId);assertTrue(processInstance.isActive("执行"));//完成【执行】活动String executionId=processInstance.findActiveExecutionIn("执行").getId();executionService.signalExecutionById(executionId);//断言流程实例结束assertProcessInstanceEnded(instanceId);

现在清楚了吧O(∩_∩)O哈哈~

转载地址:https://deniro.blog.csdn.net/article/details/80634179 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:说说如何搭建 HTTP 服务器
下一篇:说说在 Android 中如何发送 HTTP 请求

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月15日 09时02分24秒