buttons) {
+ return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable,mergeable, buttons);
}
- public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable) {
- return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable, null);
+ public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable,boolean mergeable) {
+ return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable,mergeable, null);
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java
index f169b75e..09217db6 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java
@@ -48,6 +48,7 @@ private void loadNodes() {
String titleGenerator = properties.getString("titleGenerator");
String name = properties.getString("name");
boolean editable = properties.getBoolean("editable");
+ boolean mergeable = properties.getBoolean("mergeable");
String view = properties.getString("view");
String type = properties.getString("type");
String approvalType = properties.getString("approvalType");
@@ -59,7 +60,7 @@ private void loadNodes() {
buttons = properties.getJSONArray("buttons").toJavaList(FlowButton.class);
}
FlowNode flowNode = new FlowNode(id, name, code, view, NodeType.parser(type), ApprovalType.parser(approvalType), new TitleGenerator(titleGenerator),
- new OperatorMatcher(operatorMatcher), timeout, StringUtils.hasLength(errTrigger) ? new ErrTrigger(errTrigger) : null, editable, buttons);
+ new OperatorMatcher(operatorMatcher), timeout, StringUtils.hasLength(errTrigger) ? new ErrTrigger(errTrigger) : null, editable,mergeable, buttons);
flowNodes.add(flowNode);
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java
index 60f94ae5..de4c7542 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java
@@ -183,8 +183,20 @@ public MessageResult rejectFlow() {
return MessageResult.create(result);
}
+ /**
+ * 停止流程
+ */
+ public void stopFlow() {
+ if (flowRecord == null) {
+ throw new IllegalArgumentException("flow record is null");
+ }
+ FlowService flowService = loadFlowService();
+ flowService.stop(flowRecord.getId(), currentOperator);
+ }
+
/**
* 上级节点的状态是驳回状态
+ *
* @return 上级节点的状态是驳回状态
*/
public boolean backStateIsReject() {
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java
index 1e9985fb..fcad0edd 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java
@@ -78,6 +78,14 @@ public class FlowNode {
*/
private boolean editable;
+ /**
+ * 是否合并记录
+ *
+ * 如果为true,则表示该节点可以合并记录
+ */
+ private boolean mergeable;
+
+
/**
* 创建时间
*/
@@ -148,6 +156,7 @@ public FlowNodeSerializable toSerializable() {
this.approvalType,
this.operatorMatcher.getScript(),
this.editable,
+ this.mergeable,
this.createTime,
this.updateTime,
this.timeout,
@@ -168,6 +177,7 @@ public FlowNode(String id,
long timeout,
ErrTrigger errTrigger,
boolean editable,
+ boolean mergeable,
List buttons) {
this.id = id;
this.code = code;
@@ -182,6 +192,7 @@ public FlowNode(String id,
this.errTrigger = errTrigger;
this.timeout = timeout;
this.editable = editable;
+ this.mergeable = mergeable;
this.buttons = buttons;
}
@@ -229,6 +240,7 @@ public FlowRecord createRecord(long workId,
FlowRecord record = new FlowRecord();
record.setProcessId(processId);
record.setNodeCode(this.code);
+ record.setMergeable(this.mergeable);
record.setCreateTime(System.currentTimeMillis());
record.setWorkId(workId);
record.setWorkCode(workCode);
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java
index c93dead9..16f7df4f 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java
@@ -17,6 +17,9 @@
@AllArgsConstructor
public class FlowRelation {
+
+ public static final int DEFAULT_ORDER = -100;
+
/**
* 关系id
*/
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java
index b6ad791a..05a6ca3c 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java
@@ -309,8 +309,10 @@ public FlowNode getStartNode() {
/**
* 是否存在退回关系
*/
- public boolean hasBackRelation() {
- return relations.stream().anyMatch(FlowRelation::isBack);
+ public boolean hasBackRelation(String sourceCode) {
+ return relations.stream()
+ .filter(relation -> relation.getSource().getCode().equals(sourceCode))
+ .anyMatch(FlowRelation::isBack);
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java
index f9f72e78..62b73d17 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java
@@ -35,6 +35,8 @@ public class Opinion {
public static final int RESULT_CIRCULATE = 4;
// 审批结果 等待
public static final int RESULT_WAITING = 5;
+ // 审批结果 停止
+ public static final int RESULT_STOP = 6;
/**
@@ -83,6 +85,10 @@ public static Opinion pass(String advice) {
return new Opinion(advice, RESULT_PASS, TYPE_DEFAULT);
}
+ public static Opinion stop() {
+ return new Opinion("", RESULT_STOP, TYPE_DEFAULT);
+ }
+
public static Opinion reject(String advice) {
return new Opinion(advice, RESULT_REJECT, TYPE_DEFAULT);
}
@@ -114,7 +120,7 @@ public boolean isSuccess() {
public boolean isWaiting() {
return result == RESULT_WAITING;
}
-
+
public boolean isReject() {
return result == RESULT_REJECT;
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java
index 5e36e57b..930eb03b 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java
@@ -24,7 +24,11 @@ public enum FlowType {
/**
* 等待执行
*/
- WAITING;
+ WAITING,
+ /**
+ * 删除
+ */
+ DELETE;
public static FlowType parser(String type){
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java
index ba6665ad..4f21d62e 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java
@@ -34,6 +34,16 @@ public class FlowApprovalEvent implements ISyncEvent {
public static final int STATE_URGE = 8;
// 抄送
public static final int STATE_CIRCULATE = 9;
+ // 保存
+ public static final int STATE_SAVE = 10;
+ // 删除
+ public static final int STATE_DELETE = 11;
+ // 退回
+ public static final int STATE_BACK = 12;
+ // 作废
+ public static final int STATE_VOIDED = 13;
+ // 停止
+ public static final int STATE_STOP = 14;
private final int state;
@@ -52,8 +62,23 @@ public FlowApprovalEvent(int state, FlowRecord flowRecord, IFlowOperator operato
}
- public boolean match(Class> bindDataClass) {
- return bindDataClass.isInstance(bindData);
+ public boolean match(String matchKey) {
+ return bindData.match(matchKey);
+ }
+
+ /**
+ * 匹配类名
+ * 当前bingData下的clazzName变成了普通的key字段了,推荐使用match(String matchKey)方法
+ * @param clazz 类名
+ * @return 是否匹配
+ */
+ @Deprecated
+ public boolean match(Class> clazz) {
+ return bindData.match(clazz.getName());
+ }
+
+ public T toJavaObject(Class clazz) {
+ return bindData.toJavaObject(clazz);
}
public boolean isUrge() {
@@ -64,6 +89,14 @@ public boolean isTodo() {
return state == STATE_TODO;
}
+ public boolean isStop() {
+ return state == STATE_STOP;
+ }
+
+ public boolean isSave() {
+ return state == STATE_SAVE;
+ }
+
public boolean isCreate() {
return state == STATE_CREATE;
}
@@ -87,4 +120,16 @@ public boolean isRecall() {
public boolean isFinish() {
return state == STATE_FINISH;
}
+
+ public boolean isDelete() {
+ return state == STATE_DELETE;
+ }
+
+ public boolean isVoided() {
+ return state == STATE_VOIDED;
+ }
+
+ public boolean isBack() {
+ return state == STATE_BACK;
+ }
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java
index 1b73841f..5dc44e71 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java
@@ -5,6 +5,7 @@
import com.codingapi.springboot.flow.domain.FlowNode;
import com.codingapi.springboot.flow.domain.FlowWork;
import com.codingapi.springboot.flow.domain.Opinion;
+import com.codingapi.springboot.flow.record.FlowMerge;
import com.codingapi.springboot.flow.record.FlowRecord;
import com.codingapi.springboot.flow.user.IFlowOperator;
import lombok.Getter;
@@ -64,8 +65,14 @@ public class FlowDetail {
*/
private final boolean canHandle;
+ /**
+ * 合并记录
+ */
+ private final List mergeRecords;
+
public FlowDetail(FlowRecord flowRecord,
+ List mergeRecords,
BindDataSnapshot snapshot,
FlowWork flowWork,
List historyRecords,
@@ -73,6 +80,7 @@ public FlowDetail(FlowRecord flowRecord,
boolean canHandle) {
this.operators = operators;
this.flowRecord = flowRecord;
+ this.mergeRecords = mergeRecords;
this.flowWork = flowWork;
this.bindData = snapshot.toBindData();
this.historyRecords = historyRecords;
@@ -92,6 +100,7 @@ public FlowDetail(FlowWork flowWork,
this.operators = operators;
this.flowCreateTime = 0;
this.flowRecord = null;
+ this.mergeRecords = null;
this.historyRecords = null;
this.bindData = null;
this.opinions = null;
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java
index 36d49c36..8f4e94db 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java
@@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
@Getter
public class FlowStepResult {
@@ -17,32 +18,34 @@ public FlowStepResult() {
this.flowNodes = new ArrayList<>();
}
- public void addFlowNode(FlowNode flowNode,List extends IFlowOperator> operators) {
- this.flowNodes.add(new FlowStepNode(flowNode.getId(), flowNode.getCode(),flowNode.getName(),flowNode.getType(),operators));
+ public void addFlowNode(FlowNode flowNode,boolean done,List extends IFlowOperator> operators) {
+ this.flowNodes.add(new FlowStepNode(flowNode.getId(), flowNode.getCode(),flowNode.getName(),flowNode.getType(),done,operators));
}
public void print(){
+ System.out.println("FlowStepResult:==========================>");
for (FlowStepNode flowNode : flowNodes) {
- System.out.println("flowNode = " + flowNode.getName());
+ System.out.println("flowNode = " + flowNode.getName()+",done = " + flowNode.isDone() + ",type = " + flowNode.getType()+" operators = " + flowNode.getOperators().stream().map(IFlowOperator::getUserId).collect(Collectors.toList()));
}
}
-
@Getter
public static class FlowStepNode{
private final String id;
private final String code;
private final String name;
private final NodeType type;
+ private final boolean done;
private final List extends IFlowOperator> operators;
- public FlowStepNode(String id, String code, String name, NodeType type,List extends IFlowOperator> operators) {
+ public FlowStepNode(String id, String code, String name, NodeType type,boolean done,List extends IFlowOperator> operators) {
this.id = id;
this.code = code;
this.name = name;
this.type = type;
this.operators = operators;
+ this.done = done;
}
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java
new file mode 100644
index 00000000..5ef07031
--- /dev/null
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java
@@ -0,0 +1,14 @@
+package com.codingapi.springboot.flow.record;
+
+import com.codingapi.springboot.flow.bind.IBindData;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class FlowMerge {
+
+ private final FlowRecord flowRecord;
+ private final IBindData bindData;
+
+}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java
index b8a4f975..46fcc892 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java
@@ -33,10 +33,24 @@ public class FlowProcess {
private long createOperatorId;
+ /**
+ * 是否作废
+ */
+ private boolean voided;
+
+ /**
+ * 作废流程
+ */
+ public void voided(){
+ this.voided = true;
+ }
+
+
public FlowProcess(long backupId, IFlowOperator createOperator) {
this.processId = RandomGenerator.generateUUID();
this.createTime = System.currentTimeMillis();
this.backupId = backupId;
this.createOperatorId = createOperator.getUserId();
+ this.voided = false;
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java
index fbd303e8..2da107d2 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java
@@ -49,6 +49,12 @@ public class FlowRecord {
*/
private String nodeCode;
+
+ /**
+ * 是否可合并
+ */
+ private boolean mergeable;
+
/**
* 流程标题
*/
@@ -241,6 +247,17 @@ public void circulate() {
this.opinion = Opinion.circulate();
}
+
+ /**
+ * 停止流程
+ */
+ public void stop(){
+ this.flowSourceDirection = FlowSourceDirection.PASS;
+ this.flowType = FlowType.DONE;
+ this.updateTime = System.currentTimeMillis();
+ this.opinion = Opinion.stop();
+ }
+
/**
* 转交流程
*/
@@ -370,6 +387,15 @@ public void matcherOperator(IFlowOperator currentOperator) {
}
}
+ /**
+ * 是否是当前发起人
+ * @param operator 操作者
+ * @return 是否是当前发起人
+ */
+ public boolean isCreateOperator(IFlowOperator operator) {
+ return this.createOperator.getUserId() == operator.getUserId();
+ }
+
/**
* 是否是当前操作者
*
@@ -389,6 +415,13 @@ public void recall() {
this.updateTime = System.currentTimeMillis();
}
+ /**
+ * 删除流程
+ */
+ public void delete() {
+ this.flowType = FlowType.DELETE;
+ this.updateTime = System.currentTimeMillis();
+ }
/**
* 复制流程记录
@@ -402,6 +435,7 @@ public FlowRecord copy() {
record.setWorkCode(this.workCode);
record.setProcessId(this.processId);
record.setNodeCode(this.nodeCode);
+ record.setMergeable(this.mergeable);
record.setTitle(this.title);
record.setCurrentOperator(this.currentOperator);
record.setFlowType(this.flowType);
@@ -423,6 +457,10 @@ public FlowRecord copy() {
return record;
}
+ public boolean isDelete() {
+ return this.flowType == FlowType.DELETE;
+ }
+
/**
* 是否超时
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java
index acf8cfe1..a0d23ef9 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java
@@ -10,9 +10,9 @@ public interface FlowProcessRepository {
void save(FlowProcess flowProcess);
-
FlowWork getFlowWorkByProcessId(String processId);
+ FlowProcess getFlowProcessByProcessId(String processId);
void deleteByProcessId(String processId);
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java
index 292d40e5..8987ec6c 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java
@@ -48,6 +48,15 @@ public interface FlowRecordRepository {
*/
List findFlowRecordByProcessId(String processId);
+ /**
+ * 获取合并的流程记录
+ * @param workCode 流程编码
+ * @param nodeCode 节点编码
+ * @param currentOperatorId 当前操作者ID
+ * @return List of FlowRecord
+ */
+ List findMergeFlowRecordById(String workCode,String nodeCode,long currentOperatorId);
+
/**
* 查询所有未完成的流程记录
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java
index d6e80dea..b517104c 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java
@@ -69,6 +69,11 @@ public class FlowNodeSerializable implements Serializable {
*/
private boolean editable;
+ /**
+ * 是否可合并审批
+ */
+ private boolean mergeable;
+
/**
* 创建时间
*/
@@ -95,6 +100,6 @@ public class FlowNodeSerializable implements Serializable {
public FlowNode toFlowNode() {
return new FlowNode(id, code, name, new TitleGenerator(titleGenerator), type, view, approvalType,
- new OperatorMatcher(operatorMatcher), editable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger),buttons);
+ new OperatorMatcher(operatorMatcher), editable, mergeable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger), buttons);
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java
index 367efa68..5ed0216c 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java
@@ -48,6 +48,7 @@ public void loadFlowSourceDirection() {
}
}
+
/**
* 重新加载审批方向
* 根据会签结果判断是否需要重新设置审批方向
@@ -84,26 +85,6 @@ public boolean hasCurrentFlowNodeIsDone() {
return historyRecords.stream().filter(item -> !item.isTransfer()).allMatch(FlowRecord::isDone);
}
-
- /**
- * 检测当前流程是否已经完成
- * 即流程已经进行到了最终节点且审批意见为同意
- */
- public boolean hasCurrentFlowIsFinish() {
- if (flowSourceDirection == FlowSourceDirection.PASS && flowNode.isOverNode()) {
- return true;
- }
- return false;
- }
-
-
- /**
- * 判断当前流程是否为默认的驳回流程
- */
- public boolean isDefaultBackRecord() {
- return flowSourceDirection == FlowSourceDirection.REJECT && !flowWork.hasBackRelation();
- }
-
/**
* 判断当前流程是否为通过流程
*/
@@ -111,11 +92,5 @@ public boolean isPassRecord() {
return flowSourceDirection == FlowSourceDirection.PASS;
}
- /**
- * 判断当前流程是否为自定义的驳回流程
- */
- public boolean isCustomBackRecord() {
- return flowSourceDirection == FlowSourceDirection.REJECT && flowWork.hasBackRelation();
- }
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java
index c0acc00a..facb0e47 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java
@@ -13,6 +13,7 @@
import com.codingapi.springboot.flow.record.FlowRecord;
import com.codingapi.springboot.flow.repository.FlowOperatorRepository;
import com.codingapi.springboot.flow.repository.FlowRecordRepository;
+import com.codingapi.springboot.flow.trigger.OutTrigger;
import com.codingapi.springboot.flow.user.IFlowOperator;
import com.codingapi.springboot.framework.event.EventPusher;
import lombok.Getter;
@@ -101,7 +102,29 @@ public void skipCirculate() {
/**
* 加载默认回退节点
*/
- public void loadDefaultBackNode(FlowRecord currentRecord) {
+ public void loadDefaultBackNode(FlowNode flowNode,FlowRecord currentRecord) {
+ // 如果退回设置了回退关系,则需要根据关系匹配下一个节点
+ if (flowWork.hasBackRelation(flowNode.getCode())) {
+ FlowNode nextNode = this.matcherNextNode(flowNode, true);
+ if (nextNode == null) {
+ throw new IllegalArgumentException("next node not found");
+ }
+ long parentRecordId = currentRecord.getPreId();
+ FlowRecord preFlowRecord = flowRecordRepository.getFlowRecordById(parentRecordId);
+ IFlowOperator flowOperator = currentOperator;
+ if (nextNode.isAnyOperatorMatcher()) {
+ while (preFlowRecord.isTransfer() || !preFlowRecord.getNodeCode().equals(nextNode.getCode())) {
+ preFlowRecord = flowRecordRepository.getFlowRecordById(preFlowRecord.getPreId());
+ }
+ flowOperator = preFlowRecord.getCurrentOperator();
+ }
+ this.nextNode = nextNode;
+ this.nextOperator = flowOperator;
+ this.backOperator = null;
+ return;
+ }
+
+ // 如果没有设置回退关系,则需要根据流程记录来匹配下一个节点
List historyRecords =
flowRecordRepository.findFlowRecordByProcessId(currentRecord.getProcessId())
.stream()
@@ -115,7 +138,7 @@ public void loadDefaultBackNode(FlowRecord currentRecord) {
throw new IllegalArgumentException("back node not found");
}
FlowRecord record = historyRecords.get(index);
- if (record.isDone()) {
+ if (record.isDone() && record.getId()== currentRecord.getPreId()) {
// 是连续的回退节点时,则根据流程记录的状态来判断
if(record.isReject()){
boolean startRemove = false;
@@ -145,28 +168,6 @@ public void loadDefaultBackNode(FlowRecord currentRecord) {
}
- /**
- * 加载自定义回退节点
- */
- public void loadCustomBackNode(FlowNode flowNode, long parentRecordId) {
- FlowNode nextNode = this.matcherNextNode(flowNode, true);
- if (nextNode == null) {
- throw new IllegalArgumentException("next node not found");
- }
- IFlowOperator flowOperator = currentOperator;
- if (nextNode.isAnyOperatorMatcher()) {
- // 如果是任意人员操作时则需要指定为当时审批人员为当前审批人员
- FlowRecord preFlowRecord = flowRecordRepository.getFlowRecordById(parentRecordId);
- while (preFlowRecord.isTransfer() || !preFlowRecord.getNodeCode().equals(nextNode.getCode())) {
- preFlowRecord = flowRecordRepository.getFlowRecordById(preFlowRecord.getPreId());
- }
- flowOperator = preFlowRecord.getCurrentOperator();
- }
- this.nextNode = nextNode;
- this.nextOperator = flowOperator;
- this.backOperator = flowOperator;
- }
-
/**
* 获取下一个节点
@@ -174,7 +175,28 @@ public void loadCustomBackNode(FlowNode flowNode, long parentRecordId) {
* @return 下一个节点
*/
private FlowNode matcherNextNode(FlowNode flowNode, boolean back) {
- List relations = flowWork.getRelations().stream()
+ List currentRelations = new ArrayList<>(flowWork.getRelations());
+ if(back){
+ String preCode = FlowNode.CODE_START;
+ if(flowRecord.getPreId()!=0){
+ FlowRecord preRecord = flowRecordRepository.getFlowRecordById(flowRecord.getPreId());
+ if(preRecord!=null){
+ preCode = preRecord.getNodeCode();
+ while (preCode.equals(flowRecord.getNodeCode())){
+ preRecord = flowRecordRepository.getFlowRecordById(preRecord.getPreId());
+ if(preRecord==null){
+ break;
+ }
+ preCode = preRecord.getNodeCode();
+ }
+ }
+ }
+ FlowRelation backRelation = new FlowRelation("defaultId",
+ "默认回退关系", flowNode, flowWork.getNodeByCode(preCode), OutTrigger.defaultOutTrigger(), FlowRelation.DEFAULT_ORDER, true);
+ currentRelations.add(backRelation);
+ }
+
+ List relations = currentRelations.stream()
.filter(relation -> relation.sourceMatcher(flowNode.getCode()))
.filter(relation -> relation.isBack() == back)
.sorted((o1, o2) -> (o2.getOrder() - o1.getOrder()))
@@ -315,7 +337,7 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO
List operatorIds = ((OperatorResult) errorResult).getOperatorIds();
List extends IFlowOperator> operators = flowOperatorRepository.findByIds(operatorIds);
for (IFlowOperator operator : operators) {
- FlowSession content = new FlowSession(flowRecord, flowWork, currentNode, createOperator, operator, snapshot.toBindData(), opinion, historyRecords);
+ FlowSession content = new FlowSession(flowRecord, flowWork, currentNode, createOperator, nextOperator, snapshot.toBindData(), opinion, historyRecords);
String recordTitle = currentNode.generateTitle(content);
FlowRecord record = currentNode.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot, opinion.isWaiting());
recordList.add(record);
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java
index 003db90c..01ecf875 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java
@@ -83,6 +83,14 @@ public void verifyFlowRecordCurrentOperator() {
}
}
+ /**
+ * 当前人是否为发起人
+ * @return 是否为发起人
+ */
+ public boolean isCreateOperator() {
+ return flowRecord.isCreateOperator(currentOperator);
+ }
+
/**
* 校验流程是否已审批
*/
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java
index 0aaa054c..2fd1c117 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java
@@ -28,6 +28,8 @@ public class FlowService {
private final FlowTransferService flowTransferService;
private final FlowPostponedService flowPostponedService;
private final FlowUrgeService flowUrgeService;
+ private final FlowVoidedService flowVoidedService;
+ private final FlowBackService flowBackService;
private final FlowServiceRepositoryHolder flowServiceRepositoryHolder;
@@ -40,13 +42,15 @@ public FlowService(FlowWorkRepository flowWorkRepository,
FlowBackupRepository flowBackupRepository) {
this.flowServiceRepositoryHolder = new FlowServiceRepositoryHolder(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository, flowBackupRepository);
this.flowDetailService = new FlowDetailService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository);
- this.flowCustomEventService = new FlowCustomEventService(flowWorkRepository,flowRecordRepository, flowProcessRepository);
- this.flowRecallService = new FlowRecallService(flowWorkRepository,flowRecordRepository, flowProcessRepository);
- this.flowRemoveService = new FlowRemoveService(flowWorkRepository,flowRecordRepository, flowProcessRepository);
- this.flowSaveService = new FlowSaveService(flowWorkRepository,flowRecordRepository, flowBindDataRepository, flowProcessRepository);
- this.flowTransferService = new FlowTransferService(flowWorkRepository,flowRecordRepository, flowBindDataRepository, flowProcessRepository);
- this.flowPostponedService = new FlowPostponedService(flowWorkRepository,flowRecordRepository, flowProcessRepository);
- this.flowUrgeService = new FlowUrgeService(flowWorkRepository,flowRecordRepository, flowProcessRepository);
+ this.flowCustomEventService = new FlowCustomEventService(flowWorkRepository, flowRecordRepository, flowProcessRepository);
+ this.flowRecallService = new FlowRecallService(flowWorkRepository, flowRecordRepository, flowProcessRepository,flowBindDataRepository);
+ this.flowRemoveService = new FlowRemoveService(flowWorkRepository, flowRecordRepository, flowProcessRepository,flowBindDataRepository);
+ this.flowSaveService = new FlowSaveService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowProcessRepository);
+ this.flowTransferService = new FlowTransferService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowProcessRepository);
+ this.flowPostponedService = new FlowPostponedService(flowWorkRepository, flowRecordRepository, flowProcessRepository);
+ this.flowUrgeService = new FlowUrgeService(flowWorkRepository, flowRecordRepository, flowProcessRepository);
+ this.flowVoidedService = new FlowVoidedService(flowWorkRepository, flowRecordRepository, flowProcessRepository, flowBindDataRepository);
+ this.flowBackService = new FlowBackService(flowWorkRepository, flowRecordRepository, flowProcessRepository, flowBindDataRepository);
}
/**
@@ -57,11 +61,13 @@ public FlowService(FlowWorkRepository flowWorkRepository,
* @return 流程详情
*/
public FlowDetail detail(long recordId, String workCode, IFlowOperator currentOperator) {
+ if (recordId > 0) {
+ return flowDetailService.detail(recordId, currentOperator);
+ }
if (StringUtils.hasText(workCode)) {
return flowDetailService.detail(workCode, currentOperator);
- } else {
- return flowDetailService.detail(recordId, currentOperator);
}
+ return null;
}
/**
@@ -189,6 +195,18 @@ public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperat
}
+ /**
+ * 获取流程执行节点
+ *
+ * @param recordId
+ * @param currentOperator
+ * @return
+ */
+ public FlowStepResult getFlowStep(long recordId, IBindData bindData, IFlowOperator currentOperator) {
+ FlowStepService flowStepService = new FlowStepService(recordId, null, currentOperator, bindData, flowServiceRepositoryHolder);
+ return flowStepService.getFlowStep();
+ }
+
/**
* 获取流程执行节点
*
@@ -197,7 +215,7 @@ public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperat
* @return
*/
public FlowStepResult getFlowStep(String workCode, IBindData bindData, IFlowOperator currentOperator) {
- FlowStepService flowStepService = new FlowStepService(workCode, currentOperator, bindData, flowServiceRepositoryHolder);
+ FlowStepService flowStepService = new FlowStepService(0, workCode, currentOperator, bindData, flowServiceRepositoryHolder);
return flowStepService.getFlowStep();
}
@@ -230,10 +248,11 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind
/**
* 唤醒流程
- * @param processId 流程实例id
+ *
+ * @param processId 流程实例id
* @param currentOperator 当前操作者
*/
- public void notifyFlow(String processId,IFlowOperator currentOperator) {
+ public void notifyFlow(String processId, IFlowOperator currentOperator) {
FlowNotifyService flowNotifyService = new FlowNotifyService(processId, currentOperator, flowServiceRepositoryHolder);
flowNotifyService.notifyFlow();
}
@@ -263,7 +282,6 @@ public void recall(long recordId, IFlowOperator currentOperator) {
}
-
/**
* 删除流程
*
@@ -273,4 +291,38 @@ public void recall(long recordId, IFlowOperator currentOperator) {
public void remove(long recordId, IFlowOperator currentOperator) {
flowRemoveService.remove(recordId, currentOperator);
}
+
+
+ /**
+ * 作废流程
+ *
+ * @param processId 流程processId
+ * @param currentOperator 当前操作者
+ */
+ public void voided(String processId, IFlowOperator currentOperator) {
+ flowVoidedService.voided(processId, currentOperator);
+ }
+
+
+ /**
+ * 退回流程
+ *
+ * @param processId 流程processId
+ * @param backNodeCode 退回节点编码
+ * @param currentOperator 当前操作者
+ */
+ public void back(String processId, String backNodeCode, IFlowOperator currentOperator) {
+ flowBackService.back(processId, backNodeCode, currentOperator);
+ }
+
+ /**
+ * 停止流程
+ *
+ * @param recordId 流程记录id
+ * @param currentOperator 当前操作者
+ */
+ public void stop(long recordId, IFlowOperator currentOperator) {
+ FlowStopService flowSubmitService = new FlowStopService(recordId, currentOperator, flowServiceRepositoryHolder);
+ flowSubmitService.stop();
+ }
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowBackService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowBackService.java
new file mode 100644
index 00000000..0e4514da
--- /dev/null
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowBackService.java
@@ -0,0 +1,101 @@
+package com.codingapi.springboot.flow.service.impl;
+
+import com.codingapi.springboot.flow.bind.IBindData;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.event.FlowApprovalEvent;
+import com.codingapi.springboot.flow.record.FlowProcess;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.FlowBindDataRepository;
+import com.codingapi.springboot.flow.repository.FlowProcessRepository;
+import com.codingapi.springboot.flow.repository.FlowRecordRepository;
+import com.codingapi.springboot.flow.repository.FlowWorkRepository;
+import com.codingapi.springboot.flow.user.IFlowOperator;
+import com.codingapi.springboot.framework.event.EventPusher;
+import lombok.AllArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 流程退回服务(流程管理员操作)
+ */
+@Transactional
+@AllArgsConstructor
+public class FlowBackService {
+
+ private final FlowWorkRepository flowWorkRepository;
+ private final FlowRecordRepository flowRecordRepository;
+ private final FlowProcessRepository flowProcessRepository;
+ private final FlowBindDataRepository flowBindDataRepository;
+
+
+ /**
+ * 退回流程
+ *
+ * @param processId 流程processId
+ * @param backNodeCode 退回节点编码
+ * @param currentOperator 当前操作者
+ */
+ public void back(String processId, String backNodeCode, IFlowOperator currentOperator) {
+ if (!currentOperator.isFlowManager()) {
+ throw new IllegalArgumentException("current operator not flow manager");
+ }
+
+ FlowProcess flowProcess = flowProcessRepository.getFlowProcessByProcessId(processId);
+ if (flowProcess.isVoided()) {
+ throw new IllegalArgumentException("flow process already voided");
+ }
+
+ List historyRecords = flowRecordRepository
+ .findFlowRecordByProcessId(processId)
+ .stream()
+ .sorted(Comparator.comparingLong(FlowRecord::getId))
+ .collect(Collectors.toList());
+
+ for (FlowRecord flowRecord : historyRecords) {
+ if (flowRecord.isFinish()) {
+ throw new IllegalArgumentException("flow record already finish");
+ }
+ }
+
+ if (historyRecords.isEmpty()) {
+ throw new IllegalArgumentException("flow record not found");
+ }
+
+ List historyNodeCodes = historyRecords.stream()
+ .map(FlowRecord::getNodeCode)
+ .distinct()
+ .collect(Collectors.toList());
+
+ if (!historyNodeCodes.contains(backNodeCode)) {
+ throw new IllegalArgumentException("flow node code not found");
+ }
+
+ FlowRecord beginRecord = historyRecords.get(0);
+ for (FlowRecord flowRecord : historyRecords) {
+ if(flowRecord.getNodeCode().equals(backNodeCode)){
+ beginRecord = flowRecord;
+ }
+ }
+
+ for (FlowRecord flowRecord : historyRecords) {
+ if(flowRecord.getId()> beginRecord.getId() ){
+ flowRecord.delete();
+ }else {
+ if(flowRecord.getNodeCode().equals(beginRecord.getNodeCode())){
+ flowRecord.recall();
+ }
+ }
+ }
+
+ flowRecordRepository.save(historyRecords);
+
+ FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(beginRecord.getWorkCode());
+ IBindData bindData = flowBindDataRepository.getBindDataSnapshotById(beginRecord.getSnapshotId()).toBindData();
+
+ EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_BACK, beginRecord, currentOperator, flowWork, bindData), true);
+
+ }
+}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java
index e684f422..718784d8 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java
@@ -5,6 +5,7 @@
import com.codingapi.springboot.flow.domain.FlowNode;
import com.codingapi.springboot.flow.domain.FlowWork;
import com.codingapi.springboot.flow.pojo.FlowDetail;
+import com.codingapi.springboot.flow.record.FlowMerge;
import com.codingapi.springboot.flow.record.FlowRecord;
import com.codingapi.springboot.flow.repository.*;
import com.codingapi.springboot.flow.service.FlowRecordVerifyService;
@@ -46,6 +47,16 @@ public FlowDetail detail(long recordId, IFlowOperator currentOperator) {
FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord();
FlowWork flowWork = flowRecordVerifyService.getFlowWork();
+ List mergeRecords = null;
+ if(flowRecord.isTodo() && flowRecord.isMergeable()){
+ List flowRecords = flowRecordRepository.findMergeFlowRecordById(flowRecord.getWorkCode(),flowRecord.getNodeCode(),currentOperator.getUserId());
+ if(!flowRecords.isEmpty()){
+ mergeRecords = flowRecords.stream().map(record->{
+ BindDataSnapshot bindDataSnapshot = flowBindDataRepository.getBindDataSnapshotById(record.getSnapshotId());
+ return new FlowMerge(record,bindDataSnapshot.toBindData());
+ }).collect(Collectors.toList());
+ }
+ }
BindDataSnapshot snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId());
List flowRecords =
@@ -64,7 +75,7 @@ public FlowDetail detail(long recordId, IFlowOperator currentOperator) {
}
}
- return new FlowDetail(flowRecord, snapshot, flowWork, flowRecords, operators, currentOperator != null && flowRecord.isTodo() && flowRecord.isOperator(currentOperator));
+ return new FlowDetail(flowRecord,mergeRecords, snapshot, flowWork, flowRecords, operators, currentOperator != null && flowRecord.isTodo() && flowRecord.isOperator(currentOperator));
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java
index 124f48b8..ca52652c 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java
@@ -1,8 +1,11 @@
package com.codingapi.springboot.flow.service.impl;
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.bind.IBindData;
import com.codingapi.springboot.flow.domain.FlowWork;
import com.codingapi.springboot.flow.event.FlowApprovalEvent;
import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.FlowBindDataRepository;
import com.codingapi.springboot.flow.repository.FlowProcessRepository;
import com.codingapi.springboot.flow.repository.FlowRecordRepository;
import com.codingapi.springboot.flow.repository.FlowWorkRepository;
@@ -12,7 +15,7 @@
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
@Transactional
@@ -22,6 +25,7 @@ public class FlowRecallService {
private final FlowWorkRepository flowWorkRepository;
private final FlowRecordRepository flowRecordRepository;
private final FlowProcessRepository flowProcessRepository;
+ private final FlowBindDataRepository flowBindDataRepository;
/**
* 撤回流程
@@ -34,7 +38,8 @@ public void recall(long recordId, IFlowOperator currentOperator) {
flowWorkRepository,
flowRecordRepository,
flowProcessRepository,
- recordId, currentOperator);
+ recordId,
+ currentOperator);
flowRecordVerifyService.verifyFlowRecordCurrentOperator();
flowRecordVerifyService.loadFlowWork();
@@ -44,18 +49,24 @@ public void recall(long recordId, IFlowOperator currentOperator) {
FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord();
FlowWork flowWork = flowRecordVerifyService.getFlowWork();
+ BindDataSnapshot bindDataSnapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId());
- // 下一流程的流程记录
- List childrenRecords = flowRecordRepository.findFlowRecordByPreId(recordId);
- // 下一流程均为办理且未读
-
- // 如果是在开始节点撤销,则直接删除
- if (flowRecord.isStartRecord() && flowRecord.isTodo()) {
- if (!childrenRecords.isEmpty()) {
- throw new IllegalArgumentException("flow record not recall");
+ if(flowRecordVerifyService.isCreateOperator()){
+ List records = flowRecordRepository.findFlowRecordByProcessId(flowRecord.getProcessId());
+ for(FlowRecord record:records){
+ if(!record.isStartRecord()) {
+ record.delete();
+ flowRecordRepository.update(record);
+ }else {
+ record.recall();
+ flowRecord = record;
+ flowRecordRepository.update(record);
+ }
}
- flowRecordRepository.delete(Collections.singletonList(flowRecord));
- } else {
+ }else {
+ // 下一流程的流程记录
+ List childrenRecords = flowRecordRepository.findFlowRecordByPreId(recordId);
+
// 如果是在中间节点撤销,则需要判断是否所有的子流程都是未读状态
if (childrenRecords.isEmpty()) {
throw new IllegalArgumentException("flow record not submit");
@@ -68,9 +79,13 @@ public void recall(long recordId, IFlowOperator currentOperator) {
flowRecord.recall();
flowRecordRepository.update(flowRecord);
- flowRecordRepository.delete(childrenRecords);
+ for (FlowRecord childrenRecord : childrenRecords) {
+ childrenRecord.delete();
+ }
+ flowRecordRepository.save(childrenRecords);
}
- EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, null), true);
+ IBindData bindData = bindDataSnapshot.toBindData();
+ EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, bindData), true);
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java
index 82c42198..92a6a776 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java
@@ -1,9 +1,13 @@
package com.codingapi.springboot.flow.service.impl;
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.bind.IBindData;
import com.codingapi.springboot.flow.domain.FlowNode;
import com.codingapi.springboot.flow.domain.FlowWork;
import com.codingapi.springboot.flow.event.FlowApprovalEvent;
+import com.codingapi.springboot.flow.record.FlowProcess;
import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.FlowBindDataRepository;
import com.codingapi.springboot.flow.repository.FlowProcessRepository;
import com.codingapi.springboot.flow.repository.FlowRecordRepository;
import com.codingapi.springboot.flow.repository.FlowWorkRepository;
@@ -13,7 +17,6 @@
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
-import java.util.Collections;
import java.util.List;
@Transactional
@@ -23,7 +26,7 @@ public class FlowRemoveService {
private final FlowWorkRepository flowWorkRepository;
private final FlowRecordRepository flowRecordRepository;
private final FlowProcessRepository flowProcessRepository;
-
+ private final FlowBindDataRepository flowBindDataRepository;
/**
* 删除流程
@@ -44,15 +47,26 @@ public void remove(long recordId, IFlowOperator currentOperator) {
flowRecordVerifyService.loadFlowNode();
flowRecordVerifyService.verifyFlowRecordNotFinish();
flowRecordVerifyService.verifyFlowRecordIsTodo();
+ FlowWork flowWork = flowRecordVerifyService.getFlowWork();
FlowNode flowNode = flowRecordVerifyService.getFlowNode();
FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord();
if(!flowNode.isStartNode()){
throw new IllegalArgumentException("flow record not remove");
}
+ BindDataSnapshot bindDataSnapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId());
+ IBindData bindData = bindDataSnapshot.toBindData();
+
+ FlowProcess flowProcess = flowProcessRepository.getFlowProcessByProcessId(flowRecord.getProcessId());
+ flowProcess.voided();
+ flowProcessRepository.save(flowProcess);
- flowProcessRepository.deleteByProcessId(flowRecord.getProcessId());
+ List historyRecords = flowRecordRepository.findFlowRecordByProcessId(flowRecord.getProcessId());
+ for (FlowRecord record : historyRecords) {
+ record.delete();
+ }
+ flowRecordRepository.save(historyRecords);
- flowRecordRepository.deleteByProcessId(flowRecord.getProcessId());
+ EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_DELETE, flowRecord, currentOperator, flowWork, bindData), true);
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java
index 6e57d924..f73c6550 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java
@@ -2,7 +2,9 @@
import com.codingapi.springboot.flow.bind.BindDataSnapshot;
import com.codingapi.springboot.flow.bind.IBindData;
+import com.codingapi.springboot.flow.domain.FlowWork;
import com.codingapi.springboot.flow.domain.Opinion;
+import com.codingapi.springboot.flow.event.FlowApprovalEvent;
import com.codingapi.springboot.flow.record.FlowRecord;
import com.codingapi.springboot.flow.repository.FlowBindDataRepository;
import com.codingapi.springboot.flow.repository.FlowProcessRepository;
@@ -10,6 +12,7 @@
import com.codingapi.springboot.flow.repository.FlowWorkRepository;
import com.codingapi.springboot.flow.service.FlowRecordVerifyService;
import com.codingapi.springboot.flow.user.IFlowOperator;
+import com.codingapi.springboot.framework.event.EventPusher;
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
@@ -48,6 +51,15 @@ public void save(long recordId, IFlowOperator currentOperator, IBindData bindDat
flowRecord.setOpinion(opinion);
flowRecordRepository.update(flowRecord);
+
+ FlowWork flowWork = flowRecordVerifyService.getFlowWork();
+
+ EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_SAVE,
+ flowRecord,
+ flowRecord.getCurrentOperator(),
+ flowWork,
+ snapshot.toBindData()),
+ true);
}
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java
index da90bf88..c8b8c127 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java
@@ -66,7 +66,7 @@ private void loadFlowWork() {
flowWork.enableValidate();
}
- private void loadFlowBackup() {
+ private synchronized void loadFlowBackup() {
FlowBackupRepository flowBackupRepository = flowServiceRepositoryHolder.getFlowBackupRepository();
this.flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime());
if (flowBackup == null) {
@@ -189,6 +189,7 @@ public FlowResult startFlow() {
for (FlowRecord record : records) {
this.pushEvent(FlowApprovalEvent.STATE_CREATE, record);
this.pushEvent(FlowApprovalEvent.STATE_TODO, record);
+ this.pushEvent(FlowApprovalEvent.STATE_SAVE, record);
}
// 当前的审批记录
return new FlowResult(flowWork, records);
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java
index c03833dd..6be110aa 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java
@@ -12,12 +12,17 @@
import com.codingapi.springboot.flow.service.FlowNodeService;
import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder;
import com.codingapi.springboot.flow.user.IFlowOperator;
+import com.codingapi.springboot.framework.utils.RandomGenerator;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
+import java.util.stream.Collectors;
public class FlowStepService {
private final FlowWork flowWork;
+ private final long recordId;
+ private final List flowRecords;
private final IFlowOperator currentOperator;
private final IBindData bindData;
@@ -25,35 +30,59 @@ public class FlowStepService {
private FlowNodeService flowNodeService;
private FlowNode flowNode;
+ private FlowRecord currentFlowRecord;
- public FlowStepService(String workCode, IFlowOperator currentOperator, IBindData bindData, FlowServiceRepositoryHolder flowServiceRepositoryHolder) {
+ public FlowStepService(long recordId,String workCode, IFlowOperator currentOperator, IBindData bindData, FlowServiceRepositoryHolder flowServiceRepositoryHolder) {
+ this.recordId = recordId;
this.currentOperator = currentOperator;
this.bindData = bindData;
this.flowServiceRepositoryHolder = flowServiceRepositoryHolder;
- this.flowWork = flowServiceRepositoryHolder.getFlowWorkRepository().getFlowWorkByCode(workCode);
+ if(this.recordId>0) {
+ this.currentFlowRecord = flowServiceRepositoryHolder.getFlowRecordRepository().getFlowRecordById(recordId);
+ this.flowRecords = flowServiceRepositoryHolder.getFlowRecordRepository().findFlowRecordByProcessId(currentFlowRecord.getProcessId()).stream().sorted(Comparator.comparingLong(FlowRecord::getId)).collect(Collectors.toList());
+ this.flowWork = flowServiceRepositoryHolder.getFlowWorkRepository().getFlowWorkByCode(currentFlowRecord.getWorkCode());
+ }else {
+ this.currentFlowRecord = null;
+ this.flowRecords = new ArrayList<>();
+ this.flowWork = flowServiceRepositoryHolder.getFlowWorkRepository().getFlowWorkByCode(workCode);
+ }
}
public FlowStepResult getFlowStep() {
FlowStepResult flowStepResult = new FlowStepResult();
- // 获取开始节点
- FlowNode start = flowWork.getStartNode();
- if (start == null) {
- throw new IllegalArgumentException("start node not found");
- }
- this.flowNode = start;
// 设置开始流程的上一个流程id
long preId = 0;
+ if(currentFlowRecord==null) {
+ // 获取开始节点
+ FlowNode start = flowWork.getStartNode();
+ if (start == null) {
+ throw new IllegalArgumentException("start node not found");
+ }
+ preId = 0;
+ this.flowNode = start;
+ }else {
+ for(FlowRecord flowRecord : flowRecords) {
+ FlowNode flowNode = this.flowWork.getNodeByCode(flowRecord.getNodeCode());
+ List operators = new ArrayList<>();
+ if(flowRecord.getCurrentOperator()!=null) {
+ operators.add(flowRecord.getCurrentOperator());
+ }
+ boolean isDone =flowRecord.isDone() || flowRecord.getOpinion().isCirculate();
+ flowStepResult.addFlowNode(flowNode,isDone, operators);
+ }
+ FlowRecord lastRecord = this.flowRecords.get(this.flowRecords.size()-1);
+ this.flowNode = this.flowWork.getNodeByCode(lastRecord.getNodeCode());
+ preId = lastRecord.getId();
+ }
// 创建流程id
- String processId = "flow_" + System.currentTimeMillis();
-
- List historyRecords = new ArrayList<>();
+ String processId = "flow_" + RandomGenerator.generateUUID();
FlowOperatorRepository flowOperatorRepository = flowServiceRepositoryHolder.getFlowOperatorRepository();
FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository();
-
+ List historyRecords = new ArrayList<>();
BindDataSnapshot snapshot = new BindDataSnapshot(bindData);
flowNodeService = new FlowNodeService(flowOperatorRepository,
flowRecordRepository,
@@ -67,15 +96,19 @@ public FlowStepResult getFlowStep() {
processId,
preId);
- flowNodeService.setNextNode(start);
+ flowNodeService.setNextNode(this.flowNode);
- this.flowNode = start;
- flowStepResult.addFlowNode(this.flowNode, this.flowNodeService.loadNextNodeOperators());
+ if(currentFlowRecord==null) {
+ flowStepResult.addFlowNode(this.flowNode, false, this.flowNodeService.loadNextNodeOperators());
+ }
do {
flowNodeService.loadNextPassNode(this.flowNode);
this.flowNode = flowNodeService.getNextNode();
- flowStepResult.addFlowNode(this.flowNode, this.flowNodeService.loadNextNodeOperators());
+
+ boolean isFinish = currentFlowRecord != null && currentFlowRecord.isFinish();
+
+ flowStepResult.addFlowNode(this.flowNode,isFinish, this.flowNodeService.loadNextNodeOperators());
} while (!flowNode.isOverNode());
return flowStepResult;
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStopService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStopService.java
new file mode 100644
index 00000000..1c8a4bb8
--- /dev/null
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStopService.java
@@ -0,0 +1,102 @@
+package com.codingapi.springboot.flow.service.impl;
+
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.domain.FlowNode;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.event.FlowApprovalEvent;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.FlowBindDataRepository;
+import com.codingapi.springboot.flow.repository.FlowRecordRepository;
+import com.codingapi.springboot.flow.service.FlowRecordVerifyService;
+import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder;
+import com.codingapi.springboot.flow.user.IFlowOperator;
+import com.codingapi.springboot.framework.event.EventPusher;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Transactional
+public class FlowStopService {
+
+
+ private final IFlowOperator currentOperator;
+ private final FlowRecordVerifyService flowRecordVerifyService;
+ private final FlowRecordRepository flowRecordRepository;
+ private final FlowBindDataRepository flowBindDataRepository ;
+
+ private FlowRecord flowRecord;
+ private FlowWork flowWork;
+ private FlowNode flowNode;
+ private BindDataSnapshot snapshot;
+
+
+
+ public FlowStopService(long recordId,
+ IFlowOperator currentOperator,
+ FlowServiceRepositoryHolder flowServiceRepositoryHolder) {
+ this.currentOperator = currentOperator;
+ this.flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository();
+ this.flowBindDataRepository = flowServiceRepositoryHolder.getFlowBindDataRepository();
+ this.flowRecordVerifyService = new FlowRecordVerifyService(
+ flowServiceRepositoryHolder.getFlowWorkRepository(),
+ flowServiceRepositoryHolder.getFlowRecordRepository(),
+ flowServiceRepositoryHolder.getFlowProcessRepository(),
+ recordId,
+ currentOperator);
+ }
+
+
+ // 加载流程
+ private void loadFlow() {
+ // 验证流程的提交状态
+ flowRecordVerifyService.verifyFlowRecordSubmitState();
+ // 验证当前操作者
+ flowRecordVerifyService.verifyFlowRecordCurrentOperator();
+
+ // 加载流程设计
+ flowRecordVerifyService.loadFlowWork();
+ // 加载流程节点
+ flowRecordVerifyService.loadFlowNode();
+ // 验证没有子流程
+ flowRecordVerifyService.verifyChildrenRecordsIsEmpty();
+
+ this.flowRecord = flowRecordVerifyService.getFlowRecord();
+ this.flowNode = flowRecordVerifyService.getFlowNode();
+ this.flowWork = flowRecordVerifyService.getFlowWork();
+ this.snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId());
+ }
+
+
+
+ /**
+ * 提交流程
+ **/
+ public void stop() {
+ // 加载流程信息
+ this.loadFlow();
+
+ // 停止流程
+ flowRecord.stop();
+ flowRecordRepository.update(flowRecord);
+
+ List todoRecords = flowRecordRepository.findFlowRecordByProcessId(flowRecord.getProcessId());
+ for (FlowRecord record : todoRecords) {
+ if (record.isTodo()) {
+ record.stop();
+ flowRecordRepository.update(record);
+ }
+ }
+
+ flowRecordRepository.finishFlowRecordByProcessId(flowRecord.getProcessId());
+
+ EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_STOP,
+ flowRecord,
+ flowRecord.getCurrentOperator(),
+ flowWork,
+ snapshot.toBindData()
+ ), true);
+
+ }
+
+
+}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java
index fd188fcd..9b2eecc3 100644
--- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java
@@ -192,12 +192,8 @@ private void loadNextNode(List historyRecords) {
// 审批通过并进入下一节点
if (flowDirectionService.isPassRecord()) {
flowNodeService.loadNextPassNode(flowNode);
- // 审批拒绝返回上一节点
- } else if (flowDirectionService.isDefaultBackRecord()) {
- flowNodeService.loadDefaultBackNode(flowRecord);
- } else {
- // 审批拒绝,并且自定了返回节点
- flowNodeService.loadCustomBackNode(flowNode, flowRecord.getPreId());
+ } else {
+ flowNodeService.loadDefaultBackNode(flowNode,flowRecord);
}
this.nextNode = flowNodeService.getNextNode();
}
@@ -282,7 +278,7 @@ private FlowResult submitCurrentFlow() {
if (flowNode.isUnSign()) {
for (FlowRecord record : historyRecords) {
if (record.isTodo() && record.getId() != flowRecord.getId()) {
- record.autoPass(currentOperator, snapshot);
+ record.autoPass(record.getCurrentOperator(), snapshot);
FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository();
flowRecordRepository.update(record);
}
diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowVoidedService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowVoidedService.java
new file mode 100644
index 00000000..059cff2d
--- /dev/null
+++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowVoidedService.java
@@ -0,0 +1,82 @@
+package com.codingapi.springboot.flow.service.impl;
+
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.bind.IBindData;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.event.FlowApprovalEvent;
+import com.codingapi.springboot.flow.record.FlowProcess;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.FlowBindDataRepository;
+import com.codingapi.springboot.flow.repository.FlowProcessRepository;
+import com.codingapi.springboot.flow.repository.FlowRecordRepository;
+import com.codingapi.springboot.flow.repository.FlowWorkRepository;
+import com.codingapi.springboot.flow.user.IFlowOperator;
+import com.codingapi.springboot.framework.event.EventPusher;
+import lombok.AllArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 流程作废服务(流程管理员操作)
+ */
+@Transactional
+@AllArgsConstructor
+public class FlowVoidedService {
+
+ private final FlowWorkRepository flowWorkRepository;
+ private final FlowRecordRepository flowRecordRepository;
+ private final FlowProcessRepository flowProcessRepository;
+ private final FlowBindDataRepository flowBindDataRepository;
+
+
+ /**
+ * 作废流程
+ *
+ * @param processId 流程processId
+ * @param currentOperator 当前操作者
+ */
+ public void voided(String processId, IFlowOperator currentOperator) {
+ if (!currentOperator.isFlowManager()) {
+ throw new IllegalArgumentException("current operator not flow manager");
+ }
+
+ FlowProcess flowProcess = flowProcessRepository.getFlowProcessByProcessId(processId);
+ if(flowProcess.isVoided()){
+ throw new IllegalArgumentException("flow process already voided");
+ }
+ flowProcess.voided();
+ flowProcessRepository.save(flowProcess);
+
+ FlowRecord currentRecord = null;
+ List flowRecords = flowRecordRepository.findFlowRecordByProcessId(processId);
+ if (!flowRecords.isEmpty()) {
+ for (FlowRecord flowRecord : flowRecords) {
+ if (flowRecord.isFinish()) {
+ throw new IllegalArgumentException("flow record already finish");
+ }
+ flowRecord.delete();
+ if(currentRecord==null || flowRecord.getId()> currentRecord.getId()){
+ currentRecord = flowRecord;
+ }
+ }
+ }
+ flowRecordRepository.save(flowRecords);
+
+ FlowWork flowWork = null;
+
+ IBindData bindData = null;
+ if (currentRecord != null) {
+ // 删除流程绑定数据
+ BindDataSnapshot dataSnapshot = flowBindDataRepository.getBindDataSnapshotById(currentRecord.getSnapshotId());
+ if (dataSnapshot != null) {
+ bindData = dataSnapshot.toBindData();
+ }
+ flowWork = flowWorkRepository.getFlowWorkByCode(currentRecord.getWorkCode());
+ }
+
+ EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_VOIDED, currentRecord, currentOperator, flowWork, bindData), true);
+
+
+ }
+}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java
index 2d02fed0..c7b3a7ca 100644
--- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java
@@ -1,11 +1,15 @@
package com.codingapi.springboot.flow.flow;
import com.codingapi.springboot.flow.bind.IBindData;
+import lombok.AllArgsConstructor;
import lombok.Getter;
+import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
+@AllArgsConstructor
+@NoArgsConstructor
public class Leave implements IBindData {
private long id;
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java
new file mode 100644
index 00000000..c6612bc4
--- /dev/null
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java
@@ -0,0 +1,23 @@
+package com.codingapi.springboot.flow.flow;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class Leave2 {
+
+ private long id;
+ private String title;
+ private int days;
+
+ public Leave2(String title) {
+ this(title,0);
+ }
+
+ public Leave2(String title, int days) {
+ this.title = title;
+ this.days = days;
+ }
+
+}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java
index b128f599..e1d96880 100644
--- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java
@@ -27,7 +27,10 @@ public void save(FlowProcess flowProcess) {
@Override
public FlowWork getFlowWorkByProcessId(String processId) {
- FlowProcess process = cache.stream().filter(flowProcess -> flowProcess.getProcessId().equals(processId)).findFirst().orElse(null);
+ FlowProcess process = cache.stream()
+ .filter(flowProcess -> flowProcess.getProcessId().equals(processId))
+ .filter(flowProcess -> !flowProcess.isVoided())
+ .findFirst().orElse(null);
if (process == null) {
return null;
}
@@ -35,6 +38,11 @@ public FlowWork getFlowWorkByProcessId(String processId) {
return flowBackup.resume(userRepository);
}
+ @Override
+ public FlowProcess getFlowProcessByProcessId(String processId) {
+ return cache.stream().filter(flowProcess -> flowProcess.getProcessId().equals(processId)).findFirst().orElse(null);
+ }
+
@Override
public void deleteByProcessId(String processId) {
cache.removeIf(flowProcess -> flowProcess.getProcessId().equals(processId));
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java
index 59f60d7b..f5d290a6 100644
--- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java
@@ -26,7 +26,10 @@ public void save(List records) {
@Override
public FlowRecord getFlowRecordById(long id) {
- return cache.stream().filter(record -> record.getId() == id).findFirst().orElse(null);
+ return cache.stream()
+ .filter(record -> record.getId() == id)
+ .filter(record -> !record.isDelete())
+ .findFirst().orElse(null);
}
@@ -40,12 +43,29 @@ public void update(FlowRecord flowRecord) {
@Override
public List findFlowRecordByPreId(long preId) {
- return cache.stream().filter(record -> record.getPreId() == preId).collect(Collectors.toList());
+ return cache.stream()
+ .filter(record -> record.getPreId() == preId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public List findMergeFlowRecordById(String workCode, String nodeCode, long operatorId) {
+ return cache.stream()
+ .filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId
+ && record.getWorkCode().equals(workCode)
+ && record.getNodeCode().equals(nodeCode)
+ && record.isMergeable()
+ )
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
}
@Override
public List findFlowRecordByProcessId(String processId) {
- return cache.stream().filter(record -> record.getProcessId().equals(processId))
+ return cache.stream().
+ filter(record -> record.getProcessId().equals(processId))
+ .filter(record -> !record.isDelete())
.sorted((o1, o2) -> (int) (o1.getCreateTime() - o2.getCreateTime()))
.collect(Collectors.toList());
}
@@ -56,97 +76,133 @@ public List findTodoFlowRecordByProcessId(String processId) {
}
public Page findAll(PageRequest pageRequest) {
- return new PageImpl<>(cache);
+ return new PageImpl<>(cache.stream()
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList()));
}
@Override
public Page findDoneByOperatorId(long operatorId, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isDone() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isDone() && record.getCurrentOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findUnReadByOperatorId(long operatorId, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isUnRead() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isUnRead() && record.getCurrentOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findUnReadByOperatorId(long operatorId, String workCode, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isUnRead() && record.getWorkCode().equals(workCode) && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isUnRead() && record.getWorkCode().equals(workCode) && record.getCurrentOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findDoneByOperatorId(long operatorId, String workCode, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isDone()
- && record.getCurrentOperator().getUserId() == operatorId
- && record.getWorkCode().equals(workCode)
- ).collect(Collectors.toList());
+ List flowRecords = cache.stream().
+ filter(record -> record.isDone()
+ && record.getCurrentOperator().getUserId() == operatorId
+ && record.getWorkCode().equals(workCode)
+ )
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findInitiatedByOperatorId(long operatorId, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isInitiated() && record.getCreateOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isInitiated() && record.getCreateOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findInitiatedByOperatorId(long operatorId, String workCode, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(
- record -> record.isInitiated()
- && record.getCreateOperator().getUserId() == operatorId
- && record.getWorkCode().equals(workCode)
- ).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(
+ record -> record.isInitiated()
+ && record.getCreateOperator().getUserId() == operatorId
+ && record.getWorkCode().equals(workCode)
+ )
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findTodoByOperatorId(long operatorId, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId && record.getWorkCode().equals(workCode)).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId && record.getWorkCode().equals(workCode))
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findTimeoutTodoByOperatorId(long operatorId, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isTimeout() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isTimeout() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findTimeoutTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(
- record -> record.isTimeout()
- && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId
- && record.getWorkCode().equals(workCode)
- ).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(
+ record -> record.isTimeout()
+ && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId
+ && record.getWorkCode().equals(workCode)
+ ).filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findPostponedTodoByOperatorId(long operatorId, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isPostponed() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isPostponed() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId)
+ .filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@Override
public Page findPostponedTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) {
- List flowRecords = cache.stream().filter(record -> record.isPostponed()
- && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId
- && record.getWorkCode().equals(workCode)
- ).collect(Collectors.toList());
+ List flowRecords = cache.stream()
+ .filter(record -> record.isPostponed()
+ && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId
+ && record.getWorkCode().equals(workCode)
+ ).filter(record -> !record.isDelete())
+ .collect(Collectors.toList());
return new PageImpl<>(flowRecords);
}
@@ -154,6 +210,7 @@ public Page findPostponedTodoByOperatorId(long operatorId, String wo
public void finishFlowRecordByProcessId(String processId) {
cache.stream()
.filter(record -> record.getProcessId().equals(processId))
+ .filter(record -> !record.isDelete())
.forEach(FlowRecord::finish);
}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java
index dd4f50f1..1342286b 100644
--- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java
@@ -1,6 +1,7 @@
package com.codingapi.springboot.flow.repository;
import com.codingapi.springboot.flow.flow.Leave;
+import com.codingapi.springboot.flow.flow.Leave2;
import java.util.ArrayList;
import java.util.List;
@@ -15,4 +16,8 @@ public void save(Leave leave) {
leave.setId(cache.size());
}
}
+ public void save(Leave2 leave2) {
+ Leave leave = new Leave(leave2.getId(), leave2.getTitle(), leave2.getDays());
+ this.save(leave);
+ }
}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java
index 97010832..93408534 100644
--- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java
@@ -53,7 +53,7 @@ void errorMatcherOperatorTest(){
.title("请假流程")
.nodes()
.node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
- .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createOperatorErrTrigger("+dept.getId()+")}"), true)
+ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createOperatorErrTrigger("+dept.getId()+")}"), true,false)
.node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
.node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
.relations()
@@ -145,7 +145,7 @@ void errorMatcherNodeTest(){
.title("请假流程")
.nodes()
.node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
- .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createNodeErrTrigger('manager')}"), true)
+ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createNodeErrTrigger('manager')}"), true,false)
.node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
.node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
.relations()
@@ -209,4 +209,84 @@ void errorMatcherNodeTest(){
List snapshots = flowBindDataRepository.findAll();
assertEquals(3, snapshots.size());
}
+
+
+ /**
+ * 相同人审批通过,指定人员审批时遇到异常,走异常节点配置
+ */
+ @Test
+ void sameUserFlow() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮");
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .skipIfSameApprover(true)
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(user.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()), new ErrTrigger("" +
+ "def run(content){" +
+ " return content.createOperatorErrTrigger(" + dept.getUserId() + ")" +
+ "}"+
+ ""),true, false)
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave leave = new Leave("我要出去看看");
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, leave, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(user.getUserId()));
+
+ // 查看刘备经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(1, deptTodos.size());
+
+ // 提交委托dept部门经理的审批
+ FlowRecord deptTodo = deptTodos.get(0);
+ flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意"));
+
+ // 查看所有流程
+ List records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+
+ userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(0, userTodos.size());
+
+ records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+ // 查看所有流程是否都已经结束
+ assertTrue(records.stream().allMatch(FlowRecord::isFinish));
+
+ List snapshots = flowBindDataRepository.findAll();
+ assertEquals(4, snapshots.size());
+
+ }
}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowBackTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowBackTest.java
new file mode 100644
index 00000000..022d7790
--- /dev/null
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowBackTest.java
@@ -0,0 +1,207 @@
+package com.codingapi.springboot.flow.test;
+
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.build.FlowWorkBuilder;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.domain.Opinion;
+import com.codingapi.springboot.flow.em.ApprovalType;
+import com.codingapi.springboot.flow.flow.Leave;
+import com.codingapi.springboot.flow.matcher.OperatorMatcher;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.*;
+import com.codingapi.springboot.flow.service.FlowService;
+import com.codingapi.springboot.flow.user.User;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.domain.PageRequest;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class FlowBackTest {
+
+ private final UserRepository userRepository = new UserRepository();
+ private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl();
+ private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl();
+ private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl();
+ private final LeaveRepository leaveRepository = new LeaveRepository();
+ private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl();
+ private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository, userRepository);
+ private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository, flowProcessRepository, flowBackupRepository);
+
+ /**
+ * 流程退回测试,结束的流程作废失败
+ */
+ @Test
+ void back1() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮",true);
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .skipIfSameApprover(true)
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()))
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave leave = new Leave("我要出去看看");
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, leave, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意"));
+
+ // 查看刘备经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(1, deptTodos.size());
+
+ // 提交委托dept部门经理的审批
+ FlowRecord deptTodo = deptTodos.get(0);
+ flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意"));
+
+ assertThrows(IllegalArgumentException.class,()->{
+ // 退回流程
+ flowService.back(deptTodo.getProcessId(),"start",boss);
+ });
+
+ // 查看所有流程
+ List records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+
+ userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(0, userTodos.size());
+
+ records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+ // 查看所有流程是否都已经结束
+ assertTrue(records.stream().allMatch(FlowRecord::isFinish));
+
+ List snapshots = flowBindDataRepository.findAll();
+ assertEquals(4, snapshots.size());
+
+ }
+
+
+ /**
+ * 流程退回测试,在结束节点前退回流程
+ */
+ @Test
+ void back2() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮",true);
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .skipIfSameApprover(true)
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave leave = new Leave("我要出去看看");
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, leave, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意"));
+
+ // 查看刘备经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(1, deptTodos.size());
+
+ // 提交委托dept部门经理的审批
+ FlowRecord deptTodo = deptTodos.get(0);
+ // 流程作废
+ flowService.back(deptTodo.getProcessId(),"start",boss);
+
+ // 查看所有流程
+ List records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(1, records.size());
+
+ userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意"));
+
+
+ deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(1, deptTodos.size());
+
+ // 提交委托dept部门经理的审批
+ deptTodo = deptTodos.get(0);
+ flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意"));
+
+ List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ FlowRecord bossTodo = bossTodos.get(0);
+ flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意"));
+
+ records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+ // 查看所有流程是否都已经结束
+ assertTrue(records.stream().allMatch(FlowRecord::isFinish));
+
+ List snapshots = flowBindDataRepository.findAll();
+ assertEquals(5, snapshots.size());
+
+ }
+}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java
new file mode 100644
index 00000000..a14c7e98
--- /dev/null
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java
@@ -0,0 +1,127 @@
+package com.codingapi.springboot.flow.test;
+
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.bind.FlowMapBindData;
+import com.codingapi.springboot.flow.build.FlowWorkBuilder;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.domain.Opinion;
+import com.codingapi.springboot.flow.em.ApprovalType;
+import com.codingapi.springboot.flow.flow.Leave2;
+import com.codingapi.springboot.flow.matcher.OperatorMatcher;
+import com.codingapi.springboot.flow.pojo.FlowDetail;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.*;
+import com.codingapi.springboot.flow.service.FlowService;
+import com.codingapi.springboot.flow.user.User;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.domain.PageRequest;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FlowMapTest {
+
+ private final UserRepository userRepository = new UserRepository();
+ private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl();
+ private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl();
+ private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl();
+ private final LeaveRepository leaveRepository = new LeaveRepository();
+ private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl();
+ private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository, userRepository);
+ private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository, flowProcessRepository, flowBackupRepository);
+
+ /**
+ * map数据绑定对象测试
+ */
+ @Test
+ void mapFlowTest() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮");
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave2 leave = new Leave2("我要出去看看");
+ FlowMapBindData bindData = FlowMapBindData.fromObject(leave);
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, bindData, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ // 保存流程
+ leave.setTitle("我要出去看看~~");
+ bindData = FlowMapBindData.fromObject(leave);
+ flowService.save(userTodo.getId(), user, bindData, "暂存");
+
+ // 查看流程详情
+ FlowDetail flowDetail = flowService.detail(userTodo.getId(), user);
+ assertEquals("我要出去看看~~", (flowDetail.getBindData().toJavaObject(Leave2.class)).getTitle());
+ assertTrue(flowDetail.getFlowRecord().isRead());
+
+
+ flowService.submitFlow(userTodo.getId(), user, bindData, Opinion.pass("同意"));
+
+ // 查看部门经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(1, deptTodos.size());
+
+ // 提交部门经理的审批
+ FlowRecord deptTodo = deptTodos.get(0);
+ flowService.submitFlow(deptTodo.getId(), dept, bindData, Opinion.pass("同意"));
+
+ // 查看总经理的待办
+ List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ // 提交总经理的审批
+ FlowRecord bossTodo = bossTodos.get(0);
+ flowService.submitFlow(bossTodo.getId(), boss, bindData, Opinion.pass("同意"));
+
+ // 查看所有流程
+ List records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+
+ userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(0, userTodos.size());
+
+
+ records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+ // 查看所有流程是否都已经结束
+ assertTrue(records.stream().allMatch(FlowRecord::isFinish));
+
+ List snapshots = flowBindDataRepository.findAll();
+ assertEquals(4, snapshots.size());
+
+ }
+}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowRecallTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowRecallTest.java
new file mode 100644
index 00000000..ddeb67b9
--- /dev/null
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowRecallTest.java
@@ -0,0 +1,236 @@
+package com.codingapi.springboot.flow.test;
+
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.build.FlowWorkBuilder;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.domain.Opinion;
+import com.codingapi.springboot.flow.em.ApprovalType;
+import com.codingapi.springboot.flow.flow.Leave;
+import com.codingapi.springboot.flow.matcher.OperatorMatcher;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.*;
+import com.codingapi.springboot.flow.service.FlowService;
+import com.codingapi.springboot.flow.user.User;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.domain.PageRequest;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FlowRecallTest {
+
+ private final UserRepository userRepository = new UserRepository();
+ private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl();
+ private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl();
+ private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl();
+ private final LeaveRepository leaveRepository = new LeaveRepository();
+ private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl();
+ private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository, userRepository);
+ private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository, flowProcessRepository, flowBackupRepository);
+
+ /**
+ * 撤回测试(发起人撤销测试)
+ */
+ @Test
+ void recall1() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User lorne = new User("lorne");
+ userRepository.save(lorne);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮");
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),lorne.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave leave = new Leave("我要出去看看");
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, leave, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(lorne.getUserId()));
+
+ // 查看刘备经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(0, deptTodos.size());
+
+ List lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneTodos.size());
+
+ // 提交委托lorne部门经理的审批
+ FlowRecord lorneTodo = lorneTodos.get(0);
+ flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意"));
+
+ List userDones = flowRecordRepository.findDoneByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userDones.size());
+
+ FlowRecord userDone = userDones.get(0);
+ flowService.recall(userDone.getId(),user);
+
+ userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(lorne.getUserId()));
+
+ lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneTodos.size());
+
+ lorneTodo = lorneTodos.get(0);
+ flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意"));
+
+ // 查看总经理的待办
+ List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ // 提交委托lorne部门经理的审批
+ FlowRecord bossTodo = bossTodos.get(0);
+ flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意"));
+
+ // 查看所有流程
+ List records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+
+ // 查看所有流程是否都已经结束
+ assertTrue(records.stream().allMatch(FlowRecord::isFinish));
+
+ List snapshots = flowBindDataRepository.findAll();
+ assertEquals(6, snapshots.size());
+
+ }
+
+
+ /**
+ * 撤回测试(流程已读)
+ */
+ @Test
+ void recall2() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User lorne = new User("lorne");
+ userRepository.save(lorne);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮");
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),lorne.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave leave = new Leave("我要出去看看");
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, leave, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(lorne.getUserId()));
+
+ // 查看刘备经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(0, deptTodos.size());
+
+ List lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneTodos.size());
+
+ // 提交委托lorne部门经理的审批
+ FlowRecord lorneTodo = lorneTodos.get(0);
+ flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意"));
+
+ // 查看总经理的待办
+ List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ // 提交委托boos部门经理的审批
+ FlowRecord bossTodo = bossTodos.get(0);
+ flowService.detail(bossTodo.getId());
+ flowService.save(bossTodo.getId(), boss, leave, "...");
+
+ List lorneDones = flowRecordRepository.findDoneByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneDones.size());
+
+ FlowRecord lorneDone = lorneDones.get(0);
+ flowService.recall(lorneDone.getId(),lorne);
+
+ lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneTodos.size());
+
+ lorneTodo = lorneTodos.get(0);
+ flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意"));
+
+ bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ bossTodo = bossTodos.get(0);
+ flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意"));
+
+ // 查看所有流程
+ List records = flowRecordRepository.findAll(pageRequest).getContent();
+ assertEquals(3, records.size());
+
+ // 查看所有流程是否都已经结束
+ assertTrue(records.stream().allMatch(FlowRecord::isFinish));
+
+ List snapshots = flowBindDataRepository.findAll();
+ assertEquals(5, snapshots.size());
+
+ }
+
+}
diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowRejectTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowRejectTest.java
new file mode 100644
index 00000000..bd63f867
--- /dev/null
+++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowRejectTest.java
@@ -0,0 +1,458 @@
+package com.codingapi.springboot.flow.test;
+
+import com.codingapi.springboot.flow.bind.BindDataSnapshot;
+import com.codingapi.springboot.flow.build.FlowWorkBuilder;
+import com.codingapi.springboot.flow.domain.FlowWork;
+import com.codingapi.springboot.flow.domain.Opinion;
+import com.codingapi.springboot.flow.em.ApprovalType;
+import com.codingapi.springboot.flow.flow.Leave;
+import com.codingapi.springboot.flow.matcher.OperatorMatcher;
+import com.codingapi.springboot.flow.pojo.FlowResult;
+import com.codingapi.springboot.flow.record.FlowRecord;
+import com.codingapi.springboot.flow.repository.*;
+import com.codingapi.springboot.flow.service.FlowService;
+import com.codingapi.springboot.flow.trigger.OutTrigger;
+import com.codingapi.springboot.flow.user.User;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.domain.PageRequest;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FlowRejectTest {
+
+ private final UserRepository userRepository = new UserRepository();
+ private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl();
+ private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl();
+ private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl();
+ private final LeaveRepository leaveRepository = new LeaveRepository();
+ private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl();
+ private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository, userRepository);
+ private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository, flowProcessRepository, flowBackupRepository);
+
+ /**
+ * 驳回测试
+ */
+ @Test
+ void reject1() {
+ PageRequest pageRequest = PageRequest.of(0, 1000);
+
+ User lorne = new User("lorne");
+ userRepository.save(lorne);
+
+ User user = new User("张飞");
+ userRepository.save(user);
+
+ User dept = new User("刘备");
+ userRepository.save(dept);
+
+ User boss = new User("诸葛亮");
+ userRepository.save(boss);
+
+ FlowWork flowWork = FlowWorkBuilder.builder(user)
+ .title("请假流程")
+ .nodes()
+ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .node("部门领导审批", "dept", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),lorne.getUserId()))
+ .node("总经理审批", "manager", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId()))
+ .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher())
+ .relations()
+ .relation("部门领导审批", "start", "dept")
+ .relation("总经理审批", "dept", "manager")
+ .relation("总经理审批", "dept", "start",new OutTrigger(" def run(content) {\n" +
+ " return false;\n" +
+ " }"),1,true)
+ .relation("结束节点", "manager", "over")
+ .build();
+
+ flowWorkRepository.save(flowWork);
+
+ String workCode = flowWork.getCode();
+
+ Leave leave = new Leave("我要出去看看");
+ leaveRepository.save(leave);
+
+ // 创建流程
+ flowService.startFlow(workCode, user, leave, "发起流程");
+
+ // 查看我的待办
+ List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent();
+ assertEquals(1, userTodos.size());
+
+ // 提交流程
+ FlowRecord userTodo = userTodos.get(0);
+ assertEquals(0, userTodo.getTimeoutTime());
+
+ flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(lorne.getUserId()));
+
+ // 查看刘备经理的待办
+ List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(0, deptTodos.size());
+
+ List lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneTodos.size());
+
+ // 提交委托lorne部门经理的审批
+ FlowRecord lorneTodo = lorneTodos.get(0);
+ flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意"));
+
+ // 查看总经理的待办
+ List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ // 提交总经理的审批
+ FlowRecord bossTodo = bossTodos.get(0);
+ FlowResult flowResult = flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.reject("不同意"));
+ System.out.println(flowResult.getRecords());
+
+ deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent();
+ assertEquals(0, deptTodos.size());
+
+ lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent();
+ assertEquals(1, lorneTodos.size());
+
+ lorneTodo = lorneTodos.get(0);
+ flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意"));
+
+ bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent();
+ assertEquals(1, bossTodos.size());
+
+ bossTodo = bossTodos.get(0);
+ flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意"));
+
+ // 查看所有流程
+ List