diff --git a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml
index 011dd23f5..630835c0b 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml
+++ b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml
@@ -64,5 +64,18 @@
cn.iocoder.boot
yudao-spring-boot-starter-flowable
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+ com.h2database
+ h2
+ 1.4.196
+ test
+
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/AbstractOATest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/AbstractOATest.java
new file mode 100644
index 000000000..f8dae183b
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/AbstractOATest.java
@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.bpm;
+
+import org.flowable.engine.*;
+import org.flowable.engine.impl.ProcessEngineImpl;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.test.TestHelper;
+import org.flowable.engine.test.FlowableRule;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+
+/**
+ * 抽象测试基类
+ *
+ * @author henryyan cuicui
+ */
+public abstract class AbstractOATest {
+
+ protected String configurationResource = "flowable.cfg.xml";
+
+ /**
+ * 专门用于测试套件
+ */
+ @Rule
+ public FlowableRule activitiRule = new FlowableRule("flowable.cfg.xml");
+
+ protected ProcessEngineConfigurationImpl processEngineConfiguration;
+
+ protected ProcessEngine processEngine;
+ protected RepositoryService repositoryService;
+ protected RuntimeService runtimeService;
+ protected TaskService taskService;
+ protected HistoryService historyService;
+ protected IdentityService identityService;
+ protected ManagementService managementService;
+ protected FormService formService;
+
+ /**
+ * 开始测试
+ */
+ @BeforeClass
+ public static void setUpForClass() throws Exception {
+ System.out.println("++++++++ 开始测试 ++++++++");
+ }
+
+ /**
+ * 结束测试
+ */
+ @AfterClass
+ public static void testOverForClass() throws Exception {
+ System.out.println("-------- 结束测试 --------");
+ }
+
+ protected void initializeProcessEngine() {
+ processEngine = TestHelper.getProcessEngine(configurationResource);
+ }
+
+ /**
+ * 初始化变量
+ */
+ @Before
+ public void setUp() throws Exception {
+ if (processEngine == null) {
+ initializeProcessEngine();
+ }
+
+ processEngineConfiguration = ((ProcessEngineImpl) processEngine).getProcessEngineConfiguration();
+ repositoryService = processEngine.getRepositoryService();
+ runtimeService = processEngine.getRuntimeService();
+ taskService = processEngine.getTaskService();
+ historyService = processEngine.getHistoryService();
+ identityService = processEngine.getIdentityService();
+ managementService = processEngine.getManagementService();
+ formService = processEngine.getFormService();
+ }
+
+}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/ConditionTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/ConditionTest.java
new file mode 100644
index 000000000..1dd73e712
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/ConditionTest.java
@@ -0,0 +1,226 @@
+package cn.iocoder.yudao.module.bpm;
+
+import org.flowable.engine.history.HistoricDetail;
+import org.flowable.engine.history.HistoricFormProperty;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.history.HistoricVariableUpdate;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.joda.time.DateTime;
+import org.junit.Test;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * 条件测试
+ * 1 某个节点-支付额度需要大于70%
+ * 2 支付条件完成后,开始倒计时15天,要完成流程处理
+ * @author cuicui
+ */
+public class ConditionTest extends AbstractOATest {
+
+ @Test
+ @Deployment(resources = {"chapter6/leave-timeLimit-money/leave-formkey-ext.bpmn", "chapter6/leave-timeLimit-money/leave-start.form",
+ "chapter6/leave-timeLimit-money/approve-deptLeader.form", "chapter6/leave-timeLimit-money/approve-hr.form", "chapter6/leave-timeLimit-money/report-back.form",
+ "chapter6/leave-timeLimit-money/modify-apply.form"})
+ public void allPass() throws Exception {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Map variables = new HashMap();
+ Calendar ca = Calendar.getInstance();
+ String startDate = sdf.format(ca.getTime());
+ ca.add(Calendar.DAY_OF_MONTH, 2); // 当前日期加2天
+ String endDate = sdf.format(ca.getTime());
+
+ // 启动流程
+ variables.put("startDate", startDate);
+ variables.put("endDate", endDate);
+ variables.put("reason", "公休");
+
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+
+ // 读取启动表单
+ Object renderedStartForm = formService.getRenderedStartForm(processDefinition.getId());
+ assertNotNull(renderedStartForm);
+
+ // 启动流程
+ // 设置当前用户
+ String currentUserId = "henryyan";
+ identityService.setAuthenticatedUserId(currentUserId);
+ ProcessInstance processInstance = formService.submitStartFormData(processDefinition.getId(), variables);
+ assertNotNull(processInstance);
+
+ // 部门领导审批通过
+ Task deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult();
+ assertNotNull(formService.getRenderedTaskForm(deptLeaderTask.getId()));
+ variables = new HashMap();
+ variables.put("deptLeaderApproved", "true");
+ formService.submitTaskFormData(deptLeaderTask.getId(), variables);
+
+ // 人事审批通过
+ Task hrTask = taskService.createTaskQuery().taskCandidateGroup("hr").singleResult();
+ assertNotNull(formService.getRenderedTaskForm(hrTask.getId()));
+ variables = new HashMap();
+ variables.put("hrApproved", "true");
+ //手动设置支付金额
+ variables.put("amountMoney", "19999");
+ formService.submitTaskFormData(hrTask.getId(), variables);
+ //判断支付金额是否>1万元
+ // 财务打款通过
+ Task caiwuTask = taskService.createTaskQuery().taskCandidateGroup("caiwu").singleResult();
+ printTask(caiwuTask);
+ taskService.complete(caiwuTask.getId());
+ //判断倒计时15天
+ Task chuNaTask = taskService.createTaskQuery().taskCandidateGroup("chuNa").singleResult();
+ printTask(chuNaTask);
+ taskService.complete(chuNaTask.getId());
+
+ // 销假(根据申请人的用户ID读取)
+// Task reportBackTask = taskService.createTaskQuery().taskAssignee(currentUserId).singleResult();
+// assertNotNull(formService.getRenderedTaskForm(reportBackTask.getId()));
+// variables = new HashMap();
+// variables.put("reportBackDate", sdf.format(ca.getTime()));
+// formService.submitTaskFormData(reportBackTask.getId(), variables);
+
+ // 验证流程是否已经结束
+ HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().finished().singleResult();
+ assertNotNull(historicProcessInstance);
+
+ // 读取历史变量
+ Map historyVariables = packageVariables(processInstance);
+
+ // 验证执行结果
+ assertEquals("ok", historyVariables.get("result"));
+
+ }
+
+ /**
+ * 查询过期任务
+ * @throws Exception
+ */
+ @Test
+ @Deployment(resources = {"chapter6/leave-timeLimit-money/leave-formkey-ext.bpmn", "chapter6/leave-timeLimit-money/leave-start.form",
+ "chapter6/leave-timeLimit-money/approve-deptLeader.form", "chapter6/leave-timeLimit-money/approve-hr.form", "chapter6/leave-timeLimit-money/report-back.form",
+ "chapter6/leave-timeLimit-money/modify-apply.form"})
+ public void dueDate() throws Exception {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Map variables = new HashMap();
+ Calendar ca = Calendar.getInstance();
+ String startDate = sdf.format(ca.getTime());
+ ca.add(Calendar.DAY_OF_MONTH, 2); // 当前日期加2天
+ String endDate = sdf.format(ca.getTime());
+
+ // 启动流程
+ variables.put("startDate", startDate);
+ variables.put("endDate", endDate);
+ variables.put("reason", "公休");
+
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+
+ // 读取启动表单
+ Object renderedStartForm = formService.getRenderedStartForm(processDefinition.getId());
+ assertNotNull(renderedStartForm);
+
+ // 启动流程
+ // 设置当前用户
+ String currentUserId = "henryyan";
+ identityService.setAuthenticatedUserId(currentUserId);
+ ProcessInstance processInstance = formService.submitStartFormData(processDefinition.getId(), variables);
+ assertNotNull(processInstance);
+
+ // 部门领导审批通过
+ Task deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult();
+ assertNotNull(formService.getRenderedTaskForm(deptLeaderTask.getId()));
+ variables = new HashMap();
+ variables.put("deptLeaderApproved", "true");
+ formService.submitTaskFormData(deptLeaderTask.getId(), variables);
+
+ // 人事审批通过
+ Task hrTask = taskService.createTaskQuery().taskCandidateGroup("hr").singleResult();
+ assertNotNull(formService.getRenderedTaskForm(hrTask.getId()));
+ variables = new HashMap();
+ variables.put("hrApproved", "true");
+ //手动设置支付金额
+ variables.put("amountMoney", "19999");
+ formService.submitTaskFormData(hrTask.getId(), variables);
+ //判断支付金额是否>1万元
+ // 财务打款通过
+ Task caiwuTask = taskService.createTaskQuery().taskCandidateGroup("caiwu").singleResult();
+ printTask(caiwuTask);
+
+ //设置5天前就过期了
+ DateTime dateTime = DateTime.now();
+ DateTime minusDays = dateTime.minusDays(5);
+ taskService.setDueDate(caiwuTask.getId(),minusDays.toDate());
+ //查询今天以前的过期任务
+ List listTask = taskService.createTaskQuery().taskDueBefore(new Date()).list();
+ for (Task task : listTask) {
+ printTask(task);
+ }
+
+ // 销假(根据申请人的用户ID读取)
+// Task reportBackTask = taskService.createTaskQuery().taskAssignee(currentUserId).singleResult();
+// assertNotNull(formService.getRenderedTaskForm(reportBackTask.getId()));
+// variables = new HashMap();
+// variables.put("reportBackDate", sdf.format(ca.getTime()));
+// formService.submitTaskFormData(reportBackTask.getId(), variables);
+
+ // 验证流程是否已经结束
+ HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().finished().singleResult();
+ assertNotNull(historicProcessInstance);
+
+ // 读取历史变量
+ Map historyVariables = packageVariables(processInstance);
+
+ // 验证执行结果
+ assertEquals("ok", historyVariables.get("result"));
+
+ }
+
+ /**
+ * 任意流程的跳转
+ */
+ @Test
+ public void taskJump(){
+ // 当前任务
+ String taskId="ddd";
+ Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
+ String assignee = "下一个自由跳转人";
+ taskService.setAssignee(taskId,assignee);
+ // 自由跳转
+ String taskDefKey="目标-任务名称";
+ //moveActivityIdTo的两个参数,源任务key,目标任务key
+ runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveActivityIdTo(task.getTaskDefinitionKey(), taskDefKey).changeState();
+
+ }
+
+
+ /**
+ * 读取历史变量并封装到Map中
+ */
+ private Map packageVariables(ProcessInstance processInstance) {
+ Map historyVariables = new HashMap();
+ List list = historyService.createHistoricDetailQuery().processInstanceId(processInstance.getId()).list();
+ for (HistoricDetail historicDetail : list) {
+ if (historicDetail instanceof HistoricFormProperty) {
+ // 表单中的字段
+ HistoricFormProperty field = (HistoricFormProperty) historicDetail;
+ historyVariables.put(field.getPropertyId(), field.getPropertyValue());
+ System.out.println("form field: taskId=" + field.getTaskId() + ", " + field.getPropertyId() + " = " + field.getPropertyValue());
+ } else if (historicDetail instanceof HistoricVariableUpdate) {
+ HistoricVariableUpdate variable = (HistoricVariableUpdate) historicDetail;
+ historyVariables.put(variable.getVariableName(), variable.getValue());
+ System.out.println("variable: " + variable.getVariableName() + " = " + variable.getValue());
+ }
+ }
+ return historyVariables;
+ }
+ private void printTask(Task task){
+ System.out.println(task.getName()+" : " + task.getDueDate());
+ }
+}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/LeaveFormKeyTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/LeaveFormKeyTest.java
new file mode 100644
index 000000000..da99fec3d
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/LeaveFormKeyTest.java
@@ -0,0 +1,177 @@
+package cn.iocoder.yudao.module.bpm;
+
+import org.flowable.engine.history.HistoricDetail;
+import org.flowable.engine.history.HistoricFormProperty;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.history.HistoricVariableUpdate;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.junit.Test;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author henryyan
+ */
+public class LeaveFormKeyTest extends AbstractOATest {
+
+ @Test
+ @Deployment(resources = {"chapter6/leave-formkey/leave-formkey.bpmn", "chapter6/leave-formkey/leave-start.form",
+ "chapter6/leave-formkey/approve-deptLeader.form", "chapter6/leave-formkey/approve-hr.form", "chapter6/leave-formkey/report-back.form",
+ "chapter6/leave-formkey/modify-apply.form"})
+ public void allPass() throws Exception {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Map variables = new HashMap();
+ Calendar ca = Calendar.getInstance();
+ String startDate = sdf.format(ca.getTime());
+ ca.add(Calendar.DAY_OF_MONTH, 2); // 当前日期加2天
+ String endDate = sdf.format(ca.getTime());
+
+ // 启动流程
+ variables.put("startDate", startDate);
+ variables.put("endDate", endDate);
+ variables.put("reason", "公休");
+
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+
+ // 读取启动表单
+ Object renderedStartForm = formService.getRenderedStartForm(processDefinition.getId());
+ assertNotNull(renderedStartForm);
+
+ // 启动流程
+ // 设置当前用户
+ String currentUserId = "henryyan";
+ identityService.setAuthenticatedUserId(currentUserId);
+ ProcessInstance processInstance = formService.submitStartFormData(processDefinition.getId(), variables);
+ assertNotNull(processInstance);
+
+ // 部门领导审批通过
+ Task deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult();
+ assertNotNull(formService.getRenderedTaskForm(deptLeaderTask.getId()));
+ variables = new HashMap();
+ variables.put("deptLeaderApproved", "true");
+ formService.submitTaskFormData(deptLeaderTask.getId(), variables);
+
+ // 人事审批通过
+ Task hrTask = taskService.createTaskQuery().taskCandidateGroup("hr").singleResult();
+ assertNotNull(formService.getRenderedTaskForm(hrTask.getId()));
+ variables = new HashMap();
+ variables.put("hrApproved", "true");
+ formService.submitTaskFormData(hrTask.getId(), variables);
+
+ // 销假(根据申请人的用户ID读取)
+ Task reportBackTask = taskService.createTaskQuery().taskAssignee(currentUserId).singleResult();
+ assertNotNull(formService.getRenderedTaskForm(reportBackTask.getId()));
+ variables = new HashMap();
+ variables.put("reportBackDate", sdf.format(ca.getTime()));
+ formService.submitTaskFormData(reportBackTask.getId(), variables);
+
+ // 验证流程是否已经结束
+ HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().finished().singleResult();
+ assertNotNull(historicProcessInstance);
+
+ // 读取历史变量
+ Map historyVariables = packageVariables(processInstance);
+
+ // 验证执行结果
+ assertEquals("ok", historyVariables.get("result"));
+
+ }
+
+ /**
+ * 任意流程的跳转
+ */
+ @Test
+ public void taskJump(){
+ // 当前任务
+ String taskId="ddd";
+ Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
+ String assignee = "下一个自由跳转人";
+ taskService.setAssignee(taskId,assignee);
+ // 自由跳转
+ String taskDefKey="目标-任务名称";
+ //moveActivityIdTo的两个参数,源任务key,目标任务key
+ runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveActivityIdTo(task.getTaskDefinitionKey(), taskDefKey).changeState();
+
+ }
+
+ /**
+ * 领导驳回后申请人取消申请
+ */
+ @Test
+ @Deployment(resources = "chapter6/dynamic-form/leave.bpmn")
+ public void cancelApply() throws Exception {
+
+ // 设置当前用户
+ String currentUserId = "henryyan";
+ identityService.setAuthenticatedUserId(currentUserId);
+
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Map variables = new HashMap();
+ Calendar ca = Calendar.getInstance();
+ String startDate = sdf.format(ca.getTime());
+ ca.add(Calendar.DAY_OF_MONTH, 2);
+ String endDate = sdf.format(ca.getTime());
+
+ // 启动流程
+ variables.put("startDate", startDate);
+ variables.put("endDate", endDate);
+ variables.put("reason", "公休");
+ ProcessInstance processInstance = formService.submitStartFormData(processDefinition.getId(), variables);
+ assertNotNull(processInstance);
+
+ // 部门领导审批通过
+ Task deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult();
+ variables = new HashMap();
+ variables.put("deptLeaderApproved", "false");
+ formService.submitTaskFormData(deptLeaderTask.getId(), variables);
+
+ // 调整申请
+ Task modifyApply = taskService.createTaskQuery().taskAssignee(currentUserId).singleResult();
+ variables = new HashMap();
+ variables.put("reApply", "false");
+ variables.put("startDate", startDate);
+ variables.put("endDate", endDate);
+ variables.put("reason", "公休");
+ formService.submitTaskFormData(modifyApply.getId(), variables);
+
+ // 读取历史变量
+ Map historyVariables = packageVariables(processInstance);
+
+ // 验证执行结果
+ assertEquals("canceled", historyVariables.get("result"));
+
+ }
+
+ /**
+ * 读取历史变量并封装到Map中
+ */
+ private Map packageVariables(ProcessInstance processInstance) {
+ Map historyVariables = new HashMap();
+ List list = historyService.createHistoricDetailQuery().processInstanceId(processInstance.getId()).list();
+ for (HistoricDetail historicDetail : list) {
+ if (historicDetail instanceof HistoricFormProperty) {
+ // 表单中的字段
+ HistoricFormProperty field = (HistoricFormProperty) historicDetail;
+ historyVariables.put(field.getPropertyId(), field.getPropertyValue());
+ System.out.println("form field: taskId=" + field.getTaskId() + ", " + field.getPropertyId() + " = " + field.getPropertyValue());
+ } else if (historicDetail instanceof HistoricVariableUpdate) {
+ HistoricVariableUpdate variable = (HistoricVariableUpdate) historicDetail;
+ historyVariables.put(variable.getVariableName(), variable.getValue());
+ System.out.println("variable: " + variable.getVariableName() + " = " + variable.getValue());
+ }
+ }
+ return historyVariables;
+ }
+}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/MultiInstancesTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/MultiInstancesTest.java
new file mode 100644
index 000000000..4244db086
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/MultiInstancesTest.java
@@ -0,0 +1,208 @@
+package cn.iocoder.yudao.module.bpm;
+
+import org.flowable.engine.history.HistoricDetail;
+import org.flowable.engine.history.HistoricFormProperty;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.history.HistoricVariableUpdate;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.junit.Test;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author henryyan
+ * testMultiInstanceForUserTask 会签
+ * cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmUserTaskActivityBehavior#handleAssignments(org.flowable.task.service.TaskService, java.lang.String, java.lang.String, java.util.List, java.util.List, org.flowable.task.service.impl.persistence.entity.TaskEntity, org.flowable.common.engine.impl.el.ExpressionManager, org.flowable.engine.delegate.DelegateExecution, org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl)
+ *
+ * 发生了死锁
+ * cn.iocoder.yudao.module.bpm.controller.admin.task.BpmProcessInstanceController#createProcessInstance(cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO)
+ * 执行了两次,任务分配到了同一个人
+ */
+public class MultiInstancesTest extends AbstractOATest {
+
+ /**
+ * Java Service多实例(是否顺序结果一样)
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceFixedNumbers.bpmn"})
+ public void testParallel() throws Exception {
+ Map variables = new HashMap();
+ long loop = 3;
+ variables.put("loop", loop);
+ variables.put("counter", 0); // 计数器
+ ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testMultiInstanceFixedNumbers", variables);
+ Object variable = runtimeService.getVariable(processInstance.getId(), "counter");
+ assertEquals(loop, variable);
+ }
+
+ /**
+ * 用户任务多实例--顺序
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceForUserTask.sequential.bpmn"})
+ public void testForUserSequence() throws Exception {
+ ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testMultiInstanceForUserTask");
+ long count = taskService.createTaskQuery().processInstanceId(processInstance.getId()).count();
+ assertEquals(1, count);
+
+ Task task = taskService.createTaskQuery().singleResult();
+ taskService.complete(task.getId());
+ count = taskService.createTaskQuery().processInstanceId(processInstance.getId()).count();
+ assertEquals(1, count);
+
+ task = taskService.createTaskQuery().singleResult();
+ taskService.complete(task.getId());
+ count = taskService.createTaskQuery().processInstanceId(processInstance.getId()).count();
+ assertEquals(1, count);
+
+ task = taskService.createTaskQuery().singleResult();
+ taskService.complete(task.getId());
+ count = taskService.createTaskQuery().processInstanceId(processInstance.getId()).count();
+ assertEquals(0, count);
+ }
+
+ /**
+ * 用户任务多实例--并行
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceForUserTask.nosequential.bpmn"})
+ public void testForUserNoSequential() throws Exception {
+ ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testMultiInstanceForUserTask");
+ long count = taskService.createTaskQuery().processInstanceId(processInstance.getId()).count();
+ assertEquals(3, count);
+ }
+
+ /**
+ * 用户任务多实例,通过用户数量决定实例个数--并行
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceForUserTask.users.nosequential.bpmn"})
+ public void testForUserCreateByUsersNoSequential() throws Exception {
+ Map variables = new HashMap();
+ List users = Arrays.asList("user1", "user2", "user3");
+ variables.put("users", users);
+ runtimeService.startProcessInstanceByKey("testMultiInstanceForUserTask", variables);
+ for (String userId : users) {
+ assertEquals(1, taskService.createTaskQuery().taskAssignee(userId).count());
+ }
+ }
+
+ /**
+ * 用户任务多实例,通过用户数量决定实例个数--顺序
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.bpmn"})
+ public void testForUserCreateByUsersSequential() throws Exception {
+ Map variables = new HashMap();
+ List users = Arrays.asList("user1", "user2", "user3");
+ variables.put("users", users);
+ runtimeService.startProcessInstanceByKey("testMultiInstanceForUserTask", variables);
+ for (String userId : users) {
+ Task task = taskService.createTaskQuery().taskAssignee(userId).singleResult();
+ taskService.complete(task.getId());
+ }
+ }
+
+ /**
+ * 用户任务多实例,按照任务完成的百分比比率决定是否提前结束流程
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.bpmn"})
+ public void testForUserCreateByUsersSequentialWithCompleteCondition() throws Exception {
+ Map variables = new HashMap();
+ List users = Arrays.asList("user1", "user2", "user3");
+ variables.put("users", users);
+ variables.put("rate", 0.6d);
+ runtimeService.startProcessInstanceByKey("testMultiInstanceForUserTask", variables);
+
+ Task task = taskService.createTaskQuery().taskAssignee("user1").singleResult();
+ taskService.complete(task.getId());
+
+ task = taskService.createTaskQuery().taskAssignee("user2").singleResult();
+ taskService.complete(task.getId());
+
+ long count = historyService.createHistoricProcessInstanceQuery().finished().count();
+ assertEquals(1, count);
+
+ }
+
+ /**
+ * 用户任务多实例,按照任务完成的百分比比率决定是否提前结束流程
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/testMultiInstanceForUserTask.exception.bpmn"})
+ public void testForUserCreateByUsersException() throws Exception {
+ Map variables = new HashMap();
+ List users = Arrays.asList("user1", "user2", "user3");
+ variables.put("users", users);
+ runtimeService.startProcessInstanceByKey("testMultiInstanceForUserTask", variables);
+
+ Task task = taskService.createTaskQuery().taskAssignee("user1").singleResult();
+ taskService.complete(task.getId());
+
+ task = taskService.createTaskQuery().taskAssignee("user2").singleResult();
+ taskService.complete(task.getId());
+
+ task = taskService.createTaskQuery().taskAssignee("user3").singleResult();
+ taskService.complete(task.getId());
+
+ List list = taskService.createTaskQuery().list();
+ for (Task task2 : list) {
+ System.out.println("============" + task2.getName());
+ }
+
+ }
+ /////////////////////////////////////////////////
+ /**
+ * 全部通过
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/leave-countersign.bpmn"})
+ public void testAllApproved() throws Exception {
+ Map variables = new HashMap();
+ List users = Arrays.asList("groupLeader", "deptLeader", "hr");
+ variables.put("users", users);
+ identityService.setAuthenticatedUserId("henryyan");
+ ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave-countersign", variables);
+ for (String user : users) {
+ Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee(user).singleResult();
+ Map taskVariables = new HashMap();
+ taskVariables.put("approved", "true");
+ taskService.complete(task.getId(), taskVariables);
+ }
+
+ Task task = taskService.createTaskQuery().taskAssignee("henryyan").singleResult();
+ assertNotNull(task);
+ assertEquals("销假", task.getName());
+ }
+
+ /**
+ * 部分通过
+ */
+ @Test
+ @Deployment(resources = {"diagrams/chapter9/leave-countersign.bpmn"})
+ public void testNotAllApproved() throws Exception {
+ Map variables = new HashMap();
+ List users = Arrays.asList("groupLeader", "deptLeader", "hr");
+ variables.put("users", users);
+ identityService.setAuthenticatedUserId("henryyan");
+ ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave-countersign", variables);
+ for (String user : users) {
+ Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee(user).singleResult();
+ Map taskVariables = new HashMap();
+ taskVariables.put("approved", "false");
+ taskService.complete(task.getId(), taskVariables);
+ }
+
+ Task task = taskService.createTaskQuery().taskAssignee("henryyan").singleResult();
+ assertNotNull(task);
+ assertEquals("调整申请", task.getName());
+ }
+}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave-dynamic-form-with-javascript.zip b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave-dynamic-form-with-javascript.zip
new file mode 100644
index 000000000..0d5addbdd
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave-dynamic-form-with-javascript.zip differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave-dynamic-form.zip b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave-dynamic-form.zip
new file mode 100644
index 000000000..5f66312ad
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave-dynamic-form.zip differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave.bpmn
new file mode 100644
index 000000000..13a58e9a2
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave.bpmn
@@ -0,0 +1,198 @@
+
+
+
+ 请假流程演示-动态表单
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 请求被驳回后员工可以选择继续申请,或者取消本次申请
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave.png b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave.png
new file mode 100644
index 000000000..cd36f9855
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/dynamic-form/leave.png differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/approve-deptLeader.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/approve-deptLeader.form
new file mode 100644
index 000000000..b62459fe0
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/approve-deptLeader.form
@@ -0,0 +1,31 @@
+
+
申请人:
+
${applyUserId}
+
+
+
+
+
+
审批意见:
+
+
+ 同意
+ 拒绝
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/approve-hr.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/approve-hr.form
new file mode 100644
index 000000000..e177d2795
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/approve-hr.form
@@ -0,0 +1,31 @@
+
+
申请人:
+
${applyUserId}
+
+
+
+
+
+
审批意见:
+
+
+ 同意
+ 拒绝
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.bpmn
new file mode 100644
index 000000000..7f030216e
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.bpmn
@@ -0,0 +1,151 @@
+
+
+
+ 《Activiti实战》第6章的例子--外置表单
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 请求被驳回后员工可以选择继续申请,或者取消本次申请
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.png b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.png
new file mode 100644
index 000000000..5e8d231a8
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.png differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.zip b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.zip
new file mode 100644
index 000000000..f846913b7
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-formkey.zip differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-start.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-start.form
new file mode 100644
index 000000000..7b6ce7814
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/leave-start.form
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/modify-apply.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/modify-apply.form
new file mode 100644
index 000000000..72f326905
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/modify-apply.form
@@ -0,0 +1,27 @@
+
+
+
+
+
是否继续申请:
+
+
+ 重新申请
+ 结束流程
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/report-back.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/report-back.form
new file mode 100644
index 000000000..f05bf8e7e
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-formkey/report-back.form
@@ -0,0 +1,28 @@
+
+
申请人:
+
${applyUserId}
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/approve-deptLeader.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/approve-deptLeader.form
new file mode 100644
index 000000000..b62459fe0
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/approve-deptLeader.form
@@ -0,0 +1,31 @@
+
+
申请人:
+
${applyUserId}
+
+
+
+
+
+
审批意见:
+
+
+ 同意
+ 拒绝
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/approve-hr.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/approve-hr.form
new file mode 100644
index 000000000..e177d2795
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/approve-hr.form
@@ -0,0 +1,31 @@
+
+
申请人:
+
${applyUserId}
+
+
+
+
+
+
审批意见:
+
+
+ 同意
+ 拒绝
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-formkey-ext.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-formkey-ext.bpmn
new file mode 100644
index 000000000..8241fed88
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-formkey-ext.bpmn
@@ -0,0 +1,188 @@
+
+
+
+ 《Activiti实战》第6章的例子--外置表单
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 倒计时30秒
+
+
+ = 10000}]]>
+
+
+
+
+
+
+
+
+ 请求被驳回后员工可以选择继续申请,或者取消本次申请
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-formkey-ext.png b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-formkey-ext.png
new file mode 100644
index 000000000..be7e777c8
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-formkey-ext.png differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-start.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-start.form
new file mode 100644
index 000000000..7b6ce7814
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/leave-start.form
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/modify-apply.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/modify-apply.form
new file mode 100644
index 000000000..72f326905
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/modify-apply.form
@@ -0,0 +1,27 @@
+
+
+
+
+
是否继续申请:
+
+
+ 重新申请
+ 结束流程
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/report-back.form b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/report-back.form
new file mode 100644
index 000000000..f05bf8e7e
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/chapter6/leave-timeLimit-money/report-back.form
@@ -0,0 +1,28 @@
+
+
申请人:
+
${applyUserId}
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/leave-countersign.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/leave-countersign.bpmn
new file mode 100644
index 000000000..bdb56857d
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/leave-countersign.bpmn
@@ -0,0 +1,152 @@
+
+
+
+ 请假流程演示-会签
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/leave-countersign.png b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/leave-countersign.png
new file mode 100644
index 000000000..e2a6f2ca2
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/leave-countersign.png differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceFixedNumbers.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceFixedNumbers.bpmn
new file mode 100644
index 000000000..998570919
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceFixedNumbers.bpmn
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+ ${loop}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.exception.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.exception.bpmn
new file mode 100644
index 000000000..05a87d939
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.exception.bpmn
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.exception.png b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.exception.png
new file mode 100644
index 000000000..5ac829387
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.exception.png differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.nosequential.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.nosequential.bpmn
new file mode 100644
index 000000000..9790144fe
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.nosequential.bpmn
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.sequential.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.sequential.bpmn
new file mode 100644
index 000000000..c7bc82b4f
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.sequential.bpmn
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.nosequential.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.nosequential.bpmn
new file mode 100644
index 000000000..ca117190c
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.nosequential.bpmn
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.bpmn
new file mode 100644
index 000000000..5dbd39676
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.bpmn
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.bpmn b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.bpmn
new file mode 100644
index 000000000..d8fc63a55
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.bpmn
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ ${nrOfCompletedInstances / nrOfInstances >= rate}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.png b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.png
new file mode 100644
index 000000000..c20f451b8
Binary files /dev/null and b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/diagrams/chapter9/testMultiInstanceForUserTask.users.sequential.with.complete.conditon.png differ
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/flowable.cfg.xml b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/flowable.cfg.xml
new file mode 100644
index 000000000..1530bae17
--- /dev/null
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/flowable.cfg.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file