mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-12 13:39:01 +08:00
feat(pigx-flow-api): 新增流程状态事件发布订阅机制
- 新增 FlowStatusEventPublisher 用于发布流程状态变更事件到 Redis Channel - 新增 FlowStatusEventListener 用于监听 Redis 消息并分发给匹配的事件服务 - 新增 IProcessInstanceStatusEventService 接口定义流程状态事件处理规范 - 新增 FlowStatusEventAutoConfiguration 自动配置类,实现事件发布者和监听器的 Bean 注册 - 重构 NodeTaskCompleteNotify 服务,使用事件发布机制替代直接调用事件服务 - 将 IProcessInstanceStatusEventService 接口移动到 flow-api 模块的 event 包下 - 在 pigx-flow-api 模块添加 spring-boot-starter-data-redis 依赖支持 - 更新相关服务的包引用和日期注释
This commit is contained in:
1 parent
d1858d5844
commit
2754607924
10 files changed
+214
-31
No files matched your search
+5
-1
@@ -63,5 +63,9 @@ application-preview.yml
|
||||
### node ###
|
||||
node_modules
|
||||
|
||||
### claude code ###
|
||||
.claude
|
||||
docs
|
||||
|
||||
### test
|
||||
*-aitest*
|
||||
aitest
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow</artifactId>
|
||||
<version>5.11.0</version>
|
||||
<version>5.12.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-flow-api</artifactId>
|
||||
@@ -33,9 +33,11 @@
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-excel</artifactId>
|
||||
</dependency>
|
||||
<!-- Redis 支持,用于流程状态事件 Pub/Sub -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-core</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+58
@@ -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<IProcessInstanceStatusEventService> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -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;
|
||||
|
||||
/**
|
||||
* 流程状态事件监听器
|
||||
* <p>
|
||||
* 监听 Redis Channel,将事件分发给匹配的 IProcessInstanceStatusEventService 实现
|
||||
* </p>
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-02-10
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class FlowStatusEventListener implements MessageListener {
|
||||
|
||||
private final List<IProcessInstanceStatusEventService> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -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;
|
||||
|
||||
/**
|
||||
* 流程状态事件发布者
|
||||
* <p>
|
||||
* 将流程状态变更事件发布到 Redis Channel
|
||||
* </p>
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.pig4cloud.pigx.flow.event;
|
||||
|
||||
import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto;
|
||||
|
||||
/**
|
||||
* 流程实例状态事件服务接口
|
||||
* <p>
|
||||
* 实现此接口以监听流程状态变更事件。
|
||||
* 通过 Redis Pub/Sub 实现跨模块通知。
|
||||
* </p>
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-02-10
|
||||
*/
|
||||
public interface IProcessInstanceStatusEventService {
|
||||
|
||||
/**
|
||||
* 获取监听的流程定义Key
|
||||
* @return 监听的流程定义Key
|
||||
*/
|
||||
String getFlowId();
|
||||
|
||||
/**
|
||||
* 处理状态事件
|
||||
* @param paramDto 流程实例参数DTO
|
||||
*/
|
||||
void handleStatusEvent(ProcessInstanceParamDto paramDto);
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.pig4cloud.pigx.flow.config.FlowStatusEventAutoConfiguration
|
||||
+7
@@ -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<BpmOaLeaveEntity>, IProcessInstanceStatusEventService {
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -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;
|
||||
* </p>
|
||||
*
|
||||
* @author cxygzl
|
||||
* @date 2026-02-10
|
||||
* @since 2023-05-25
|
||||
*/
|
||||
@Slf4j
|
||||
|
||||
+18
-26
@@ -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;
|
||||
|
||||
/**
|
||||
* 节点任务完成通知服务
|
||||
* <p>
|
||||
@@ -29,7 +26,7 @@ import java.util.Optional;
|
||||
* </p>
|
||||
*
|
||||
* @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<List<IProcessInstanceStatusEventService>> processInstanceStatusEventServicesOptional;
|
||||
private final FlowStatusEventPublisher flowStatusEventPublisher;
|
||||
|
||||
/**
|
||||
* 异步发送任务完成通知
|
||||
@@ -76,33 +73,28 @@ public class NodeTaskCompleteNotify {
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步发送流程通知
|
||||
* @param token 认证令牌
|
||||
* @param instanceRecordParamDto 流程节点记录参数DTO
|
||||
* @throws IllegalArgumentException 参数为空时抛出异常
|
||||
* 异步发送流程实例状态变更通知
|
||||
* <p>
|
||||
* 当流程实例状态发生变更时,查询最新的流程实例记录状态,
|
||||
* 并通过Redis发布流程状态变更事件,供订阅方消费处理。
|
||||
* </p>
|
||||
* @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.<ProcessInstanceRecord>lambdaQuery()
|
||||
.eq(ProcessInstanceRecord::getProcessInstanceId, instanceRecordParamDto.getProcessInstanceId()));
|
||||
|
||||
if (iProcessInstanceStatusEventService.getFlowId().equals(instanceRecordParamDto.getFlowId())) {
|
||||
// 处理状态事件
|
||||
ProcessInstanceRecord processInstanceRecord = processInstanceRecordMapper.selectOne(Wrappers
|
||||
.<ProcessInstanceRecord>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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user