mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
fix(flow): 修正拒绝结束状态
(cherry picked from commit 5158476c3ff652f6a97256497074ba9cbd448ff5)
This commit is contained in:
1 parent
269d7fe5e4
commit
4023b142d1
8 files changed
+237
-10
No files matched your search
+5
@@ -60,6 +60,11 @@ public class ProcessInstanceConstant {
|
||||
*/
|
||||
public static final String REJECT_TO_STARTER_VAR = "rejectToStarter";
|
||||
|
||||
/**
|
||||
* 流程变量:明确的流程结束原因
|
||||
*/
|
||||
public static final String FINISH_REASON_VAR = "_pigx_finish_reason";
|
||||
|
||||
/**
|
||||
* 流程结束原因:拒绝
|
||||
*/
|
||||
|
||||
+14
-2
@@ -1,5 +1,7 @@
|
||||
package com.pig4cloud.pigx.flow.dto;
|
||||
|
||||
import com.pig4cloud.pigx.flow.constant.NodeStatusEnum;
|
||||
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -49,10 +51,20 @@ public class ProcessInstanceParamDto {
|
||||
*/
|
||||
private String processInstanceId;
|
||||
|
||||
/** 状态 */
|
||||
/**
|
||||
* 状态,对应 {@link NodeStatusEnum}
|
||||
* <p>
|
||||
* 枚举值:0(未开始)、1(进行中)、2(已结束)、9(已终止)。
|
||||
* </p>
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/** 完成原因 */
|
||||
/**
|
||||
* 完成原因,取值定义见 {@link ProcessInstanceConstant}
|
||||
* <p>
|
||||
* 取值:0(拒绝)、1(通过)、3(撤回)、9(终止)。
|
||||
* </p>
|
||||
*/
|
||||
private String finishReason;
|
||||
|
||||
}
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package com.pig4cloud.pigx.flow.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@@ -48,7 +49,7 @@ public class BpmOaLeaveServiceImpl extends ServiceImpl<BpmOaLeaveMapper, BpmOaLe
|
||||
@Override
|
||||
public void handleStatusEvent(ProcessInstanceParamDto paramDto) {
|
||||
this.update(Wrappers.<BpmOaLeaveEntity>lambdaUpdate()
|
||||
.set(BpmOaLeaveEntity::getLeaveStatus, paramDto.getStatus())
|
||||
.set(BpmOaLeaveEntity::getLeaveStatus, Convert.toInt(paramDto.getFinishReason(), null))
|
||||
.eq(BpmOaLeaveEntity::getProcessInstanceId, paramDto.getProcessInstanceId()));
|
||||
}
|
||||
|
||||
|
||||
+8
-6
@@ -336,14 +336,16 @@ public class ProcessInstanceServiceImpl implements IProcessInstanceService {
|
||||
@Override
|
||||
public R end(ProcessInstanceParamDto processInstanceParamDto) {
|
||||
|
||||
// 从参数中提取审批条件,用于判断流程结束原因(通过/拒绝)
|
||||
Optional<Boolean> objectOptional = processInstanceParamDto.getParamMap().keySet().stream().filter(key -> key.contains("approve_condition")).reduce((first, second) -> second).map(key -> MapUtil.getBool(processInstanceParamDto.getParamMap(), key, true));
|
||||
// 拒绝结束时会写入明确的结束原因;正常流转到结束节点时默认为通过
|
||||
Map<String, Object> paramMap = Optional.ofNullable(processInstanceParamDto.getParamMap())
|
||||
.orElseGet(Collections::emptyMap);
|
||||
String finishReason = MapUtil.getStr(paramMap, ProcessInstanceConstant.FINISH_REASON_VAR);
|
||||
if (StrUtil.isBlank(finishReason)) {
|
||||
finishReason = ProcessInstanceConstant.FINISH_REASON_PASS;
|
||||
}
|
||||
|
||||
// 构建更新条件
|
||||
LambdaUpdateChainWrapper<ProcessInstanceRecord> lambdaUpdateChainWrapper = processInstanceRecordService.lambdaUpdate().set(ProcessInstanceRecord::getEndTime, new Date()).set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode());
|
||||
|
||||
// 如果有审批结果,设置结束原因
|
||||
objectOptional.ifPresent(aBoolean -> lambdaUpdateChainWrapper.set(ProcessInstanceRecord::getFinishReason, aBoolean));
|
||||
LambdaUpdateChainWrapper<ProcessInstanceRecord> lambdaUpdateChainWrapper = processInstanceRecordService.lambdaUpdate().set(ProcessInstanceRecord::getEndTime, new Date()).set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode()).set(ProcessInstanceRecord::getFinishReason, finishReason);
|
||||
|
||||
// 执行更新
|
||||
lambdaUpdateChainWrapper.eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceParamDto.getProcessInstanceId()).update(new ProcessInstanceRecord());
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@ package com.pig4cloud.pigx.flow.support.tasks;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.dto.Refuse;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessNodeDataService;
|
||||
@@ -112,10 +113,12 @@ public class ApproveServiceTask implements JavaDelegate {
|
||||
}
|
||||
else {
|
||||
// 默认行为:重定向到结束节点
|
||||
runtimeService.setVariable(processInstanceId, ProcessInstanceConstant.FINISH_REASON_VAR,
|
||||
ProcessInstanceConstant.FINISH_REASON_REFUSE);
|
||||
stateChangeBuilder.moveActivityIdTo(currentActivityId, "end").changeState();
|
||||
}
|
||||
}
|
||||
// 如果未设置refuseConfig,工作流将沿其正常路径继续
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.pig4cloud.pigx.flow.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.pig4cloud.pigx.flow.api.feign.RemoteFlowApiFlowService;
|
||||
import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
|
||||
import com.pig4cloud.pigx.flow.entity.BpmOaLeaveEntity;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class BpmOaLeaveServiceImplTests {
|
||||
|
||||
@BeforeAll
|
||||
static void initializeTableInfo() {
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), "test"),
|
||||
BpmOaLeaveEntity.class);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({ "0, 0", "1, 1", "3, 3", "9, 9" })
|
||||
void statusEventStoresFinishReasonAsLeaveStatus(String finishReason, int expectedStatus) {
|
||||
assertThat(captureLeaveStatus(finishReason)).isEqualTo(expectedStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
void statusEventClearsLeaveStatusBeforeProcessFinishes() {
|
||||
assertThat(captureLeaveStatus(null)).isNull();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private Integer captureLeaveStatus(String finishReason) {
|
||||
BpmOaLeaveServiceImpl service = spy(new BpmOaLeaveServiceImpl(mock(RemoteFlowApiFlowService.class)));
|
||||
doReturn(true).when(service).update(any(Wrapper.class));
|
||||
ProcessInstanceParamDto paramDto = new ProcessInstanceParamDto();
|
||||
paramDto.setProcessInstanceId("process-7598");
|
||||
paramDto.setFinishReason(finishReason);
|
||||
|
||||
service.handleStatusEvent(paramDto);
|
||||
|
||||
ArgumentCaptor<Wrapper<BpmOaLeaveEntity>> wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class);
|
||||
verify(service).update(wrapperCaptor.capture());
|
||||
LambdaUpdateWrapper<BpmOaLeaveEntity> updateWrapper = (LambdaUpdateWrapper<BpmOaLeaveEntity>) wrapperCaptor
|
||||
.getValue();
|
||||
updateWrapper.getSqlSet();
|
||||
return (Integer) updateWrapper.getParamNameValuePairs().values().iterator().next();
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.pig4cloud.pigx.flow.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
|
||||
import com.pig4cloud.pigx.flow.entity.ProcessInstanceRecord;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessCopyService;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessInstanceRecordService;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessNodeRecordAssignUserService;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessNodeRecordService;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessService;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.RETURNS_SELF;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ProcessInstanceServiceImplTests {
|
||||
|
||||
@Test
|
||||
void endPersistsExplicitRefusalWhenHistoricalApprovalVariableAppearsLast() {
|
||||
Map<String, Object> variables = new LinkedHashMap<>();
|
||||
variables.put("current_approve_condition", false);
|
||||
variables.put("previous_approve_condition", true);
|
||||
variables.put(ProcessInstanceConstant.FINISH_REASON_VAR, ProcessInstanceConstant.FINISH_REASON_REFUSE);
|
||||
|
||||
assertThat(captureFinishReason(variables)).isEqualTo(ProcessInstanceConstant.FINISH_REASON_REFUSE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endDefaultsToPassWhenNoExplicitFinishReasonExists() {
|
||||
Map<String, Object> variables = new LinkedHashMap<>();
|
||||
variables.put("approve_approve_condition", true);
|
||||
|
||||
assertThat(captureFinishReason(variables)).isEqualTo(ProcessInstanceConstant.FINISH_REASON_PASS);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private Object captureFinishReason(Map<String, Object> variables) {
|
||||
IProcessInstanceRecordService recordService = mock(IProcessInstanceRecordService.class);
|
||||
LambdaUpdateChainWrapper<ProcessInstanceRecord> update = mock(LambdaUpdateChainWrapper.class, RETURNS_SELF);
|
||||
when(recordService.lambdaUpdate()).thenReturn(update);
|
||||
doReturn(update).when(update).set(any(), any());
|
||||
doReturn(update).when(update).eq(any(), any());
|
||||
|
||||
ProcessInstanceServiceImpl service = new ProcessInstanceServiceImpl(mock(RemoteUserService.class),
|
||||
mock(TaskService.class), mock(RuntimeService.class), mock(HistoryService.class), recordService,
|
||||
mock(IProcessCopyService.class), mock(IProcessService.class), mock(IProcessNodeRecordService.class),
|
||||
mock(IProcessNodeRecordAssignUserService.class), new ObjectMapper());
|
||||
ProcessInstanceParamDto request = new ProcessInstanceParamDto();
|
||||
request.setProcessInstanceId("process-7598");
|
||||
request.setParamMap(variables);
|
||||
|
||||
service.end(request);
|
||||
|
||||
ArgumentCaptor<Object> values = ArgumentCaptor.forClass(Object.class);
|
||||
verify(update, times(3)).set(any(), values.capture());
|
||||
return values.getAllValues().get(2);
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.pig4cloud.pigx.flow.support.tasks;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.dto.Refuse;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessNodeDataService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.flowable.engine.runtime.ChangeActivityStateBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ApproveServiceTaskTests {
|
||||
|
||||
@Test
|
||||
void directRejectionMarksFinishReasonBeforeMovingToEnd() {
|
||||
ExecutionEntityImpl execution = mock(ExecutionEntityImpl.class);
|
||||
when(execution.getActivityId()).thenReturn("approve_service_task_approve-1");
|
||||
when(execution.getProcessDefinitionKey()).thenReturn("leave");
|
||||
when(execution.getProcessInstanceId()).thenReturn("process-7598");
|
||||
when(execution.getVariable("approve-1_approve_condition", Boolean.class)).thenReturn(false);
|
||||
|
||||
RuntimeService runtimeService = mock(RuntimeService.class);
|
||||
ChangeActivityStateBuilder stateChangeBuilder = mock(ChangeActivityStateBuilder.class);
|
||||
when(runtimeService.createChangeActivityStateBuilder()).thenReturn(stateChangeBuilder);
|
||||
when(stateChangeBuilder.processInstanceId("process-7598")).thenReturn(stateChangeBuilder);
|
||||
when(stateChangeBuilder.moveActivityIdTo("approve_service_task_approve-1", "end"))
|
||||
.thenReturn(stateChangeBuilder);
|
||||
|
||||
Refuse refuse = new Refuse();
|
||||
refuse.setHandler("TO_END");
|
||||
Node node = new Node();
|
||||
node.setRefuse(refuse);
|
||||
IProcessNodeDataService nodeDataService = mock(IProcessNodeDataService.class);
|
||||
when(nodeDataService.getNodeData("leave", "approve-1")).thenReturn(R.ok(node));
|
||||
|
||||
ApplicationContext previousContext = SpringUtil.getApplicationContext();
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.getBeanFactory().registerSingleton("runtimeService", runtimeService);
|
||||
context.getBeanFactory().registerSingleton("processNodeDataService", nodeDataService);
|
||||
context.refresh();
|
||||
new SpringUtil().setApplicationContext(context);
|
||||
try {
|
||||
new ApproveServiceTask().execute(execution);
|
||||
}
|
||||
finally {
|
||||
new SpringUtil().setApplicationContext(previousContext);
|
||||
context.close();
|
||||
}
|
||||
|
||||
InOrder inOrder = inOrder(runtimeService, stateChangeBuilder);
|
||||
inOrder.verify(runtimeService).setVariable("process-7598", ProcessInstanceConstant.FINISH_REASON_VAR,
|
||||
ProcessInstanceConstant.FINISH_REASON_REFUSE);
|
||||
inOrder.verify(stateChangeBuilder).moveActivityIdTo("approve_service_task_approve-1", "end");
|
||||
inOrder.verify(stateChangeBuilder).changeState();
|
||||
verify(runtimeService).createChangeActivityStateBuilder();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user