diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java
index 980f97f24..240bac4b7 100644
--- a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java
+++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java
@@ -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";
+
/**
* 流程结束原因:拒绝
*/
diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/ProcessInstanceParamDto.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/ProcessInstanceParamDto.java
index 6f4797751..e0609612d 100644
--- a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/ProcessInstanceParamDto.java
+++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/ProcessInstanceParamDto.java
@@ -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}
+ *
+ * 枚举值:0(未开始)、1(进行中)、2(已结束)、9(已终止)。
+ *
+ */
private Integer status;
- /** 完成原因 */
+ /**
+ * 完成原因,取值定义见 {@link ProcessInstanceConstant}
+ *
+ * 取值:0(拒绝)、1(通过)、3(撤回)、9(终止)。
+ *
+ */
private String finishReason;
}
diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java
index d3713caaa..df94c2624 100644
--- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java
+++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java
@@ -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 ServiceImpllambdaUpdate()
- .set(BpmOaLeaveEntity::getLeaveStatus, paramDto.getStatus())
+ .set(BpmOaLeaveEntity::getLeaveStatus, Convert.toInt(paramDto.getFinishReason(), null))
.eq(BpmOaLeaveEntity::getProcessInstanceId, paramDto.getProcessInstanceId()));
}
diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java
index d9a3c917d..f95a124a9 100644
--- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java
+++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java
@@ -336,14 +336,16 @@ public class ProcessInstanceServiceImpl implements IProcessInstanceService {
@Override
public R end(ProcessInstanceParamDto processInstanceParamDto) {
- // 从参数中提取审批条件,用于判断流程结束原因(通过/拒绝)
- Optional objectOptional = processInstanceParamDto.getParamMap().keySet().stream().filter(key -> key.contains("approve_condition")).reduce((first, second) -> second).map(key -> MapUtil.getBool(processInstanceParamDto.getParamMap(), key, true));
+ // 拒绝结束时会写入明确的结束原因;正常流转到结束节点时默认为通过
+ Map 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 lambdaUpdateChainWrapper = processInstanceRecordService.lambdaUpdate().set(ProcessInstanceRecord::getEndTime, new Date()).set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode());
-
- // 如果有审批结果,设置结束原因
- objectOptional.ifPresent(aBoolean -> lambdaUpdateChainWrapper.set(ProcessInstanceRecord::getFinishReason, aBoolean));
+ LambdaUpdateChainWrapper 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());
diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java
index 7a0a22db1..2eada9c80 100644
--- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java
+++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java
@@ -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,工作流将沿其正常路径继续
}
-}
\ No newline at end of file
+}
diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImplTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImplTests.java
new file mode 100644
index 000000000..e8c902d60
--- /dev/null
+++ b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImplTests.java
@@ -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> wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class);
+ verify(service).update(wrapperCaptor.capture());
+ LambdaUpdateWrapper updateWrapper = (LambdaUpdateWrapper) wrapperCaptor
+ .getValue();
+ updateWrapper.getSqlSet();
+ return (Integer) updateWrapper.getParamNameValuePairs().values().iterator().next();
+ }
+
+}
diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImplTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImplTests.java
new file mode 100644
index 000000000..aab4f9457
--- /dev/null
+++ b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImplTests.java
@@ -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 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 variables = new LinkedHashMap<>();
+ variables.put("approve_approve_condition", true);
+
+ assertThat(captureFinishReason(variables)).isEqualTo(ProcessInstanceConstant.FINISH_REASON_PASS);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private Object captureFinishReason(Map variables) {
+ IProcessInstanceRecordService recordService = mock(IProcessInstanceRecordService.class);
+ LambdaUpdateChainWrapper 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