diff --git a/.gitignore b/.gitignore
index 988d5f855..5cc135113 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,5 +63,9 @@ application-preview.yml
### node ###
node_modules
+### claude code ###
+.claude
+docs
+
### test
-*-aitest*
\ No newline at end of file
+aitest
\ No newline at end of file
diff --git a/pigx-flow/pigx-flow-api/pom.xml b/pigx-flow/pigx-flow-api/pom.xml
index 0ddeb5fdf..a730ebb01 100644
--- a/pigx-flow/pigx-flow-api/pom.xml
+++ b/pigx-flow/pigx-flow-api/pom.xml
@@ -7,7 +7,7 @@
com.pig4cloud
pigx-flow
- 5.11.0
+ 5.12.0
pigx-flow-api
@@ -33,9 +33,11 @@
com.pig4cloud
pigx-common-excel
+
- com.pig4cloud
- pigx-common-core
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ true
diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/config/FlowStatusEventAutoConfiguration.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/config/FlowStatusEventAutoConfiguration.java
new file mode 100644
index 000000000..2cf6e0b54
--- /dev/null
+++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/config/FlowStatusEventAutoConfiguration.java
@@ -0,0 +1,58 @@
+package com.pig4cloud.pigx.flow.config;
+
+import com.pig4cloud.pigx.flow.event.FlowStatusEventListener;
+import com.pig4cloud.pigx.flow.event.FlowStatusEventPublisher;
+import com.pig4cloud.pigx.flow.event.IProcessInstanceStatusEventService;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.listener.ChannelTopic;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+import java.util.List;
+
+/**
+ * 流程状态事件自动配置
+ *
+ * @author lengleng
+ * @date 2026-02-10
+ */
+@AutoConfiguration
+@ConditionalOnClass(StringRedisTemplate.class)
+public class FlowStatusEventAutoConfiguration {
+
+ /**
+ * 事件发布者 - 所有依赖 flow-api 的模块可用
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public FlowStatusEventPublisher flowStatusEventPublisher(StringRedisTemplate redisTemplate) {
+ return new FlowStatusEventPublisher(redisTemplate);
+ }
+
+ /**
+ * 事件监听器 - 仅当有 IProcessInstanceStatusEventService 实现时启用
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnBean(IProcessInstanceStatusEventService.class)
+ public FlowStatusEventListener flowStatusEventListener(List eventServices,
+ StringRedisTemplate redisTemplate) {
+ return new FlowStatusEventListener(eventServices, redisTemplate);
+ }
+
+ /**
+ * 注册监听器到 Redis 容器
+ */
+ @Bean
+ @ConditionalOnBean(FlowStatusEventListener.class)
+ public ChannelTopic flowStatusTopic(RedisMessageListenerContainer container, FlowStatusEventListener listener) {
+ ChannelTopic topic = new ChannelTopic(FlowStatusEventPublisher.CHANNEL);
+ container.addMessageListener(listener, topic);
+ return topic;
+ }
+
+}
diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/FlowStatusEventListener.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/FlowStatusEventListener.java
new file mode 100644
index 000000000..791787665
--- /dev/null
+++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/FlowStatusEventListener.java
@@ -0,0 +1,52 @@
+package com.pig4cloud.pigx.flow.event;
+
+import cn.hutool.json.JSONUtil;
+import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.util.List;
+
+/**
+ * 流程状态事件监听器
+ *
+ * 监听 Redis Channel,将事件分发给匹配的 IProcessInstanceStatusEventService 实现
+ *
+ *
+ * @author lengleng
+ * @date 2026-02-10
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class FlowStatusEventListener implements MessageListener {
+
+ private final List eventServices;
+
+ private final StringRedisTemplate redisTemplate;
+
+ @Override
+ public void onMessage(Message message, byte[] pattern) {
+ String body = redisTemplate.getStringSerializer().deserialize(message.getBody());
+ ProcessInstanceParamDto paramDto = JSONUtil.toBean(body, ProcessInstanceParamDto.class);
+
+ log.debug("收到流程状态事件: flowId={}, processInstanceId={}", paramDto.getFlowId(),
+ paramDto.getProcessInstanceId());
+
+ // 分发给匹配 flowId 的实现
+ for (IProcessInstanceStatusEventService service : eventServices) {
+ if (service.getFlowId().equals(paramDto.getFlowId())) {
+ try {
+ service.handleStatusEvent(paramDto);
+ }
+ catch (Exception e) {
+ log.error("处理流程状态事件失败: flowId={}, service={}", paramDto.getFlowId(),
+ service.getClass().getSimpleName(), e);
+ }
+ }
+ }
+ }
+
+}
diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/FlowStatusEventPublisher.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/FlowStatusEventPublisher.java
new file mode 100644
index 000000000..8cf2c3dbb
--- /dev/null
+++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/FlowStatusEventPublisher.java
@@ -0,0 +1,37 @@
+package com.pig4cloud.pigx.flow.event;
+
+import cn.hutool.json.JSONUtil;
+import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+/**
+ * 流程状态事件发布者
+ *
+ * 将流程状态变更事件发布到 Redis Channel
+ *
+ *
+ * @author lengleng
+ * @date 2026-02-10
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class FlowStatusEventPublisher {
+
+ public static final String CHANNEL = "pigx:flow:status";
+
+ private final StringRedisTemplate redisTemplate;
+
+ /**
+ * 发布流程状态变更事件
+ * @param paramDto 流程实例参数
+ */
+ public void publish(ProcessInstanceParamDto paramDto) {
+ String message = JSONUtil.toJsonStr(paramDto);
+ redisTemplate.convertAndSend(CHANNEL, message);
+ log.debug("发布流程状态事件: flowId={}, processInstanceId={}", paramDto.getFlowId(),
+ paramDto.getProcessInstanceId());
+ }
+
+}
diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/IProcessInstanceStatusEventService.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/IProcessInstanceStatusEventService.java
new file mode 100644
index 000000000..6afb8a1b9
--- /dev/null
+++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/event/IProcessInstanceStatusEventService.java
@@ -0,0 +1,29 @@
+package com.pig4cloud.pigx.flow.event;
+
+import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
+
+/**
+ * 流程实例状态事件服务接口
+ *
+ * 实现此接口以监听流程状态变更事件。
+ * 通过 Redis Pub/Sub 实现跨模块通知。
+ *
+ *
+ * @author lengleng
+ * @date 2026-02-10
+ */
+public interface IProcessInstanceStatusEventService {
+
+ /**
+ * 获取监听的流程定义Key
+ * @return 监听的流程定义Key
+ */
+ String getFlowId();
+
+ /**
+ * 处理状态事件
+ * @param paramDto 流程实例参数DTO
+ */
+ void handleStatusEvent(ProcessInstanceParamDto paramDto);
+
+}
diff --git a/pigx-flow/pigx-flow-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/pigx-flow/pigx-flow-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..0a86175a6
--- /dev/null
+++ b/pigx-flow/pigx-flow-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.pig4cloud.pigx.flow.config.FlowStatusEventAutoConfiguration
\ No newline at end of file
diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/BpmOaLeaveService.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/BpmOaLeaveService.java
index 52e439b2b..4da92c8b9 100644
--- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/BpmOaLeaveService.java
+++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/BpmOaLeaveService.java
@@ -2,7 +2,14 @@ package com.pig4cloud.pigx.flow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.flow.entity.BpmOaLeaveEntity;
+import com.pig4cloud.pigx.flow.event.IProcessInstanceStatusEventService;
+/**
+ * 请假申请服务接口
+ *
+ * @author lengleng
+ * @date 2026-02-10
+ */
public interface BpmOaLeaveService extends IService, IProcessInstanceStatusEventService {
/**
diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java
index 3c67cef54..f0efc1c48 100644
--- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java
+++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java
@@ -23,8 +23,8 @@ import com.pig4cloud.pigx.flow.dto.Node;
import com.pig4cloud.pigx.flow.dto.NodeUser;
import com.pig4cloud.pigx.flow.entity.Process;
import com.pig4cloud.pigx.flow.entity.ProcessStarter;
+import com.pig4cloud.pigx.flow.event.IProcessInstanceStatusEventService;
import com.pig4cloud.pigx.flow.mapper.ProcessMapper;
-import com.pig4cloud.pigx.flow.service.IProcessInstanceStatusEventService;
import com.pig4cloud.pigx.flow.service.IProcessService;
import com.pig4cloud.pigx.flow.service.IProcessStarterService;
import com.pig4cloud.pigx.flow.support.utils.ModelUtil;
@@ -53,6 +53,7 @@ import java.util.Optional;
*
*
* @author cxygzl
+ * @date 2026-02-10
* @since 2023-05-25
*/
@Slf4j
diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/NodeTaskCompleteNotify.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/NodeTaskCompleteNotify.java
index 6b7ab81da..f40a04d5e 100644
--- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/NodeTaskCompleteNotify.java
+++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/NodeTaskCompleteNotify.java
@@ -10,17 +10,14 @@ import com.pig4cloud.pigx.common.security.util.NonWebTokenContextHolder;
import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
import com.pig4cloud.pigx.flow.dto.ProcessNodeRecordAssignUserParamDto;
import com.pig4cloud.pigx.flow.entity.ProcessInstanceRecord;
+import com.pig4cloud.pigx.flow.event.FlowStatusEventPublisher;
import com.pig4cloud.pigx.flow.mapper.ProcessInstanceRecordMapper;
-import com.pig4cloud.pigx.flow.service.IProcessInstanceStatusEventService;
import com.pig4cloud.pigx.flow.service.IProcessNodeDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
-import java.util.List;
-import java.util.Optional;
-
/**
* 节点任务完成通知服务
*
@@ -29,7 +26,7 @@ import java.util.Optional;
*
*
* @author lengleng
- * @date 2024/7/9
+ * @date 2026-02-10
*/
@Slf4j
@Service
@@ -40,7 +37,7 @@ public class NodeTaskCompleteNotify {
private final ProcessInstanceRecordMapper processInstanceRecordMapper;
- private final Optional> processInstanceStatusEventServicesOptional;
+ private final FlowStatusEventPublisher flowStatusEventPublisher;
/**
* 异步发送任务完成通知
@@ -76,33 +73,28 @@ public class NodeTaskCompleteNotify {
}
/**
- * 异步发送流程通知
- * @param token 认证令牌
- * @param instanceRecordParamDto 流程节点记录参数DTO
- * @throws IllegalArgumentException 参数为空时抛出异常
+ * 异步发送流程实例状态变更通知
+ *
+ * 当流程实例状态发生变更时,查询最新的流程实例记录状态,
+ * 并通过Redis发布流程状态变更事件,供订阅方消费处理。
+ *
+ * @param token 安全令牌,用于在异步线程中传递认证信息
+ * @param instanceRecordParamDto 流程实例参数,包含流程实例ID等信息
*/
public void sendFlowNotify(String token, ProcessInstanceParamDto instanceRecordParamDto) {
// 在异步线程中设置认证令牌
NonWebTokenContextHolder.setToken(token);
- if (processInstanceStatusEventServicesOptional.isEmpty()) {
- return;
- }
- for (IProcessInstanceStatusEventService iProcessInstanceStatusEventService : processInstanceStatusEventServicesOptional
- .get()) {
+ // 查询最新状态
+ ProcessInstanceRecord processInstanceRecord = processInstanceRecordMapper
+ .selectOne(Wrappers.lambdaQuery()
+ .eq(ProcessInstanceRecord::getProcessInstanceId, instanceRecordParamDto.getProcessInstanceId()));
- if (iProcessInstanceStatusEventService.getFlowId().equals(instanceRecordParamDto.getFlowId())) {
- // 处理状态事件
- ProcessInstanceRecord processInstanceRecord = processInstanceRecordMapper.selectOne(Wrappers
- .lambdaQuery()
- .eq(ProcessInstanceRecord::getProcessInstanceId, instanceRecordParamDto.getProcessInstanceId()));
+ instanceRecordParamDto.setStatus(processInstanceRecord.getStatus());
+ instanceRecordParamDto.setFinishReason(processInstanceRecord.getFinishReason());
- instanceRecordParamDto.setStatus(processInstanceRecord.getStatus());
- instanceRecordParamDto.setFinishReason(processInstanceRecord.getFinishReason());
- iProcessInstanceStatusEventService.handleStatusEvent(instanceRecordParamDto);
- }
-
- }
+ // 发布到 Redis
+ flowStatusEventPublisher.publish(instanceRecordParamDto);
}
}