From a7d5c69442a5723fabaa0e637c5bb10927557c8c Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 12 Feb 2026 15:01:43 +0800 Subject: [PATCH] #13 --- .../com/codingapi/flow/form/FormData.java | 2 +- .../com/codingapi/flow/form/FormMeta.java | 7 +- .../flow/pojo/response/FlowContent.java | 103 +++++++++++++- .../flow/pojo/response/FlowOperator.java | 21 +++ .../com/codingapi/flow/record/FlowRecord.java | 29 ++++ .../flow/repository/FlowRecordRepository.java | 16 +++ .../flow/service/impl/FlowActionService.java | 5 +- .../flow/service/impl/FlowDetailService.java | 108 +++++++++++++-- .../node/FormFieldPermissionStrategy.java | 2 +- .../repository/FlowRecordRepositoryImpl.java | 14 ++ .../service/FlowMergeableServiceTest.java | 130 ++++++++++++++++++ .../infra/jpa/FlowRecordEntityRepository.java | 6 + .../impl/FlowRecordRepositoryImpl.java | 15 ++ 13 files changed, 436 insertions(+), 22 deletions(-) create mode 100644 flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowOperator.java diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormData.java b/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormData.java index 09fc4fad..cfafd9c2 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormData.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormData.java @@ -123,7 +123,7 @@ public static class DataBody { public DataBody(FormMeta formMeta) { this.formMeta = formMeta; this.data = new HashMap<>(); - this.fieldTypes = formMeta.getMainFieldTypeMaps(); + this.fieldTypes = formMeta.loadMainFieldTypeMaps(); } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormMeta.java b/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormMeta.java index bfc932bc..5539b15b 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormMeta.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/form/FormMeta.java @@ -1,6 +1,5 @@ package com.codingapi.flow.form; -import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -91,7 +90,7 @@ public static FormMeta fromMap(Map map) { /** * 获取表单字段名称 */ - public List getFieldNames() { + public List loadFieldNames() { return fields.stream().map(FormFieldMeta::getName).toList(); } @@ -118,7 +117,7 @@ private void initFormFieldTypes(FormMeta form, Map types) { * * @return 表单字段类型 */ - public Map getAllFieldTypeMaps() { + public Map loadAllFieldTypeMaps() { Map types = new HashMap<>(); List forms = this.getSubForms(); if (forms == null) { @@ -137,7 +136,7 @@ public Map getAllFieldTypeMaps() { * * @return 表单字段类型 */ - public Map getMainFieldTypeMaps() { + public Map loadMainFieldTypeMaps() { Map types = new HashMap<>(); List forms = new ArrayList<>(); forms.add(this); diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowContent.java b/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowContent.java index 6a897873..fb093789 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowContent.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowContent.java @@ -2,8 +2,17 @@ import com.codingapi.flow.action.IFlowAction; import com.codingapi.flow.form.FormMeta; +import com.codingapi.flow.manager.NodeStrategyManager; +import com.codingapi.flow.manager.OperatorManager; +import com.codingapi.flow.node.IFlowNode; +import com.codingapi.flow.operator.IFlowOperator; +import com.codingapi.flow.record.FlowRecord; +import com.codingapi.flow.session.FlowSession; +import com.codingapi.flow.strategy.node.OperatorLoadStrategy; +import com.codingapi.flow.workflow.Workflow; import lombok.Data; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -48,7 +57,22 @@ public class FlowContent { /** * 发起者id */ - private long createOperatorId; + private FlowOperator createOperator; + + /** + * 当前审批者id + */ + private FlowOperator currentOperator; + + /** + * 流程状态 | 运行中、已完成、异常、删除 + */ + private int flowState; + + /** + * 节点状态 | 待办、已办 + */ + private int recordState; /** * 历史记录 @@ -60,6 +84,78 @@ public class FlowContent { */ private List nextNodes; + public void pushNextNodes(FlowSession flowSession, List nextNodes) { + List nextNodeList = new ArrayList<>(); + for (IFlowNode node : nextNodes){ + NextNode nextNode = new NextNode(); + nextNode.setNodeId(node.getId()); + nextNode.setNodeName(node.getName()); + nextNode.setNodeType(node.getType()); + + NodeStrategyManager nodeStrategyManager = node.strategyManager(); + OperatorManager operatorManager = nodeStrategyManager.loadOperators(flowSession); + nextNode.setOperators(operatorManager.getOperators().stream().map(FlowOperator::new).toList()); + + nextNodeList.add(nextNode); + } + this.nextNodes = nextNodeList; + } + + public void pushCurrentNode(IFlowNode currentNode) { + this.actions = currentNode.actionManager().getActions(); + Map nodeData = currentNode.toMap(); + this.view = (String) nodeData.get("view"); + } + + public void pushWorkflow(Workflow workflow) { + this.form = workflow.getForm(); + this.workflowCode = workflow.getCode(); + } + + public void pushRecords(FlowRecord record, List mergeRecords) { + this.recordId = record.getId(); + this.createOperator = new FlowOperator(record.getCreateOperatorId(), record.getCreateOperatorName()); + this.mergeable = record.isMergeable(); + this.flowState = record.getFlowState(); + this.recordState = record.getRecordState(); + + this.todos = new ArrayList<>(); + for (FlowRecord item : mergeRecords){ + Body body = new Body(); + body.setRecordId(item.getId()); + body.setTitle(item.getTitle()); + body.setData(item.getFormData()); + body.setRecordState(item.getRecordState()); + body.setFlowState(item.getFlowState()); + this.todos.add(body); + } + } + + public void pushHistory(Workflow workflow,List historyRecords) { + this.histories = new ArrayList<>(); + for (FlowRecord item : historyRecords){ + IFlowNode node = workflow.getFlowNode(item.getNodeId()); + History history = new History(); + history.setRecordId(item.getId()); + history.setTitle(item.getTitle()); + history.setAdvice(item.getAdvice()); + history.setSignKey(item.getSignKey()); + history.setNodeName(node.getName()); + history.setNodeId(item.getNodeId()); + history.setNodeType(item.getNodeType()); + history.setUpdateTime(item.getUpdateTime()); + history.setCurrentOperatorId(item.getCurrentOperatorId()); + history.setCurrentOperatorName(item.getCurrentOperatorName()); + this.histories.add(history); + } + } + + public void pushCurrentOperator(IFlowOperator currentOperator) { + this.currentOperator = new FlowOperator(currentOperator); + this.createOperator = new FlowOperator(currentOperator); + } + + /** * 流程图 */ @@ -77,6 +173,11 @@ public static class NextNode{ * 节点类型 */ private String nodeType; + + /** + * 节点审批人 + */ + private List operators; } @Data diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowOperator.java b/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowOperator.java new file mode 100644 index 00000000..82a61b1c --- /dev/null +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/FlowOperator.java @@ -0,0 +1,21 @@ +package com.codingapi.flow.pojo.response; + +import com.codingapi.flow.operator.IFlowOperator; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 流程审批人 + */ +@Data +@AllArgsConstructor +public class FlowOperator { + + private long id; + private String name; + + public FlowOperator(IFlowOperator flowOperator) { + this.id = flowOperator.getUserId(); + this.name = flowOperator.getName(); + } +} diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/record/FlowRecord.java b/flow-engine-framework/src/main/java/com/codingapi/flow/record/FlowRecord.java index 519b7dfa..ca3fe795 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/record/FlowRecord.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/record/FlowRecord.java @@ -1,7 +1,9 @@ package com.codingapi.flow.record; import com.codingapi.flow.action.IFlowAction; +import com.codingapi.flow.context.RepositoryHolderContext; import com.codingapi.flow.exception.FlowValidationException; +import com.codingapi.flow.form.FormData; import com.codingapi.flow.manager.NodeStrategyManager; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.operator.IFlowOperator; @@ -14,6 +16,8 @@ import lombok.Setter; import org.springframework.util.StringUtils; +import java.util.ArrayList; +import java.util.List; import java.util.Map; /** @@ -543,4 +547,29 @@ public void newRecord() { public boolean isForward() { return forwardOperatorId > 0; } + + + /** + * 创建会话 + * @param workflow 流程设计器 + * @param currentOperator 当前操作人 + * @param formData 表单数据 + * @param advice 节点审批信息 + * @return FlowSession + */ + public FlowSession createFlowSession( Workflow workflow,IFlowOperator currentOperator,FormData formData, FlowAdvice advice) { + List currentRecords = RepositoryHolderContext.getInstance().findCurrentNodeRecords(this.getFromId(), this.getNodeId()); + IFlowNode currentNode = workflow.getFlowNode(nodeId); + return new FlowSession( + currentOperator, + workflow, + currentNode, + advice.getAction(), + formData, + this, + currentRecords, + this.workBackupId, + advice + ); + } } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/repository/FlowRecordRepository.java b/flow-engine-framework/src/main/java/com/codingapi/flow/repository/FlowRecordRepository.java index 8c6ae3ab..e5e1ece0 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/repository/FlowRecordRepository.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/repository/FlowRecordRepository.java @@ -16,6 +16,13 @@ public interface FlowRecordRepository { */ FlowRecord get(long id); + /** + * 批量获取流程详细 + * @param ids 流程id列表 + * @return 批量流程记录 + */ + List findByIds(List ids); + /** * 保存流程 * 为何确保待办合并数据的一致性,保存流程需要通过 {@link com.codingapi.flow.context.RepositoryHolderContext#saveRecord(FlowRecord)} 保存 @@ -69,4 +76,13 @@ public interface FlowRecordRepository { */ List findAfterRecords(String processId, long fromId); + + /** + * 查询所有之前的流程记录 + * @param processId 流程id + * @param id 记录id + * @return 记录列表 + */ + List findBeforeRecords(String processId,long id); + } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowActionService.java b/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowActionService.java index 451a50e5..ad11264a 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowActionService.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowActionService.java @@ -18,8 +18,6 @@ import com.codingapi.flow.session.FlowSession; import com.codingapi.flow.workflow.Workflow; -import java.util.List; - /** * 节点动作服务 */ @@ -78,8 +76,7 @@ public void action() { formData.reset(request.getFormData()); FlowAdvice flowAdvice = request.toFlowAdvice(workflow, flowAction); - List currentRecords = RepositoryHolderContext.getInstance().findCurrentNodeRecords(flowRecord.getFromId(), flowRecord.getNodeId()); - FlowSession session = new FlowSession(currentOperator, workflow, currentNode, flowAction, formData, flowRecord, currentRecords, workflowBackup.getId(), flowAdvice); + FlowSession session = flowRecord.createFlowSession(workflow,currentOperator,formData,flowAdvice); // 验证会话 currentNode.verifySession(session); // 执行动作 diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowDetailService.java b/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowDetailService.java index 7751f40d..9b5e5691 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowDetailService.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowDetailService.java @@ -1,24 +1,31 @@ package com.codingapi.flow.service.impl; +import com.codingapi.flow.action.IFlowAction; +import com.codingapi.flow.action.actions.PassAction; import com.codingapi.flow.backup.WorkflowBackup; import com.codingapi.flow.context.RepositoryHolderContext; import com.codingapi.flow.exception.FlowNotFoundException; -import com.codingapi.flow.gateway.FlowOperatorGateway; +import com.codingapi.flow.form.FormData; +import com.codingapi.flow.manager.ActionManager; +import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.operator.IFlowOperator; import com.codingapi.flow.pojo.response.FlowContent; import com.codingapi.flow.record.FlowRecord; -import com.codingapi.flow.repository.FlowRecordRepository; -import com.codingapi.flow.repository.WorkflowBackupRepository; -import com.codingapi.flow.repository.WorkflowRepository; +import com.codingapi.flow.record.FlowTodoMerge; +import com.codingapi.flow.record.FlowTodoRecord; +import com.codingapi.flow.repository.*; +import com.codingapi.flow.session.FlowSession; import com.codingapi.flow.workflow.Workflow; -import lombok.AllArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; public class FlowDetailService { private final String id; private final IFlowOperator currentOperator; private final FlowRecordRepository flowRecordRepository; - private final FlowOperatorGateway flowOperatorGateway; private final WorkflowRepository workflowRepository; private final WorkflowBackupRepository workflowBackupRepository; @@ -26,7 +33,6 @@ public FlowDetailService(String id, IFlowOperator currentOperator) { this.id = id; this.currentOperator = currentOperator; this.flowRecordRepository = RepositoryHolderContext.getInstance().getFlowRecordRepository(); - this.flowOperatorGateway = RepositoryHolderContext.getInstance().getFlowOperatorGateway(); this.workflowRepository = RepositoryHolderContext.getInstance().getWorkflowRepository(); this.workflowBackupRepository = RepositoryHolderContext.getInstance().getWorkflowBackupRepository(); } @@ -42,7 +48,7 @@ public FlowContent detail() { if (workflow == null) { throw FlowNotFoundException.workflow(id); } - return new FlowContentFactory(workflow, null).create(); + return new FlowContentFactory(workflow, null, currentOperator).create(); } else { FlowRecord flowRecord = flowRecordRepository.get(Long.parseLong(id)); if (flowRecord == null) { @@ -53,18 +59,98 @@ public FlowContent detail() { throw FlowNotFoundException.workflow(flowRecord.getWorkBackupId() + " not found"); } Workflow workflow = workflowBackup.toWorkflow(); - return new FlowContentFactory(workflow, flowRecord).create(); + return new FlowContentFactory(workflow, flowRecord,currentOperator).create(); } } - @AllArgsConstructor private static class FlowContentFactory { + private final IFlowOperator currentOperator; private final Workflow workflow; private final FlowRecord flowRecord; + private final FlowContent flowContent; + + private final FlowTodoMergeRepository flowTodoMergeRepository; + private final FlowTodoRecordRepository flowTodoRecordRepository; + private final FlowRecordRepository flowRecordRepository; + + public FlowContentFactory(Workflow workflow, FlowRecord flowRecord,IFlowOperator currentOperator) { + this.workflow = workflow; + this.flowRecord = flowRecord; + this.currentOperator = currentOperator; + this.flowTodoMergeRepository = RepositoryHolderContext.getInstance().getFlowTodoMergeRepository(); + this.flowTodoRecordRepository = RepositoryHolderContext.getInstance().getFlowTodoRecordRepository(); + this.flowRecordRepository = RepositoryHolderContext.getInstance().getFlowRecordRepository(); + this.flowContent = new FlowContent(); + + } + + private void loadCurrentOperator(){ + this.flowContent.pushCurrentOperator(currentOperator); + } + + private void loadWorkflow(){ + this.flowContent.pushWorkflow(workflow); + } + + private void loadTodoFlowRecords(){ + if(this.flowRecord!=null){ + if(this.flowRecord.isMergeable()){ + FlowTodoRecord todoRecord = flowTodoRecordRepository.getByMergeKey(flowRecord.getMergeKey()); + List todoMerges = flowTodoMergeRepository.findByTodoId(todoRecord.getId()); + List margeRecords = flowRecordRepository.findByIds(todoMerges.stream().map(FlowTodoMerge::getRecordId).toList()); + this.flowContent.pushRecords(this.flowRecord, margeRecords); + }else { + this.flowContent.pushRecords(this.flowRecord,List.of(this.flowRecord)); + } + } + } + + + private void loadHistoryRecords() { + if (flowRecord != null) { + List historyRecords = flowRecordRepository.findBeforeRecords(flowRecord.getProcessId(), flowRecord.getId()); + this.flowContent.pushHistory(workflow,historyRecords); + } + } + + private void loadNextNodes() { + List nextNodes = new ArrayList<>(); + IFlowNode currentNode = null; + FlowSession flowSession = null; + if (flowRecord != null) { + currentNode = workflow.getFlowNode(flowRecord.getNodeId()); + FormData formData = new FormData(workflow.getForm()); + formData.reset(flowRecord.getFormData()); + flowSession = flowRecord.createFlowSession(workflow,currentOperator,formData,flowRecord.toAdvice(workflow)); + }else { + currentNode = workflow.getStartNode(); + ActionManager actionManager = currentNode.actionManager(); + IFlowAction flowAction = actionManager.getAction(PassAction.class); + flowSession = FlowSession.startSession(currentOperator, workflow, currentNode, flowAction, null, 0); + } + if (currentNode != null) { + List nodes = workflow.nextNodes(currentNode); + if(nodes!=null && !nodes.isEmpty()){ + nextNodes.addAll(nodes); + } + } + + this.flowContent.pushNextNodes(flowSession,nextNodes); + if(currentNode!=null) { + this.flowContent.pushCurrentNode(currentNode); + } + } + public FlowContent create() { - return new FlowContent(); + this.loadCurrentOperator(); + this.loadWorkflow(); + this.loadNextNodes(); + this.loadTodoFlowRecords(); + this.loadHistoryRecords(); + + return flowContent; } } } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java b/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java index ca36936d..55912dd3 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java @@ -42,7 +42,7 @@ public FormFieldPermissionStrategy(List fieldPermissions) { */ @Override public void verifyNode(FormMeta form) { - Map fieldTypes = form.getAllFieldTypeMaps(); + Map fieldTypes = form.loadAllFieldTypeMaps(); for (FormFieldPermission permission : fieldPermissions) { String key = permission.getFormCode() + "." + permission.getFieldCode(); if (!fieldTypes.containsKey(key)) { diff --git a/flow-engine-framework/src/test/java/com/codingapi/flow/repository/FlowRecordRepositoryImpl.java b/flow-engine-framework/src/test/java/com/codingapi/flow/repository/FlowRecordRepositoryImpl.java index c0371c60..d32eee8a 100644 --- a/flow-engine-framework/src/test/java/com/codingapi/flow/repository/FlowRecordRepositoryImpl.java +++ b/flow-engine-framework/src/test/java/com/codingapi/flow/repository/FlowRecordRepositoryImpl.java @@ -15,6 +15,10 @@ public FlowRecord get(long id) { return cache.get(id); } + @Override + public List findByIds(List ids) { + return ids.stream().map(cache::get).toList(); + } public List findTodoByOperator(long operatorId) { return cache.values().stream().filter(flowRecord -> flowRecord.getCurrentOperatorId() == operatorId && flowRecord.isTodo()).toList(); @@ -83,4 +87,14 @@ public List findAfterRecords(String processId, long fromId) { && !flowRecord.isHidden() ).toList(); } + + @Override + public List findBeforeRecords(String processId, long id) { + return cache.values().stream().filter(flowRecord -> + flowRecord.getProcessId().equals(processId) + && flowRecord.getId() < id + && !flowRecord.isRevoked() + && !flowRecord.isHidden() + ).toList(); + } } diff --git a/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowMergeableServiceTest.java b/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowMergeableServiceTest.java index 2684d680..25c402cf 100644 --- a/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowMergeableServiceTest.java +++ b/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowMergeableServiceTest.java @@ -16,6 +16,7 @@ import com.codingapi.flow.pojo.body.FlowAdviceBody; import com.codingapi.flow.pojo.request.FlowActionRequest; import com.codingapi.flow.pojo.request.FlowCreateRequest; +import com.codingapi.flow.pojo.response.FlowContent; import com.codingapi.flow.record.FlowRecord; import com.codingapi.flow.record.FlowTodoRecord; import com.codingapi.flow.record.FlowTodoMerge; @@ -34,6 +35,7 @@ import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class FlowMergeableServiceTest { @@ -175,4 +177,132 @@ void mergeableRecords() { } + + + + /** + * 流程详情 + */ + @Test + void detail() { + + User user = new User(1, "user"); + User boss = new User(2, "boss"); + userGateway.save(user); + userGateway.save(boss); + + GatewayContext.getInstance().setFlowOperatorGateway(userGateway); + + FormMeta form = FormMetaBuilder.builder() + .name("请假流程") + .code("leave") + .addField("请假人", "name", "string") + .addField("请假天数", "days", "int") + .addField("请假事由", "reason", "string") + .build(); + + StartNode startNode = StartNode + .builder() + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .build()) + .actions(ActionBuilder.builder() + .addAction(new CustomAction()) + .build()) + .build(); + + ApprovalNode bossNode = ApprovalNode.builder() + .name("经理审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(2)]}")) + .build() + ) + .build(); + + EndNode endNode = EndNode.builder().build(); + Workflow workflow = WorkflowBuilder.builder() + .title("请假流程") + .code("leave") + .createdOperator(user) + .form(form) + .addNode(startNode) + .addNode(bossNode) + .addNode(endNode) + .build(); + + workflowRepository.save(workflow); + + FlowContent detail = flowService.detail(workflow.getId(), user); + assertEquals(detail.getForm().getCode(), form.getCode()); + assertEquals(detail.getActions().size(), startNode.actionManager().getActions().size()); + assertEquals(1, detail.getNextNodes().size()); + assertNull(detail.getTodos()); + assertNull(detail.getHistories()); + + + Map data = Map.of("name", "lorne", "days", 1, "reason", "leave"); + + List startActions = startNode.actionManager().getActions(); + FlowCreateRequest userCreateRequest = new FlowCreateRequest(); + userCreateRequest.setWorkId(workflow.getId()); + userCreateRequest.setFormData(data); + userCreateRequest.setActionId(startActions.get(0).id()); + userCreateRequest.setOperatorId(user.getUserId()); + flowService.create(userCreateRequest); + + List userRecordList = flowRecordRepository.findTodoByOperator(user.getUserId()); + assertEquals(1, userRecordList.size()); + + detail = flowService.detail(String.valueOf(userRecordList.get(0).getId()), user); + assertEquals(detail.getForm().getCode(), form.getCode()); + assertEquals(detail.getActions().size(), startNode.actionManager().getActions().size()); + assertEquals(1, detail.getNextNodes().size()); + assertEquals(1, detail.getTodos().size()); + assertEquals(1, detail.getNextNodes().get(0).getOperators().size()); + assertEquals(boss.getUserId(), detail.getNextNodes().get(0).getOperators().get(0).getId()); + assertEquals(0, detail.getHistories().size()); + assertEquals(data, detail.getTodos().get(0).getData()); + + + FlowActionRequest userRequest = new FlowActionRequest(); + userRequest.setFormData(data); + userRequest.setRecordId(userRecordList.get(0).getId()); + userRequest.setAdvice(new FlowAdviceBody(startActions.get(0).id(), "同意", user.getUserId())); + flowService.action(userRequest); + + List bossRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, bossRecordList.size()); + + detail = flowService.detail(String.valueOf(bossRecordList.get(0).getId()), user); + assertEquals(detail.getForm().getCode(), form.getCode()); + assertEquals(detail.getActions().size(), bossNode.actionManager().getActions().size()); + assertEquals(1, detail.getNextNodes().size()); + assertEquals(1, detail.getTodos().size()); + assertEquals(1, detail.getHistories().size()); + assertEquals(0, detail.getNextNodes().get(0).getOperators().size()); + assertEquals(endNode.getId(), detail.getNextNodes().get(0).getNodeId()); + assertEquals(data, detail.getTodos().get(0).getData()); + + List bossActions = bossNode.actionManager().getActions(); + + FlowActionRequest bossRequest = new FlowActionRequest(); + bossRequest.setFormData(data); + bossRequest.setRecordId(bossRecordList.get(0).getId()); + bossRequest.setAdvice(new FlowAdviceBody(bossActions.get(0).id(), "同意", boss.getUserId())); + flowService.action(bossRequest); + + List records = flowRecordRepository.findProcessRecords(bossRecordList.get(0).getProcessId()); + assertEquals(3, records.size()); + assertEquals(3, records.stream().filter(FlowRecord::isFinish).toList().size()); + + } } diff --git a/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java b/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java index 3f46eb6c..da8ffcfa 100644 --- a/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java +++ b/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java @@ -12,6 +12,9 @@ public interface FlowRecordEntityRepository extends FastRepository findByIds(List ids); + @Query("from FlowRecordEntity r where r.processId = ?1") List findProcessIdRecords(String processId); @@ -24,6 +27,9 @@ public interface FlowRecordEntityRepository extends FastRepository=?2 and r.hidden=false and r.revoked = false") List findAfterRecords(String processId, long fromId); + @Query("from FlowRecordEntity r where r.processId = ?1 and r.id < ?2 and r.hidden=false and r.revoked = false") + List findBeforeRecords(String processId, long id); + @Query("from FlowRecordEntity r where r.currentOperatorId = ?1 and (r.recordState = 0 and r.flowState = 0 and r.hidden=false and r.revoked = false)") Page findTodoRecordPage(long operatorId, PageRequest pageRequest); diff --git a/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/repository/impl/FlowRecordRepositoryImpl.java b/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/repository/impl/FlowRecordRepositoryImpl.java index 25d9215a..79472277 100644 --- a/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/repository/impl/FlowRecordRepositoryImpl.java +++ b/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/repository/impl/FlowRecordRepositoryImpl.java @@ -19,6 +19,13 @@ public FlowRecord get(long id) { return FlowRecordConvertor.convert(flowRecordEntityRepository.getFlowRecordEntityById(id)); } + @Override + public List findByIds(List ids) { + return flowRecordEntityRepository.findByIds(ids).stream() + .map(FlowRecordConvertor::convert) + .toList(); + } + @Override public void save(FlowRecord flowRecord) { FlowRecordEntity entity = FlowRecordConvertor.convert(flowRecord); @@ -69,4 +76,12 @@ public List findAfterRecords(String processId, long fromId) { .map(FlowRecordConvertor::convert) .toList(); } + + @Override + public List findBeforeRecords(String processId, long id) { + return flowRecordEntityRepository.findBeforeRecords(processId, id) + .stream() + .map(FlowRecordConvertor::convert) + .toList(); + } }