feat(databus): 完成阶段四-DataBus Server完整功能
- 补充缺失的 API 类(DatabusMessage、DatabusBatchMessage、DatabusEventType) - 新增变更消息消费者(3个:部门、用户、岗位) - 新增数据提供者(3个:部门、用户、岗位) - 确认分发器服务(核心定向推送逻辑) - 确认全量同步与消息推送组件 - 确认管理后台 API(5个 Controller) - 确认 Service ��(4个核心服务) - 确认 DAL 层(7个 DO + Mapper) - 添加 databus-server starter 依赖到 pom.xml - 编译验证通过 Ref: docs/databus/implementation-checklist.md 任务 39-70
This commit is contained in:
71
zt-framework/zt-spring-boot-starter-databus-client/pom.xml
Normal file
71
zt-framework/zt-spring-boot-starter-databus-client/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>zt-spring-boot-starter-databus-client</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>DataBus 客户端组件,负责接收数据变更并同步</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Databus API(事件类型枚举等) -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-databus-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- System API(用于默认Handler实现) -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-system-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MQ 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis 相关 (用于幂等) -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 (用于HTTP接收) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.zt.plat.framework.databus.client.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Databus 同步客户端自动配置
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(DatabusSyncClientProperties.class)
|
||||
@ComponentScan(basePackages = "com.zt.plat.framework.databus.client")
|
||||
public class DatabusSyncClientAutoConfiguration {
|
||||
|
||||
public DatabusSyncClientAutoConfiguration() {
|
||||
log.info("[Databus] 数据同步客户端模块已加载");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.zt.plat.framework.databus.client.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Databus 数据同步客户端配置属性
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "zt.databus.sync.client")
|
||||
public class DatabusSyncClientProperties {
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
/**
|
||||
* RocketMQ NameServer 地址
|
||||
*/
|
||||
private String nameServer;
|
||||
|
||||
/**
|
||||
* 客户端编码(必填,用于订阅专属Topic)
|
||||
* Topic格式: databus-sync-{eventType}-{clientCode}
|
||||
*/
|
||||
private String clientCode;
|
||||
|
||||
/**
|
||||
* MQ配置
|
||||
*/
|
||||
private Mq mq = new Mq();
|
||||
|
||||
/**
|
||||
* HTTP配置
|
||||
*/
|
||||
private Http http = new Http();
|
||||
|
||||
/**
|
||||
* 幂等配置
|
||||
*/
|
||||
private Idempotent idempotent = new Idempotent();
|
||||
|
||||
@Data
|
||||
public static class Mq {
|
||||
/**
|
||||
* 是否启用MQ消费
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
/**
|
||||
* RocketMQ NameServer 地址
|
||||
*/
|
||||
private String nameServer;
|
||||
|
||||
/**
|
||||
* Topic基础名称
|
||||
*/
|
||||
private String topicBase = "databus-sync";
|
||||
|
||||
/**
|
||||
* 消费者组前缀,完整格式: {consumerGroupPrefix}-{eventType}
|
||||
* 默认: databus-client-{clientCode}
|
||||
*/
|
||||
private String consumerGroupPrefix;
|
||||
|
||||
/**
|
||||
* 消费线程数
|
||||
*/
|
||||
private Integer consumeThreadMin = 1;
|
||||
|
||||
/**
|
||||
* 消费线程数
|
||||
*/
|
||||
private Integer consumeThreadMax = 5;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private Integer maxReconsumeTimes = 3;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Http {
|
||||
/**
|
||||
* 是否启用HTTP推送接收
|
||||
*/
|
||||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 接收端点路径
|
||||
*/
|
||||
private String endpoint = "/databus/sync/receive";
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Idempotent {
|
||||
/**
|
||||
* 是否启用幂等检查
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
/**
|
||||
* RocketMQ NameServer 地址
|
||||
*/
|
||||
private String nameServer;
|
||||
|
||||
/**
|
||||
* 幂等记录过期时间(秒)
|
||||
*/
|
||||
private Integer expireSeconds = 86400; // 24小时
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zt.plat.framework.databus.client.core.controller;
|
||||
|
||||
import com.zt.plat.framework.databus.client.core.processor.MessageProcessor;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 同步消息HTTP接收控制器
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("${zt.databus.sync.client.http.endpoint:/databus/sync/receive}")
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(prefix = "zt.databus.sync.client.http", name = "enabled", havingValue = "true")
|
||||
public class SyncMessageController {
|
||||
|
||||
private final MessageProcessor messageProcessor;
|
||||
|
||||
/**
|
||||
* 接收同步消息
|
||||
*
|
||||
* @param eventType 事件类型
|
||||
* @param message 消息体
|
||||
*/
|
||||
@PostMapping("/{eventType}")
|
||||
public void receive(@PathVariable("eventType") String eventType, @RequestBody String message) {
|
||||
log.debug("[Databus Client] 接收到HTTP消息, eventType={}", eventType);
|
||||
|
||||
DatabusEventType type = DatabusEventType.valueOf(eventType);
|
||||
if (type == null) {
|
||||
log.warn("[Databus Client] 未知的事件类型: {}", eventType);
|
||||
return;
|
||||
}
|
||||
|
||||
messageProcessor.process(message, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.zt.plat.framework.databus.client.core.idempotent;
|
||||
|
||||
/**
|
||||
* 幂等存储接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface IdempotentStore {
|
||||
|
||||
/**
|
||||
* 检查并记录消息是否已处理
|
||||
*
|
||||
* @param syncId 同步ID
|
||||
* @param expireSeconds 过期时间(秒)
|
||||
* @return true-未处理(可以处理), false-已处理(应该跳过)
|
||||
*/
|
||||
boolean checkAndMark(String syncId, int expireSeconds);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zt.plat.framework.databus.client.core.idempotent;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 基于 Redis 的幂等存储实现
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RedisIdempotentStore implements IdempotentStore {
|
||||
|
||||
private static final String KEY_PREFIX = "databus:sync:idempotent:";
|
||||
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Override
|
||||
public boolean checkAndMark(String syncId, int expireSeconds) {
|
||||
String key = KEY_PREFIX + syncId;
|
||||
Boolean success = stringRedisTemplate.opsForValue()
|
||||
.setIfAbsent(key, "1", expireSeconds, TimeUnit.SECONDS);
|
||||
return Boolean.TRUE.equals(success);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.zt.plat.framework.databus.client.core.listener;
|
||||
|
||||
import com.zt.plat.framework.databus.client.config.DatabusSyncClientProperties;
|
||||
import com.zt.plat.framework.databus.client.core.processor.MessageProcessor;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
|
||||
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
|
||||
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus Consumer 注册器
|
||||
* <p>
|
||||
* 根据 DatabusEventType 枚举自动为所有事件类型创建消费者
|
||||
* Topic格式: {topicBase}-{module}-{entity}-{action}-{clientCode}
|
||||
* 例如: databus-sync-system-user-create-company-a
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "zt.databus.sync.client.mq", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class DatabusConsumerRegistry {
|
||||
|
||||
private final MessageProcessor messageProcessor;
|
||||
private final DatabusSyncClientProperties properties;
|
||||
|
||||
|
||||
/**
|
||||
* 管理的消费者列表
|
||||
*/
|
||||
private final List<DefaultMQPushConsumer> consumers = new ArrayList<>();
|
||||
|
||||
public DatabusConsumerRegistry(MessageProcessor messageProcessor, DatabusSyncClientProperties properties) {
|
||||
this.messageProcessor = messageProcessor;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (!Boolean.TRUE.equals(properties.getEnabled())) {
|
||||
log.info("[Databus Client] 客户端未启用,跳过初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
String nameServer = properties.getMq().getNameServer();
|
||||
if (nameServer == null || nameServer.isEmpty()) {
|
||||
log.warn("[Databus Client] RocketMQ nameServer未配置,跳过MQ消费者初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
String clientCode = properties.getClientCode();
|
||||
if (clientCode == null || clientCode.isEmpty()) {
|
||||
log.error("[Databus Client] clientCode未配置,无法订阅Topic");
|
||||
return;
|
||||
}
|
||||
|
||||
String topicBase = properties.getMq().getTopicBase();
|
||||
String consumerGroupPrefix = properties.getMq().getConsumerGroupPrefix();
|
||||
if (consumerGroupPrefix == null || consumerGroupPrefix.isEmpty()) {
|
||||
consumerGroupPrefix = "databus-client-" + clientCode;
|
||||
}
|
||||
|
||||
// 为每个事件类型创建独立的消费者
|
||||
for (DatabusEventType eventType : DatabusEventType.values()) {
|
||||
try {
|
||||
createConsumer(eventType, topicBase, clientCode, consumerGroupPrefix, nameServer);
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Client] 创建消费者失败, eventType={}", eventType.name(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("[Databus Client] 消费者注册完成,共创建 {} 个消费者", consumers.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定事件类型创建消费者
|
||||
*/
|
||||
private void createConsumer(DatabusEventType eventType, String topicBase, String clientCode, String consumerGroupPrefix, String nameServer) throws MQClientException {
|
||||
// Topic: databus-sync-system-user-create-company-a
|
||||
String topic = eventType.getTopic(topicBase, clientCode);
|
||||
// ConsumerGroup: databus-client-company-a-system-user-create
|
||||
String consumerGroup = String.format("%s-%s", consumerGroupPrefix, eventType.getTopicSuffix());
|
||||
|
||||
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
|
||||
consumer.setNamesrvAddr(nameServer);
|
||||
consumer.setConsumeThreadMin(properties.getMq().getConsumeThreadMin());
|
||||
consumer.setConsumeThreadMax(properties.getMq().getConsumeThreadMax());
|
||||
consumer.setMaxReconsumeTimes(properties.getMq().getMaxReconsumeTimes());
|
||||
|
||||
// 订阅Topic
|
||||
consumer.subscribe(topic, "*");
|
||||
|
||||
// 设置消息监听器
|
||||
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
|
||||
for (MessageExt msg : msgs) {
|
||||
try {
|
||||
String messageBody = new String(msg.getBody(), StandardCharsets.UTF_8);
|
||||
log.debug("[Databus Client] 收到消息, topic={}, msgId={}, eventType={}",
|
||||
msg.getTopic(), msg.getMsgId(), eventType.name());
|
||||
|
||||
messageProcessor.process(messageBody, eventType);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Client] 消息处理失败, topic={}, msgId={}, reconsumeTimes={}",
|
||||
msg.getTopic(), msg.getMsgId(), msg.getReconsumeTimes(), e);
|
||||
|
||||
if (msg.getReconsumeTimes() >= properties.getMq().getMaxReconsumeTimes()) {
|
||||
log.error("[Databus Client] 重试次数已达上限,放弃处理, msgId={}", msg.getMsgId());
|
||||
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
|
||||
}
|
||||
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
|
||||
}
|
||||
}
|
||||
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
|
||||
});
|
||||
|
||||
consumer.start();
|
||||
consumers.add(consumer);
|
||||
|
||||
log.info("[Databus Client] 消费者启动成功, topic={}, consumerGroup={}, event={}",
|
||||
topic, consumerGroup, eventType.getName());
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
for (DefaultMQPushConsumer consumer : consumers) {
|
||||
try {
|
||||
consumer.shutdown();
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Client] 关闭消费者失败", e);
|
||||
}
|
||||
}
|
||||
consumers.clear();
|
||||
log.info("[Databus Client] 所有消费者已关闭");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.zt.plat.framework.databus.client.core.message;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据同步消息(服务端推送格式)
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SyncMessage {
|
||||
|
||||
/**
|
||||
* 同步ID(唯一标识)
|
||||
*/
|
||||
private String syncId;
|
||||
|
||||
/**
|
||||
* 事件记录ID
|
||||
*/
|
||||
private Long eventRecordId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件动作
|
||||
*/
|
||||
private String eventAction;
|
||||
|
||||
/**
|
||||
* 业务数据快照(JSON字符串)
|
||||
*/
|
||||
private String dataSnapshot;
|
||||
|
||||
/**
|
||||
* 数据版本
|
||||
*/
|
||||
private Integer dataVersion;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package com.zt.plat.framework.databus.client.core.processor;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.databus.client.config.DatabusSyncClientProperties;
|
||||
import com.zt.plat.framework.databus.client.core.idempotent.IdempotentStore;
|
||||
import com.zt.plat.framework.databus.client.core.message.SyncMessage;
|
||||
import com.zt.plat.framework.databus.client.handler.BatchSyncEventHandler;
|
||||
import com.zt.plat.framework.databus.client.handler.SyncEventHandler;
|
||||
import com.zt.plat.module.databus.api.message.DatabusBatchMessage;
|
||||
import com.zt.plat.module.databus.api.message.DatabusMessage;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 消息处理器
|
||||
* <p>
|
||||
* 负责消息的幂等检查和路由到具体的Handler
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class MessageProcessor {
|
||||
|
||||
private final IdempotentStore idempotentStore;
|
||||
private final DatabusSyncClientProperties properties;
|
||||
|
||||
/**
|
||||
* 增量同步处理器列表
|
||||
*/
|
||||
private List<SyncEventHandler> handlers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 全量同步处理器列表
|
||||
*/
|
||||
private List<BatchSyncEventHandler> batchHandlers = new ArrayList<>();
|
||||
|
||||
private Map<DatabusEventType, SyncEventHandler> handlerMap;
|
||||
private Map<DatabusEventType, BatchSyncEventHandler> batchHandlerMap;
|
||||
|
||||
public MessageProcessor(IdempotentStore idempotentStore, DatabusSyncClientProperties properties) {
|
||||
this.idempotentStore = idempotentStore;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setHandlers(List<SyncEventHandler> handlers) {
|
||||
if (handlers != null) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setBatchHandlers(List<BatchSyncEventHandler> batchHandlers) {
|
||||
if (batchHandlers != null) {
|
||||
this.batchHandlers = batchHandlers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理同步消息
|
||||
*
|
||||
* @param messageJson 消息JSON字符串
|
||||
* @param eventType 事件类型枚举
|
||||
*/
|
||||
public void process(String messageJson, DatabusEventType eventType) {
|
||||
try {
|
||||
if (eventType.isFullSync()) {
|
||||
processBatchMessage(messageJson, eventType);
|
||||
} else {
|
||||
processIncrementalMessage(messageJson, eventType);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Client] 消息处理失败, eventType={}, message={}", eventType.name(), messageJson, e);
|
||||
throw new RuntimeException("消息处理失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理增量同步消息
|
||||
*/
|
||||
private void processIncrementalMessage(String messageJson, DatabusEventType eventType) {
|
||||
// 先解析为服务端推送的 SyncMessage 格式
|
||||
SyncMessage syncMessage = JSONUtil.toBean(messageJson, SyncMessage.class);
|
||||
|
||||
// 幂等检查
|
||||
if (properties.getIdempotent().getEnabled()) {
|
||||
boolean canProcess = idempotentStore.checkAndMark(
|
||||
syncMessage.getSyncId(),
|
||||
properties.getIdempotent().getExpireSeconds()
|
||||
);
|
||||
if (!canProcess) {
|
||||
log.info("[Databus Client] 消息已处理,跳过, syncId={}", syncMessage.getSyncId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 路由到对应的Handler
|
||||
SyncEventHandler handler = getHandler(eventType);
|
||||
if (handler == null) {
|
||||
log.warn("[Databus Client] 未找到事件处理器, eventType={}", eventType.name());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取 Handler 期望的数据类型,并转换消息
|
||||
Class<?> dataType = handler.getDataType();
|
||||
DatabusMessage message = convertToDatabusMessage(syncMessage, eventType, dataType);
|
||||
handler.handle(message);
|
||||
log.info("[Databus Client] 增量消息处理成功, syncId={}, eventType={}",
|
||||
syncMessage.getSyncId(), eventType.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 SyncMessage 转换为 DatabusMessage
|
||||
*
|
||||
* @param syncMessage 服务端推送的同步消息
|
||||
* @param eventType 事件类型
|
||||
* @param dataType Handler 期望的数据类型
|
||||
* @return DatabusMessage
|
||||
*/
|
||||
private DatabusMessage convertToDatabusMessage(SyncMessage syncMessage, DatabusEventType eventType, Class<?> dataType) {
|
||||
DatabusMessage message = new DatabusMessage();
|
||||
message.setMessageId(syncMessage.getSyncId());
|
||||
message.setEventType(eventType);
|
||||
|
||||
// 从 dataSnapshot JSON 字符串解析业务数据对象
|
||||
if (syncMessage.getDataSnapshot() != null && !syncMessage.getDataSnapshot().isEmpty()) {
|
||||
// 使用 Handler 提供的类型进行反序列化
|
||||
Object data = JSONUtil.toBean(syncMessage.getDataSnapshot(), dataType);
|
||||
message.setData(data);
|
||||
}
|
||||
|
||||
// 设置时间戳
|
||||
if (syncMessage.getTimestamp() != null) {
|
||||
message.setTimestamp(java.time.LocalDateTime.ofInstant(
|
||||
java.time.Instant.ofEpochMilli(syncMessage.getTimestamp()),
|
||||
java.time.ZoneId.systemDefault()
|
||||
));
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理全量同步批量消息
|
||||
*/
|
||||
private void processBatchMessage(String messageJson, DatabusEventType eventType) {
|
||||
DatabusBatchMessage message = JSONUtil.toBean(messageJson, DatabusBatchMessage.class);
|
||||
|
||||
// 幂等检查
|
||||
if (properties.getIdempotent().getEnabled()) {
|
||||
boolean canProcess = idempotentStore.checkAndMark(
|
||||
message.getMessageId(),
|
||||
properties.getIdempotent().getExpireSeconds()
|
||||
);
|
||||
if (!canProcess) {
|
||||
log.info("[Databus Client] 批量消息已处理,跳过, messageId={}", message.getMessageId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 路由到对应的BatchHandler
|
||||
BatchSyncEventHandler handler = getBatchHandler(eventType);
|
||||
if (handler == null) {
|
||||
log.warn("[Databus Client] 未找到批量事件处理器, eventType={}", eventType.name());
|
||||
return;
|
||||
}
|
||||
|
||||
// 全量同步开始回调(第一批)
|
||||
if (message.getBatchNo() != null && message.getBatchNo() == 1) {
|
||||
handler.onFullSyncStart(message);
|
||||
log.info("[Databus Client] 全量同步开始, taskId={}, eventType={}, totalCount={}",
|
||||
message.getTaskId(), eventType.name(), message.getTotalCount());
|
||||
}
|
||||
|
||||
// 执行批量处理
|
||||
handler.handleBatch(message);
|
||||
log.info("[Databus Client] 批量消息处理成功, messageId={}, eventType={}, batchNo={}/{}, count={}",
|
||||
message.getMessageId(), eventType.name(),
|
||||
message.getBatchNo(), message.getTotalBatch(), message.getCount());
|
||||
|
||||
// 全量同步完成回调(最后一批)
|
||||
if (Boolean.TRUE.equals(message.getIsLastBatch())) {
|
||||
handler.onFullSyncComplete(message);
|
||||
log.info("[Databus Client] 全量同步完成, taskId={}, eventType={}, totalCount={}",
|
||||
message.getTaskId(), eventType.name(), message.getTotalCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取增量同步事件处理器
|
||||
*/
|
||||
private SyncEventHandler getHandler(DatabusEventType eventType) {
|
||||
if (handlerMap == null) {
|
||||
if (handlers == null || handlers.isEmpty()) {
|
||||
handlerMap = Collections.emptyMap();
|
||||
} else {
|
||||
handlerMap = handlers.stream()
|
||||
.collect(Collectors.toMap(
|
||||
SyncEventHandler::getSupportedEventType,
|
||||
Function.identity()
|
||||
));
|
||||
}
|
||||
}
|
||||
return handlerMap.get(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全量同步批量事件处理器
|
||||
*/
|
||||
private BatchSyncEventHandler getBatchHandler(DatabusEventType eventType) {
|
||||
if (batchHandlerMap == null) {
|
||||
if (batchHandlers == null || batchHandlers.isEmpty()) {
|
||||
batchHandlerMap = Collections.emptyMap();
|
||||
} else {
|
||||
batchHandlerMap = batchHandlers.stream()
|
||||
.collect(Collectors.toMap(
|
||||
BatchSyncEventHandler::getSupportedEventType,
|
||||
Function.identity()
|
||||
));
|
||||
}
|
||||
}
|
||||
return batchHandlerMap.get(eventType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zt.plat.framework.databus.client.handler;
|
||||
|
||||
import com.zt.plat.module.databus.api.message.DatabusBatchMessage;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
|
||||
/**
|
||||
* 批量同步事件处理器接口
|
||||
* <p>
|
||||
* 业务系统需要实现此接口来处理全量同步的批量数据
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface BatchSyncEventHandler<T> {
|
||||
|
||||
/**
|
||||
* 获取支持的事件类型
|
||||
*
|
||||
* @return 事件类型枚举
|
||||
*/
|
||||
DatabusEventType getSupportedEventType();
|
||||
|
||||
/**
|
||||
* 处理批量同步消息
|
||||
*
|
||||
* @param message 批量同步消息
|
||||
*/
|
||||
void handleBatch(DatabusBatchMessage<T> message);
|
||||
|
||||
/**
|
||||
* 全量同步开始回调(可选实现)
|
||||
* <p>
|
||||
* 当收到第一批数据(batchNo=1)时调用
|
||||
*
|
||||
* @param message 批量同步消息
|
||||
*/
|
||||
default void onFullSyncStart(DatabusBatchMessage<T> message) {
|
||||
// 默认空实现,子类可覆盖
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量同步完成回调(可选实现)
|
||||
* <p>
|
||||
* 当收到最后一批数据(isLastBatch=true)时调用
|
||||
*
|
||||
* @param message 批量同步消息
|
||||
*/
|
||||
default void onFullSyncComplete(DatabusBatchMessage<T> message) {
|
||||
// 默认空实现,子类可覆盖
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.zt.plat.framework.databus.client.handler;
|
||||
|
||||
import com.zt.plat.module.databus.api.message.DatabusMessage;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 同步事件处理器接口
|
||||
* <p>
|
||||
* 业务系统需要实现此接口来处理增量数据同步
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface SyncEventHandler<T> {
|
||||
|
||||
/**
|
||||
* 获取支持的事件类型
|
||||
*
|
||||
* @return 事件类型枚举
|
||||
*/
|
||||
DatabusEventType getSupportedEventType();
|
||||
|
||||
/**
|
||||
* 处理同步消息
|
||||
*
|
||||
* @param message 同步消息
|
||||
*/
|
||||
void handle(DatabusMessage<T> message);
|
||||
|
||||
/**
|
||||
* 获取数据类型
|
||||
* <p>
|
||||
* 默认通过反射获取泛型类型参数,子类可以覆盖此方法提供具体类型
|
||||
*
|
||||
* @return 数据类型的 Class 对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default Class<T> getDataType() {
|
||||
Type[] genericInterfaces = this.getClass().getGenericInterfaces();
|
||||
for (Type genericInterface : genericInterfaces) {
|
||||
if (genericInterface instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
|
||||
if (parameterizedType.getRawType().equals(SyncEventHandler.class)) {
|
||||
Type[] typeArguments = parameterizedType.getActualTypeArguments();
|
||||
if (typeArguments.length > 0 && typeArguments[0] instanceof Class) {
|
||||
return (Class<T>) typeArguments[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果无法获取泛型类型,返回 Object.class
|
||||
return (Class<T>) Object.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.zt.plat.framework.databus.client.handler.post;
|
||||
|
||||
import com.zt.plat.module.databus.api.data.PostData;
|
||||
import com.zt.plat.module.system.api.dept.PostApi;
|
||||
import com.zt.plat.module.system.api.dept.dto.PostSaveReqDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 岗位同步业务逻辑
|
||||
* <p>
|
||||
* 被各个 PostHandler 共享使用
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@ConditionalOnProperty(prefix = "zt.databus.sync.client", name = "enabled", havingValue = "true")
|
||||
@ConditionalOnClass(name = "com.zt.plat.module.system.api.dept.PostApi")
|
||||
public class PostSyncService {
|
||||
|
||||
@Autowired(required = false)
|
||||
private PostApi postApi;
|
||||
|
||||
/**
|
||||
* 创建岗位
|
||||
*/
|
||||
public void create(PostData data) {
|
||||
if (postApi == null) {
|
||||
log.warn("[PostSync] PostApi未注入,跳过创建岗位操作, postId={}", data.getId());
|
||||
return;
|
||||
}
|
||||
PostSaveReqDTO dto = buildPostDTO(data);
|
||||
postApi.createPost(dto).checkError();
|
||||
log.info("[PostSync] 创建岗位成功, postId={}, postName={}", dto.getId(), dto.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新岗位
|
||||
*/
|
||||
public void update(PostData data) {
|
||||
if (postApi == null) {
|
||||
log.warn("[PostSync] PostApi未注入,跳过更新岗位操作, postId={}", data.getId());
|
||||
return;
|
||||
}
|
||||
PostSaveReqDTO dto = buildPostDTO(data);
|
||||
postApi.updatePost(dto).checkError();
|
||||
log.info("[PostSync] 更新岗位成功, postId={}, postName={}", dto.getId(), dto.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除岗位
|
||||
*/
|
||||
public void delete(PostData data) {
|
||||
if (postApi == null) {
|
||||
log.warn("[PostSync] PostApi未注入,跳过删除岗位操作, postId={}", data.getId());
|
||||
return;
|
||||
}
|
||||
Long postId = data.getId();
|
||||
if (postId != null) {
|
||||
postApi.deletePost(postId).checkError();
|
||||
log.info("[PostSync] 删除岗位成功, postId={}", postId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量同步单条数据(存在则更新,不存在则创建)
|
||||
*/
|
||||
public void fullSync(PostData data) {
|
||||
if (postApi == null) {
|
||||
log.warn("[PostSync] PostApi未注入,跳过全量同步岗位操作, postId={}", data.getId());
|
||||
return;
|
||||
}
|
||||
PostSaveReqDTO dto = buildPostDTO(data);
|
||||
try {
|
||||
if (dto.getId() != null) {
|
||||
var existing = postApi.getPost(dto.getId());
|
||||
if (existing.isSuccess() && existing.getData() != null) {
|
||||
postApi.updatePost(dto).checkError();
|
||||
} else {
|
||||
postApi.createPost(dto).checkError();
|
||||
}
|
||||
} else {
|
||||
postApi.createPost(dto).checkError();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
postApi.createPost(dto).checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建岗位DTO
|
||||
*/
|
||||
private PostSaveReqDTO buildPostDTO(PostData data) {
|
||||
PostSaveReqDTO dto = new PostSaveReqDTO();
|
||||
dto.setId(data.getId());
|
||||
dto.setCode(data.getCode());
|
||||
dto.setName(data.getName());
|
||||
dto.setSort(data.getSort());
|
||||
dto.setStatus(data.getStatus());
|
||||
dto.setRemark(data.getRemark());
|
||||
return dto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.zt.plat.framework.databus.client.handler.post;
|
||||
|
||||
import com.zt.plat.framework.databus.client.handler.BatchSyncEventHandler;
|
||||
import com.zt.plat.module.databus.api.data.PostData;
|
||||
import com.zt.plat.module.databus.api.message.DatabusBatchMessage;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 岗位全量同步事件处理器
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "zt.databus.sync.client", name = "enabled", havingValue = "true")
|
||||
@ConditionalOnBean(PostSyncService.class)
|
||||
public class SystemPostFullHandler implements BatchSyncEventHandler<PostData> {
|
||||
|
||||
@Resource
|
||||
private PostSyncService postSyncService;
|
||||
|
||||
@Override
|
||||
public DatabusEventType getSupportedEventType() {
|
||||
return DatabusEventType.SYSTEM_POST_FULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFullSyncStart(DatabusBatchMessage<PostData> message) {
|
||||
log.info("[PostSync] 全量同步开始, taskId={}, totalCount={}, totalBatch={}",
|
||||
message.getTaskId(), message.getTotalCount(), message.getTotalBatch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBatch(DatabusBatchMessage<PostData> message) {
|
||||
log.info("[PostSync] 处理批次, batchNo={}/{}, count={}",
|
||||
message.getBatchNo(), message.getTotalBatch(), message.getCount());
|
||||
|
||||
if (message.getDataList() == null || message.getDataList().isEmpty()) {
|
||||
log.warn("[PostSync] 数据列表为空, batchNo={}", message.getBatchNo());
|
||||
return;
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
for (PostData data : message.getDataList()) {
|
||||
try {
|
||||
postSyncService.fullSync(data);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
failCount++;
|
||||
log.error("[PostSync] 处理数据项失败, postId={}", data.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("[PostSync] 批次处理完成, batchNo={}, success={}, fail={}",
|
||||
message.getBatchNo(), successCount, failCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFullSyncComplete(DatabusBatchMessage<PostData> message) {
|
||||
log.info("[PostSync] 全量同步完成, taskId={}, totalCount={}",
|
||||
message.getTaskId(), message.getTotalCount());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.zt.plat.framework.databus.client.config.DatabusSyncClientAutoConfiguration
|
||||
@@ -0,0 +1 @@
|
||||
com.zt.plat.framework.databus.client.config.DatabusSyncClientAutoConfiguration
|
||||
73
zt-framework/zt-spring-boot-starter-databus-server/pom.xml
Normal file
73
zt-framework/zt-spring-boot-starter-databus-server/pom.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>zt-spring-boot-starter-databus-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>DataBus 服务端组件,负责发送数据变更消息到各客户端</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Databus API -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-databus-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MQ -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Security -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Tenant -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zt.plat.framework.databus.server.config;
|
||||
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusher;
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusherImpl;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Databus 消息配置类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Configuration
|
||||
public class DatabusMessagingConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MessagePusher messagePusher(@Autowired(required = false) RocketMQTemplate rocketMQTemplate) {
|
||||
MessagePusherImpl pusher = new MessagePusherImpl();
|
||||
pusher.setRocketMQTemplate(rocketMQTemplate);
|
||||
return pusher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.framework.databus.server.config;
|
||||
|
||||
import com.zt.plat.framework.databus.server.producer.DatabusMessageProducer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Databus 服务端自动配置
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(DatabusServerProperties.class)
|
||||
@ConditionalOnProperty(prefix = "zt.databus.sync.server", name = "enabled", havingValue = "true")
|
||||
public class DatabusServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public DatabusMessageProducer databusMessageProducer(DatabusServerProperties properties) {
|
||||
return new DatabusMessageProducer(properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.zt.plat.framework.databus.server.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus 服务端配置属性
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "zt.databus.sync.server")
|
||||
public class DatabusServerProperties {
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* MQ 配置
|
||||
*/
|
||||
private MqConfig mq = new MqConfig();
|
||||
|
||||
/**
|
||||
* 订阅的客户端列表
|
||||
*/
|
||||
private List<String> clients;
|
||||
|
||||
@Data
|
||||
public static class MqConfig {
|
||||
/**
|
||||
* 是否启用 MQ 发送
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* RocketMQ NameServer 地址
|
||||
*/
|
||||
private String nameServer;
|
||||
|
||||
/**
|
||||
* Topic 基础名称
|
||||
*/
|
||||
private String topicBase = "databus-sync";
|
||||
|
||||
/**
|
||||
* 生产者组名称
|
||||
*/
|
||||
private String producerGroup = "databus-server-producer";
|
||||
/**
|
||||
* 发送超时时间(毫秒)
|
||||
*/
|
||||
private int sendMsgTimeout = 10000; // 默认 10 秒
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.zt.plat.framework.databus.server.config;
|
||||
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusher;
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusherImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
* Databus 同步服务端自动配置
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@EnableAsync
|
||||
@EnableConfigurationProperties(DatabusSyncServerProperties.class)
|
||||
@ComponentScan(basePackages = "com.zt.plat.framework.databus.server")
|
||||
@MapperScan("com.zt.plat.framework.databus.server.dal.mapper")
|
||||
public class DatabusSyncServerAutoConfiguration {
|
||||
|
||||
public DatabusSyncServerAutoConfiguration() {
|
||||
log.info("[Databus] 数据同步服务端模块已加载");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册消息推送器 Bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(RocketMQTemplate.class)
|
||||
public MessagePusher messagePusher(RocketMQTemplate rocketMQTemplate) {
|
||||
MessagePusherImpl pusher = new MessagePusherImpl();
|
||||
pusher.setRocketMQTemplate(rocketMQTemplate);
|
||||
log.info("[Databus] MessagePusher Bean 已注册");
|
||||
return pusher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.zt.plat.framework.databus.server.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Databus 数据同步服务端配置属性
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "zt.databus.sync.server")
|
||||
public class DatabusSyncServerProperties {
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 重试配置
|
||||
*/
|
||||
private Retry retry = new Retry();
|
||||
|
||||
/**
|
||||
* 批量推送配置
|
||||
*/
|
||||
private Batch batch = new Batch();
|
||||
|
||||
@Data
|
||||
public static class Retry {
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private Integer maxAttempts = 5;
|
||||
|
||||
/**
|
||||
* 初始重试延迟(秒)
|
||||
*/
|
||||
private Integer initialDelay = 1;
|
||||
|
||||
/**
|
||||
* 重试延迟倍数
|
||||
*/
|
||||
private Integer multiplier = 2;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Batch {
|
||||
/**
|
||||
* 默认批量大小
|
||||
*/
|
||||
private Integer defaultSize = 500;
|
||||
|
||||
/**
|
||||
* 批量推送间隔(秒)
|
||||
*/
|
||||
private Integer interval = 5;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientRespVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncClientConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncClientService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 数据同步客户端")
|
||||
@RestController
|
||||
@RequestMapping("/databus/sync/client")
|
||||
@Validated
|
||||
public class DatabusSyncClientController {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncClientService clientService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据同步客户端")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:create')")
|
||||
public CommonResult<Long> createClient(@Valid @RequestBody DatabusSyncClientSaveReqVO createReqVO) {
|
||||
return success(clientService.createClient(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新数据同步客户端")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:update')")
|
||||
public CommonResult<Boolean> updateClient(@Valid @RequestBody DatabusSyncClientSaveReqVO updateReqVO) {
|
||||
clientService.updateClient(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除数据同步客户端")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:delete')")
|
||||
public CommonResult<Boolean> deleteClient(@RequestParam("id") Long id) {
|
||||
clientService.deleteClient(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得数据同步客户端")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:query')")
|
||||
public CommonResult<DatabusSyncClientRespVO> getClient(@RequestParam("id") Long id) {
|
||||
DatabusSyncClientDO client = clientService.getClient(id);
|
||||
return success(DatabusSyncClientConvert.INSTANCE.convert(client));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得数据同步客户端分页")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:query')")
|
||||
public CommonResult<PageResult<DatabusSyncClientRespVO>> getClientPage(@Valid DatabusSyncClientPageReqVO pageReqVO) {
|
||||
PageResult<DatabusSyncClientDO> pageResult = clientService.getClientPage(pageReqVO);
|
||||
return success(DatabusSyncClientConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得数据同步客户端列表")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:query')")
|
||||
public CommonResult<List<DatabusSyncClientRespVO>> getClientList() {
|
||||
List<DatabusSyncClientDO> list = clientService.getClientList();
|
||||
return success(DatabusSyncClientConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "修改客户端启用状态")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:client:update')")
|
||||
public CommonResult<Boolean> updateClientStatus(
|
||||
@RequestParam("id") Long id,
|
||||
@RequestParam("enabled") Integer enabled) {
|
||||
clientService.updateClientStatus(id, enabled);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterRespVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncDeadLetterConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncDeadLetterService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 数据同步死信队列")
|
||||
@RestController
|
||||
@RequestMapping("/databus/sync/dead-letter")
|
||||
@Validated
|
||||
public class DatabusSyncDeadLetterController {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncDeadLetterService deadLetterService;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得数据同步死信队列")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:query')")
|
||||
public CommonResult<DatabusSyncDeadLetterRespVO> getDeadLetter(@RequestParam("id") Long id) {
|
||||
DatabusSyncDeadLetterDO deadLetter = deadLetterService.getDeadLetter(id);
|
||||
return success(DatabusSyncDeadLetterConvert.INSTANCE.convert(deadLetter));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得数据同步死信队列分页")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:query')")
|
||||
public CommonResult<PageResult<DatabusSyncDeadLetterRespVO>> getDeadLetterPage(@Valid DatabusSyncDeadLetterPageReqVO pageReqVO) {
|
||||
PageResult<DatabusSyncDeadLetterDO> pageResult = deadLetterService.getDeadLetterPage(pageReqVO);
|
||||
return success(DatabusSyncDeadLetterConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@PostMapping("/reprocess")
|
||||
@Operation(summary = "重新投递死信消息")
|
||||
@Parameter(name = "id", description = "死信ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:reprocess')")
|
||||
public CommonResult<Boolean> reprocessDeadLetter(@RequestParam("id") Long id) {
|
||||
deadLetterService.reprocessDeadLetter(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/batch-reprocess")
|
||||
@Operation(summary = "批量重新投递死信消息")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:reprocess')")
|
||||
public CommonResult<Boolean> batchReprocessDeadLetter(@RequestBody List<Long> ids) {
|
||||
deadLetterService.batchReprocessDeadLetter(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/mark-handled")
|
||||
@Operation(summary = "标记为已处理")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:update')")
|
||||
public CommonResult<Boolean> markHandled(
|
||||
@RequestParam("id") Long id,
|
||||
@RequestParam(value = "remark", required = false) String remark) {
|
||||
deadLetterService.markHandled(id, remark);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventRespVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncEventConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncEventService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 数据同步事件")
|
||||
@RestController
|
||||
@RequestMapping("/databus/sync/event")
|
||||
@Validated
|
||||
public class DatabusSyncEventController {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncEventService eventService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据同步事件")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:create')")
|
||||
public CommonResult<Long> createEvent(@Valid @RequestBody DatabusSyncEventSaveReqVO createReqVO) {
|
||||
return success(eventService.createEvent(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新数据同步事件")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:update')")
|
||||
public CommonResult<Boolean> updateEvent(@Valid @RequestBody DatabusSyncEventSaveReqVO updateReqVO) {
|
||||
eventService.updateEvent(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除数据同步事件")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:delete')")
|
||||
public CommonResult<Boolean> deleteEvent(@RequestParam("id") Long id) {
|
||||
eventService.deleteEvent(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得数据同步事件")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:query')")
|
||||
public CommonResult<DatabusSyncEventRespVO> getEvent(@RequestParam("id") Long id) {
|
||||
DatabusSyncEventDO event = eventService.getEvent(id);
|
||||
return success(DatabusSyncEventConvert.INSTANCE.convert(event));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得数据同步事件分页")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:query')")
|
||||
public CommonResult<PageResult<DatabusSyncEventRespVO>> getEventPage(@Valid DatabusSyncEventPageReqVO pageReqVO) {
|
||||
PageResult<DatabusSyncEventDO> pageResult = eventService.getEventPage(pageReqVO);
|
||||
return success(DatabusSyncEventConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得数据同步事件列表")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:query')")
|
||||
public CommonResult<List<DatabusSyncEventRespVO>> getEventList() {
|
||||
List<DatabusSyncEventDO> list = eventService.getEventList();
|
||||
return success(DatabusSyncEventConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "修改事件启用状态")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:event:update')")
|
||||
public CommonResult<Boolean> updateEventStatus(
|
||||
@RequestParam("id") Long id,
|
||||
@RequestParam("enabled") Integer enabled) {
|
||||
eventService.updateEventStatus(id, enabled);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskCreateReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskRespVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncFullTaskConvert;
|
||||
import com.zt.plat.framework.databus.server.core.sync.DatabusFullSyncService;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncFullTaskMapper;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 管理后台 - 数据全量同步任务
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Tag(name = "管理后台 - 全量同步任务")
|
||||
@RestController
|
||||
@RequestMapping("/databus/sync/full-task")
|
||||
@Validated
|
||||
public class DatabusSyncFullTaskController {
|
||||
|
||||
@Resource
|
||||
private DatabusFullSyncService fullSyncService;
|
||||
|
||||
@Resource
|
||||
private DatabusSyncFullTaskMapper fullTaskMapper;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建全量同步任务")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:create')")
|
||||
public CommonResult<Long> createFullTask(@Valid @RequestBody DatabusSyncFullTaskCreateReqVO createReqVO) {
|
||||
Long taskId = fullSyncService.createFullSyncTask(createReqVO.getSubscriptionId(), createReqVO.getRemark());
|
||||
return success(taskId);
|
||||
}
|
||||
|
||||
@PostMapping("/execute")
|
||||
@Operation(summary = "执行全量同步任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:execute')")
|
||||
public CommonResult<Boolean> executeFullTask(@RequestParam("id") Long id) {
|
||||
fullSyncService.executeFullSyncTask(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/create-and-execute")
|
||||
@Operation(summary = "创建并执行全量同步任务")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:create')")
|
||||
public CommonResult<Long> createAndExecuteFullTask(@Valid @RequestBody DatabusSyncFullTaskCreateReqVO createReqVO) {
|
||||
Long taskId = fullSyncService.createFullSyncTask(createReqVO.getSubscriptionId(), createReqVO.getRemark());
|
||||
// 异步执行任务
|
||||
fullSyncService.executeFullSyncTask(taskId);
|
||||
return success(taskId);
|
||||
}
|
||||
|
||||
@PostMapping("/cancel")
|
||||
@Operation(summary = "取消全量同步任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:cancel')")
|
||||
public CommonResult<Boolean> cancelFullTask(@RequestParam("id") Long id) {
|
||||
fullSyncService.cancelFullSyncTask(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得全量同步任务详情")
|
||||
@Parameter(name = "id", description = "任务ID", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:query')")
|
||||
public CommonResult<DatabusSyncFullTaskRespVO> getFullTask(@RequestParam("id") Long id) {
|
||||
DatabusSyncFullTaskDO task = fullSyncService.getTaskById(id);
|
||||
return success(DatabusSyncFullTaskConvert.INSTANCE.convert(task));
|
||||
}
|
||||
|
||||
@GetMapping("/get-by-task-no")
|
||||
@Operation(summary = "根据任务编号获得全量同步任务详情")
|
||||
@Parameter(name = "taskNo", description = "任务编号", required = true, example = "abc123")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:query')")
|
||||
public CommonResult<DatabusSyncFullTaskRespVO> getFullTaskByTaskNo(@RequestParam("taskNo") String taskNo) {
|
||||
DatabusSyncFullTaskDO task = fullSyncService.getTaskByTaskNo(taskNo);
|
||||
return success(DatabusSyncFullTaskConvert.INSTANCE.convert(task));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得全量同步任务分页")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:full-task:query')")
|
||||
public CommonResult<PageResult<DatabusSyncFullTaskRespVO>> getFullTaskPage(@Valid DatabusSyncFullTaskPageReqVO pageReqVO) {
|
||||
PageResult<DatabusSyncFullTaskDO> pageResult = fullTaskMapper.selectPage(pageReqVO);
|
||||
return success(DatabusSyncFullTaskConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogRespVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncPushLogConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventRecordDO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncEventRecordMapper;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncLogService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 数据同步推送日志")
|
||||
@RestController
|
||||
@RequestMapping("/databus/sync/push-log")
|
||||
@Validated
|
||||
public class DatabusSyncPushLogController {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncLogService pushLogService;
|
||||
|
||||
@Resource
|
||||
private DatabusSyncEventRecordMapper eventRecordMapper;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得数据同步推送日志")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:push-log:query')")
|
||||
public CommonResult<DatabusSyncPushLogRespVO> getPushLog(@RequestParam("id") Long id) {
|
||||
DatabusSyncLogDO pushLog = pushLogService.getPushLog(id);
|
||||
DatabusSyncPushLogRespVO respVO = DatabusSyncPushLogConvert.INSTANCE.convert(pushLog);
|
||||
// 关联查询事件记录,获取消息内容
|
||||
if (pushLog != null && pushLog.getEventRecordId() != null) {
|
||||
DatabusSyncEventRecordDO eventRecord = eventRecordMapper.selectById(pushLog.getEventRecordId());
|
||||
if (eventRecord != null) {
|
||||
respVO.setDataSnapshot(eventRecord.getDataSnapshot());
|
||||
}
|
||||
}
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得数据同步推送日志分页")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:push-log:query')")
|
||||
public CommonResult<PageResult<DatabusSyncPushLogRespVO>> getPushLogPage(@Valid DatabusSyncPushLogPageReqVO pageReqVO) {
|
||||
PageResult<DatabusSyncLogDO> pageResult = pushLogService.getPushLogPage(pageReqVO);
|
||||
return success(DatabusSyncPushLogConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@PostMapping("/retry")
|
||||
@Operation(summary = "重试推送")
|
||||
@Parameter(name = "id", description = "日志ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:push-log:retry')")
|
||||
public CommonResult<Boolean> retryPush(@RequestParam("id") Long id) {
|
||||
pushLogService.retryPush(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionRespVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncSubscriptionConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncSubscriptionService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 数据同步订阅")
|
||||
@RestController
|
||||
@RequestMapping("/databus/sync/subscription")
|
||||
@Validated
|
||||
public class DatabusSyncSubscriptionController {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncSubscriptionService subscriptionService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据同步订阅")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:create')")
|
||||
public CommonResult<Long> createSubscription(@Valid @RequestBody DatabusSyncSubscriptionSaveReqVO createReqVO) {
|
||||
return success(subscriptionService.createSubscription(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新数据同步订阅")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:update')")
|
||||
public CommonResult<Boolean> updateSubscription(@Valid @RequestBody DatabusSyncSubscriptionSaveReqVO updateReqVO) {
|
||||
subscriptionService.updateSubscription(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除数据同步订阅")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:delete')")
|
||||
public CommonResult<Boolean> deleteSubscription(@RequestParam("id") Long id) {
|
||||
subscriptionService.deleteSubscription(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得数据同步订阅")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:query')")
|
||||
public CommonResult<DatabusSyncSubscriptionRespVO> getSubscription(@RequestParam("id") Long id) {
|
||||
DatabusSyncSubscriptionDO subscription = subscriptionService.getSubscription(id);
|
||||
return success(DatabusSyncSubscriptionConvert.INSTANCE.convert(subscription));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得数据同步订阅分页")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:query')")
|
||||
public CommonResult<PageResult<DatabusSyncSubscriptionRespVO>> getSubscriptionPage(@Valid DatabusSyncSubscriptionPageReqVO pageReqVO) {
|
||||
PageResult<DatabusSyncSubscriptionDO> pageResult = subscriptionService.getSubscriptionPage(pageReqVO);
|
||||
return success(DatabusSyncSubscriptionConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "修改订阅启用状态")
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:update')")
|
||||
public CommonResult<Boolean> updateSubscriptionStatus(
|
||||
@RequestParam("id") Long id,
|
||||
@RequestParam("enabled") Integer enabled) {
|
||||
subscriptionService.updateSubscriptionStatus(id, enabled);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/reset-checkpoint")
|
||||
@Operation(summary = "重置订阅断点")
|
||||
@Parameter(name = "id", description = "订阅ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:update')")
|
||||
public CommonResult<Boolean> resetCheckpoint(@RequestParam("id") Long id) {
|
||||
subscriptionService.resetCheckpoint(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/trigger-sync")
|
||||
@Operation(summary = "手动触发同步")
|
||||
@Parameter(name = "id", description = "订阅ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('databus:sync:subscription:trigger')")
|
||||
public CommonResult<Boolean> triggerSync(@RequestParam("id") Long id) {
|
||||
subscriptionService.triggerSync(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.client;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步客户端分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DatabusSyncClientPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "客户端编码", example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "客户端名称", example = "A分公司")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", example = "1")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "通信方式(MQ_FIRST-MQ优先 HTTP_ONLY-仅HTTP)", example = "MQ_FIRST")
|
||||
private String transportType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.client;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步客户端 Response VO")
|
||||
@Data
|
||||
public class DatabusSyncClientRespVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "租户ID", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "客户端编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "客户端名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "A分公司")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "通信方式(MQ_FIRST-MQ优先 HTTP_ONLY-仅HTTP)", requiredMode = Schema.RequiredMode.REQUIRED, example = "MQ_FIRST")
|
||||
private String transportType;
|
||||
|
||||
@Schema(description = "MQ是否启用(0-否 1-是)", example = "1")
|
||||
private Integer mqEnabled;
|
||||
|
||||
@Schema(description = "MQ NameServer地址", example = "localhost:9876")
|
||||
private String mqNamesrvAddr;
|
||||
|
||||
@Schema(description = "MQ Topic基础名称", example = "databus-sync")
|
||||
private String mqTopicBase;
|
||||
|
||||
@Schema(description = "HTTP是否启用(0-否 1-是)", example = "1")
|
||||
private Integer httpEnabled;
|
||||
|
||||
@Schema(description = "HTTP推送端点", example = "http://example.com/databus/sync/receive")
|
||||
private String httpEndpoint;
|
||||
|
||||
@Schema(description = "应用Key", example = "app-key-001")
|
||||
private String appKey;
|
||||
|
||||
@Schema(description = "应用Secret", example = "***")
|
||||
private String appSecret;
|
||||
|
||||
@Schema(description = "创建者", example = "admin")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新者", example = "admin")
|
||||
private String updater;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.client;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步客户端创建/修改 Request VO")
|
||||
@Data
|
||||
public class DatabusSyncClientSaveReqVO {
|
||||
|
||||
@Schema(description = "主键ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "客户端编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "company-a")
|
||||
@NotBlank(message = "客户端编码不能为空")
|
||||
@Pattern(regexp = "^[a-z0-9-]+$", message = "客户端编码只能包含小写字母、数字和短横线")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "客户端名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "A分公司")
|
||||
@NotBlank(message = "客户端名称不能为空")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "启用状态不能为空")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "通信方式(MQ_FIRST-MQ优先 HTTP_ONLY-仅HTTP)", requiredMode = Schema.RequiredMode.REQUIRED, example = "MQ_FIRST")
|
||||
@NotBlank(message = "通信方式不能为空")
|
||||
private String transportType;
|
||||
|
||||
@Schema(description = "MQ是否启用(0-否 1-是)", example = "1")
|
||||
private Integer mqEnabled;
|
||||
|
||||
@Schema(description = "MQ NameServer地址", example = "localhost:9876")
|
||||
private String mqNamesrvAddr;
|
||||
|
||||
@Schema(description = "MQ Topic基础名称", example = "databus-sync")
|
||||
private String mqTopicBase;
|
||||
|
||||
@Schema(description = "HTTP是否启用(0-否 1-是)", example = "1")
|
||||
private Integer httpEnabled;
|
||||
|
||||
@Schema(description = "HTTP推送端点", example = "http://example.com/databus/sync/receive")
|
||||
private String httpEndpoint;
|
||||
|
||||
@Schema(description = "应用Key", example = "app-key-001")
|
||||
private String appKey;
|
||||
|
||||
@Schema(description = "应用Secret(加密存储)", example = "secret")
|
||||
private String appSecret;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.deadletter;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步死信队列分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DatabusSyncDeadLetterPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "同步ID", example = "sync-20231124-001")
|
||||
private String syncId;
|
||||
|
||||
@Schema(description = "客户端编码", example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "事件类型", example = "user-changed")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "状态(PENDING-待处理 REDELIVERED-已重新投递 IGNORED-已忽略)", example = "PENDING")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "是否已处理(0-否 1-是)", example = "0")
|
||||
private Integer handled;
|
||||
|
||||
@Schema(description = "创建时间开始", example = "2023-11-24 00:00:00")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime createTimeStart;
|
||||
|
||||
@Schema(description = "创建时间结束", example = "2023-11-24 23:59:59")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime createTimeEnd;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.deadletter;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步死信队列 Response VO")
|
||||
@Data
|
||||
public class DatabusSyncDeadLetterRespVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "同步ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "sync-20231124-001")
|
||||
private String syncId;
|
||||
|
||||
@Schema(description = "同步日志ID", example = "10001")
|
||||
private Long syncLogId;
|
||||
|
||||
@Schema(description = "客户端编码", example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "事件类型", example = "user-changed")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "消息体(JSON)", example = "{\"userId\":1001,\"userName\":\"张三\"}")
|
||||
private String messageBody;
|
||||
|
||||
@Schema(description = "状态(PENDING-待处理 REDELIVERED-已重新投递 IGNORED-已忽略)", example = "PENDING")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "失败原因", example = "连接超时")
|
||||
private String lastErrorMessage;
|
||||
|
||||
@Schema(description = "失败时间")
|
||||
private LocalDateTime lastErrorTime;
|
||||
|
||||
@Schema(description = "重试次数", example = "5")
|
||||
private Integer retryCount;
|
||||
|
||||
@Schema(description = "是否已处理(0-否 1-是)", example = "0")
|
||||
private Integer handled;
|
||||
|
||||
@Schema(description = "处理时间")
|
||||
private LocalDateTime handleTime;
|
||||
|
||||
@Schema(description = "处理人", example = "admin")
|
||||
private String handler;
|
||||
|
||||
@Schema(description = "处理备注", example = "已通知开发人员修复")
|
||||
private String handleRemark;
|
||||
|
||||
@Schema(description = "租户ID", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.event;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步事件分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DatabusSyncEventPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "事件类型编码", example = "user-changed")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "事件名称", example = "用户信息变更")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", example = "1")
|
||||
private Integer enabled;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.event;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步事件 Response VO")
|
||||
@Data
|
||||
public class DatabusSyncEventRespVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "租户ID", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "事件类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "system-user-full")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "事件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户信息变更")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "事件描述", example = "用户基本信息变更事件")
|
||||
private String eventDesc;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "是否支持全量同步(0-否 1-是)", example = "1")
|
||||
private Integer supportFullSync;
|
||||
|
||||
@Schema(description = "是否支持增量同步(0-否 1-是)", example = "1")
|
||||
private Integer supportIncrementalSync;
|
||||
|
||||
@Schema(description = "数据结构版本", example = "1")
|
||||
private Integer dataVersion;
|
||||
|
||||
@Schema(description = "数据提供者类型(全量同步使用)", example = "ORG")
|
||||
private String dataProviderMethod;
|
||||
|
||||
@Schema(description = "创建者", example = "admin")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新者", example = "admin")
|
||||
private String updater;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.event;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步事件创建/修改 Request VO")
|
||||
@Data
|
||||
public class DatabusSyncEventSaveReqVO {
|
||||
|
||||
@Schema(description = "主键ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "事件类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "system-user-full")
|
||||
@NotBlank(message = "事件类型编码不能为空")
|
||||
@Pattern(regexp = "^[a-z0-9-]+$", message = "事件类型编码只能包含小写字母、数字和短横线")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "事件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户信息变更")
|
||||
@NotBlank(message = "事件名称不能为空")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "事件描述", example = "用户基本信息变更事件")
|
||||
private String eventDesc;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "启用状态不能为空")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "数据提供者类型(全量同步使用)", example = "ORG")
|
||||
private String dataProviderMethod;
|
||||
|
||||
@Schema(description = "是否支持全量同步(0-否 1-是)", example = "1")
|
||||
private Integer supportFullSync;
|
||||
|
||||
@Schema(description = "是否支持增量同步(0-否 1-是)", example = "1")
|
||||
private Integer supportIncrementalSync;
|
||||
|
||||
@Schema(description = "数据结构版本", example = "1")
|
||||
private Integer dataVersion;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.fulltask;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 全量同步任务创建 Request VO")
|
||||
@Data
|
||||
public class DatabusSyncFullTaskCreateReqVO {
|
||||
|
||||
@Schema(description = "订阅关系ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "订阅关系ID不能为空")
|
||||
private Long subscriptionId;
|
||||
|
||||
@Schema(description = "备注", example = "手动触发全量同步")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.fulltask;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 全量同步任务分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DatabusSyncFullTaskPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "任务编号", example = "abc123")
|
||||
private String taskNo;
|
||||
|
||||
@Schema(description = "订阅关系ID", example = "1")
|
||||
private Long subscriptionId;
|
||||
|
||||
@Schema(description = "客户端编码", example = "client-01")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "事件类型", example = "ORG_SYNC")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "任务状态(0-待执行 1-执行中 2-已完成 3-失败 4-已取消)", example = "2")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.fulltask;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 全量同步任务 Response VO")
|
||||
@Data
|
||||
public class DatabusSyncFullTaskRespVO {
|
||||
|
||||
@Schema(description = "主键ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "任务编号", example = "abc123")
|
||||
private String taskNo;
|
||||
|
||||
@Schema(description = "订阅关系ID", example = "1")
|
||||
private Long subscriptionId;
|
||||
|
||||
@Schema(description = "客户端编码", example = "client-01")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "事件类型", example = "ORG_SYNC")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "任务状态(0-待执行 1-执行中 2-已完成 3-失败 4-已取消)", example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "总数据量", example = "1000")
|
||||
private Long totalCount;
|
||||
|
||||
@Schema(description = "已处理数据量", example = "500")
|
||||
private Long processedCount;
|
||||
|
||||
@Schema(description = "成功数据量", example = "480")
|
||||
private Long successCount;
|
||||
|
||||
@Schema(description = "失败数据量", example = "20")
|
||||
private Long failCount;
|
||||
|
||||
@Schema(description = "总批次数", example = "10")
|
||||
private Integer totalBatch;
|
||||
|
||||
@Schema(description = "当前批次号", example = "5")
|
||||
private Integer currentBatch;
|
||||
|
||||
@Schema(description = "批量大小", example = "100")
|
||||
private Integer batchSize;
|
||||
|
||||
@Schema(description = "任务开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "任务结束时间")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "最后一次错误信息", example = "网络超时")
|
||||
private String lastErrorMessage;
|
||||
|
||||
@Schema(description = "租户ID", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "备注", example = "手动触发全量同步")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(description = "进度百分比", example = "50.0")
|
||||
private Double progressPercent;
|
||||
|
||||
/**
|
||||
* 计算进度百分比
|
||||
*/
|
||||
public Double getProgressPercent() {
|
||||
if (totalCount == null || totalCount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (processedCount == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return Math.round(processedCount * 10000.0 / totalCount) / 100.0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.pushlog;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步推送日志分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DatabusSyncPushLogPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "同步ID", example = "sync-20231124-001")
|
||||
private String syncId;
|
||||
|
||||
@Schema(description = "订阅关系ID", example = "1")
|
||||
private Long subscriptionId;
|
||||
|
||||
@Schema(description = "客户端编码", example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "事件类型", example = "user-changed")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "状态(PENDING-待处理 SUCCESS-成功 FAILED-失败 RETRYING-重试中)", example = "SUCCESS")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "传输方式(MQ/HTTP)", example = "MQ")
|
||||
private String transportType;
|
||||
|
||||
@Schema(description = "创建时间开始", example = "2023-11-24 00:00:00")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime createTimeStart;
|
||||
|
||||
@Schema(description = "创建时间结束", example = "2023-11-24 23:59:59")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime createTimeEnd;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.pushlog;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步推送日志 Response VO")
|
||||
@Data
|
||||
public class DatabusSyncPushLogRespVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "同步ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "sync-20231124-001")
|
||||
private String syncId;
|
||||
|
||||
@Schema(description = "事件记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10001")
|
||||
private Long eventRecordId;
|
||||
|
||||
@Schema(description = "订阅关系ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long subscriptionId;
|
||||
|
||||
@Schema(description = "客户端编码", example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "事件类型", example = "user-changed")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "同步模式(FULL-全量 INCREMENTAL-增量)", example = "INCREMENTAL")
|
||||
private String syncMode;
|
||||
|
||||
@Schema(description = "传输方式(MQ/HTTP)", requiredMode = Schema.RequiredMode.REQUIRED, example = "MQ")
|
||||
private String transportType;
|
||||
|
||||
@Schema(description = "MQ Topic", example = "databus-sync-user-changed-company-a")
|
||||
private String mqTopic;
|
||||
|
||||
@Schema(description = "MQ消息ID", example = "C0A8012300002A9F0000000000000001")
|
||||
private String mqMsgId;
|
||||
|
||||
@Schema(description = "状态(PENDING-待处理 SUCCESS-成功 FAILED-失败 RETRYING-重试中)", requiredMode = Schema.RequiredMode.REQUIRED, example = "SUCCESS")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "重试次数", example = "0")
|
||||
private Integer retryCount;
|
||||
|
||||
@Schema(description = "错误信息", example = "连接超时")
|
||||
private String errorMessage;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "结束时间")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "耗时(毫秒)", example = "150")
|
||||
private Integer duration;
|
||||
|
||||
@Schema(description = "租户ID", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(description = "推送消息内容(JSON)", example = "{\"id\":1,\"name\":\"张三\"}")
|
||||
private String dataSnapshot;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.subscription;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步订阅分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DatabusSyncSubscriptionPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "客户端ID", example = "1")
|
||||
private Long clientId;
|
||||
|
||||
@Schema(description = "事件ID", example = "1")
|
||||
private Long eventId;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", example = "1")
|
||||
private Integer enabled;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.subscription;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步订阅 Response VO")
|
||||
@Data
|
||||
public class DatabusSyncSubscriptionRespVO {
|
||||
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "租户ID", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "客户端ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long clientId;
|
||||
|
||||
@Schema(description = "客户端编码(关联查询)", example = "company-a")
|
||||
private String clientCode;
|
||||
|
||||
@Schema(description = "客户端名称(关联查询)", example = "A分公司")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "事件ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long eventId;
|
||||
|
||||
@Schema(description = "事件类型(关联查询)", example = "user-changed")
|
||||
private String eventType;
|
||||
|
||||
@Schema(description = "事件名称(关联查询)", example = "用户信息变更")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "同步模式(FULL-全量 INCREMENTAL-增量)", example = "INCREMENTAL")
|
||||
private String syncMode;
|
||||
|
||||
@Schema(description = "时效性(REALTIME-实时 NEAR_REALTIME-准实时 BATCH-批量)", example = "NEAR_REALTIME")
|
||||
private String timeliness;
|
||||
|
||||
@Schema(description = "时效值(秒)", example = "60")
|
||||
private Integer timelinessValue;
|
||||
|
||||
@Schema(description = "批量大小", example = "500")
|
||||
private Integer batchSize;
|
||||
|
||||
@Schema(description = "最后同步的事件记录ID", example = "10001")
|
||||
private Long lastSyncEventId;
|
||||
|
||||
@Schema(description = "最后同步时间")
|
||||
private LocalDateTime lastSyncTime;
|
||||
|
||||
@Schema(description = "总同步次数", example = "100")
|
||||
private Long totalSyncCount;
|
||||
|
||||
@Schema(description = "成功次数", example = "95")
|
||||
private Long totalSuccessCount;
|
||||
|
||||
@Schema(description = "失败次数", example = "5")
|
||||
private Long totalFailCount;
|
||||
|
||||
@Schema(description = "创建者", example = "admin")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新者", example = "admin")
|
||||
private String updater;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.zt.plat.framework.databus.server.controller.admin.vo.subscription;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 数据同步订阅创建/修改 Request VO")
|
||||
@Data
|
||||
public class DatabusSyncSubscriptionSaveReqVO {
|
||||
|
||||
@Schema(description = "主键ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "客户端ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "客户端ID不能为空")
|
||||
private Long clientId;
|
||||
|
||||
@Schema(description = "事件ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "事件ID不能为空")
|
||||
private Long eventId;
|
||||
|
||||
@Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "启用状态不能为空")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "同步模式(FULL-全量 INCREMENTAL-增量)", example = "INCREMENTAL")
|
||||
private String syncMode;
|
||||
|
||||
@Schema(description = "时效性(REALTIME-实时 NEAR_REALTIME-准实时 BATCH-批量)", example = "NEAR_REALTIME")
|
||||
private String timeliness;
|
||||
|
||||
@Schema(description = "时效值(秒)", example = "60")
|
||||
private Integer timelinessValue;
|
||||
|
||||
@Schema(description = "批量大小", example = "500")
|
||||
private Integer batchSize;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zt.plat.framework.databus.server.convert;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientRespVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DatabusSyncClientConvert {
|
||||
|
||||
DatabusSyncClientConvert INSTANCE = Mappers.getMapper(DatabusSyncClientConvert.class);
|
||||
|
||||
DatabusSyncClientDO convert(DatabusSyncClientSaveReqVO bean);
|
||||
|
||||
DatabusSyncClientRespVO convert(DatabusSyncClientDO bean);
|
||||
|
||||
List<DatabusSyncClientRespVO> convertList(List<DatabusSyncClientDO> list);
|
||||
|
||||
PageResult<DatabusSyncClientRespVO> convertPage(PageResult<DatabusSyncClientDO> page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zt.plat.framework.databus.server.convert;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterRespVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DatabusSyncDeadLetterConvert {
|
||||
|
||||
DatabusSyncDeadLetterConvert INSTANCE = Mappers.getMapper(DatabusSyncDeadLetterConvert.class);
|
||||
|
||||
DatabusSyncDeadLetterRespVO convert(DatabusSyncDeadLetterDO bean);
|
||||
|
||||
List<DatabusSyncDeadLetterRespVO> convertList(List<DatabusSyncDeadLetterDO> list);
|
||||
|
||||
PageResult<DatabusSyncDeadLetterRespVO> convertPage(PageResult<DatabusSyncDeadLetterDO> page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zt.plat.framework.databus.server.convert;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventRespVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DatabusSyncEventConvert {
|
||||
|
||||
DatabusSyncEventConvert INSTANCE = Mappers.getMapper(DatabusSyncEventConvert.class);
|
||||
|
||||
DatabusSyncEventDO convert(DatabusSyncEventSaveReqVO bean);
|
||||
|
||||
DatabusSyncEventRespVO convert(DatabusSyncEventDO bean);
|
||||
|
||||
List<DatabusSyncEventRespVO> convertList(List<DatabusSyncEventDO> list);
|
||||
|
||||
PageResult<DatabusSyncEventRespVO> convertPage(PageResult<DatabusSyncEventDO> page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zt.plat.framework.databus.server.convert;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskRespVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DatabusSyncFullTaskConvert {
|
||||
|
||||
DatabusSyncFullTaskConvert INSTANCE = Mappers.getMapper(DatabusSyncFullTaskConvert.class);
|
||||
|
||||
DatabusSyncFullTaskRespVO convert(DatabusSyncFullTaskDO bean);
|
||||
|
||||
List<DatabusSyncFullTaskRespVO> convertList(List<DatabusSyncFullTaskDO> list);
|
||||
|
||||
PageResult<DatabusSyncFullTaskRespVO> convertPage(PageResult<DatabusSyncFullTaskDO> page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zt.plat.framework.databus.server.convert;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogRespVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DatabusSyncPushLogConvert {
|
||||
|
||||
DatabusSyncPushLogConvert INSTANCE = Mappers.getMapper(DatabusSyncPushLogConvert.class);
|
||||
|
||||
DatabusSyncPushLogRespVO convert(DatabusSyncLogDO bean);
|
||||
|
||||
List<DatabusSyncPushLogRespVO> convertList(List<DatabusSyncLogDO> list);
|
||||
|
||||
PageResult<DatabusSyncPushLogRespVO> convertPage(PageResult<DatabusSyncLogDO> page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zt.plat.framework.databus.server.convert;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionRespVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DatabusSyncSubscriptionConvert {
|
||||
|
||||
DatabusSyncSubscriptionConvert INSTANCE = Mappers.getMapper(DatabusSyncSubscriptionConvert.class);
|
||||
|
||||
DatabusSyncSubscriptionDO convert(DatabusSyncSubscriptionSaveReqVO bean);
|
||||
|
||||
DatabusSyncSubscriptionRespVO convert(DatabusSyncSubscriptionDO bean);
|
||||
|
||||
List<DatabusSyncSubscriptionRespVO> convertList(List<DatabusSyncSubscriptionDO> list);
|
||||
|
||||
PageResult<DatabusSyncSubscriptionRespVO> convertPage(PageResult<DatabusSyncSubscriptionDO> page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.zt.plat.framework.databus.server.core.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Databus 事件对象
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusEvent {
|
||||
|
||||
/**
|
||||
* 事件类型编码
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件动作(create-创建 update-更新 delete-删除)
|
||||
*/
|
||||
private String eventAction;
|
||||
|
||||
/**
|
||||
* 完整业务数据快照(JSON格式)
|
||||
*/
|
||||
private String dataSnapshot;
|
||||
|
||||
/**
|
||||
* 数据版本号
|
||||
*/
|
||||
private Integer dataVersion;
|
||||
|
||||
/**
|
||||
* 来源服务名
|
||||
*/
|
||||
private String sourceService;
|
||||
|
||||
/**
|
||||
* 来源MQ Topic
|
||||
*/
|
||||
private String sourceTopic;
|
||||
|
||||
/**
|
||||
* 来源MQ消息ID
|
||||
*/
|
||||
private String sourceMsgId;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 操作人
|
||||
*/
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 事件发生时间
|
||||
*/
|
||||
private LocalDateTime eventTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.zt.plat.framework.databus.server.core.message;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量数据同步消息
|
||||
* <p>
|
||||
* 用于全量同步和批量增量同步,一条消息包含多条数据
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BatchSyncMessage {
|
||||
|
||||
/**
|
||||
* 消息ID(唯一标识)
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 请求ID(用于追踪)
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 同步模式(1-全量 2-增量)
|
||||
*/
|
||||
private Integer syncMode;
|
||||
|
||||
/**
|
||||
* 数据结构版本
|
||||
*/
|
||||
private Integer dataVersion;
|
||||
|
||||
/**
|
||||
* 消息时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
*/
|
||||
private List<SyncDataItem> dataList;
|
||||
|
||||
/**
|
||||
* 本批次数据条数
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
// ========== 全量同步相关字段 ==========
|
||||
|
||||
/**
|
||||
* 全量任务ID
|
||||
*/
|
||||
private Long fullTaskId;
|
||||
|
||||
/**
|
||||
* 当前批次号
|
||||
*/
|
||||
private Integer batchNo;
|
||||
|
||||
/**
|
||||
* 总批次数
|
||||
*/
|
||||
private Integer totalBatch;
|
||||
|
||||
/**
|
||||
* 是否最后一批
|
||||
*/
|
||||
private Boolean isLastBatch;
|
||||
|
||||
/**
|
||||
* 总数据量
|
||||
*/
|
||||
private Long totalCount;
|
||||
|
||||
/**
|
||||
* 同步数据项
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class SyncDataItem {
|
||||
|
||||
/**
|
||||
* 操作类型:CREATE/UPDATE/DELETE
|
||||
*/
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 业务数据ID
|
||||
*/
|
||||
private Long uid;
|
||||
|
||||
/**
|
||||
* 业务数据快照(JSON)
|
||||
*/
|
||||
private String data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.zt.plat.framework.databus.server.core.message;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据同步消息
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SyncMessage {
|
||||
|
||||
/**
|
||||
* 同步ID(唯一标识)
|
||||
*/
|
||||
private String syncId;
|
||||
|
||||
/**
|
||||
* 事件记录ID
|
||||
*/
|
||||
private Long eventRecordId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件动作
|
||||
*/
|
||||
private String eventAction;
|
||||
|
||||
/**
|
||||
* 业务数据快照(JSON)
|
||||
*/
|
||||
private String dataSnapshot;
|
||||
|
||||
/**
|
||||
* 数据版本
|
||||
*/
|
||||
private Integer dataVersion;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.zt.plat.framework.databus.server.core.provider;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据提供者接口
|
||||
* <p>
|
||||
* 用于全量同步时获取数据,由业务模块实现具体的数据获取逻辑
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DataProvider<T> {
|
||||
|
||||
/**
|
||||
* 获取数据提供者类型标识
|
||||
*
|
||||
* @return 类型标识,如 "ORG"、"USER"
|
||||
*/
|
||||
String getProviderType();
|
||||
|
||||
/**
|
||||
* 游标分页获取数据
|
||||
*
|
||||
* @param cursorTime 游标时间(首次查询传 null)
|
||||
* @param cursorId 游标ID(首次查询传 null)
|
||||
* @param batchSize 批量大小
|
||||
* @param tenantId 租户ID
|
||||
* @return 分页结果
|
||||
*/
|
||||
CursorPageData<T> getPageByCursor(LocalDateTime cursorTime, Long cursorId, int batchSize, Long tenantId);
|
||||
|
||||
/**
|
||||
* 统计总数
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 总数量
|
||||
*/
|
||||
long count(Long tenantId);
|
||||
|
||||
/**
|
||||
* 从数据对象中提取 UID
|
||||
*
|
||||
* @param data 数据对象
|
||||
* @return UID
|
||||
*/
|
||||
Long extractUid(T data);
|
||||
|
||||
/**
|
||||
* 游标分页数据结果
|
||||
*/
|
||||
class CursorPageData<T> {
|
||||
private List<T> list;
|
||||
private LocalDateTime nextCursorTime;
|
||||
private Long nextCursorId;
|
||||
private int count;
|
||||
private boolean hasMore;
|
||||
private long total;
|
||||
|
||||
public List<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public LocalDateTime getNextCursorTime() {
|
||||
return nextCursorTime;
|
||||
}
|
||||
|
||||
public void setNextCursorTime(LocalDateTime nextCursorTime) {
|
||||
this.nextCursorTime = nextCursorTime;
|
||||
}
|
||||
|
||||
public Long getNextCursorId() {
|
||||
return nextCursorId;
|
||||
}
|
||||
|
||||
public void setNextCursorId(Long nextCursorId) {
|
||||
this.nextCursorId = nextCursorId;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public boolean isHasMore() {
|
||||
return hasMore;
|
||||
}
|
||||
|
||||
public void setHasMore(boolean hasMore) {
|
||||
this.hasMore = hasMore;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public static <T> CursorPageData<T> of(List<T> list, LocalDateTime nextCursorTime, Long nextCursorId,
|
||||
int count, boolean hasMore, long total) {
|
||||
CursorPageData<T> data = new CursorPageData<>();
|
||||
data.setList(list);
|
||||
data.setNextCursorTime(nextCursorTime);
|
||||
data.setNextCursorId(nextCursorId);
|
||||
data.setCount(count);
|
||||
data.setHasMore(hasMore);
|
||||
data.setTotal(total);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.zt.plat.framework.databus.server.core.provider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 数据提供者注册中心
|
||||
* <p>
|
||||
* 管理所有注册的数据提供者,根据类型获取对应的提供者
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DataProviderRegistry {
|
||||
|
||||
private final Map<String, DataProvider<?>> providers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 注册数据提供者
|
||||
*
|
||||
* @param provider 数据提供者
|
||||
*/
|
||||
public void register(DataProvider<?> provider) {
|
||||
String type = provider.getProviderType();
|
||||
providers.put(type, provider);
|
||||
log.info("[Databus] 数据提供者已注册: type={}", type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据提供者
|
||||
*
|
||||
* @param type 类型标识
|
||||
* @return 数据提供者,不存在则返回 null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> DataProvider<T> getProvider(String type) {
|
||||
return (DataProvider<T>) providers.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在指定类型的提供者
|
||||
*
|
||||
* @param type 类型标识
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean hasProvider(String type) {
|
||||
return providers.containsKey(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的提供者类型
|
||||
*
|
||||
* @return 类型列表
|
||||
*/
|
||||
public java.util.Set<String> getRegisteredTypes() {
|
||||
return providers.keySet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.framework.databus.server.core.publisher;
|
||||
|
||||
import com.zt.plat.framework.databus.server.core.event.DatabusEvent;
|
||||
|
||||
/**
|
||||
* Databus 事件发布器接口
|
||||
* 用于发布业务数据变更事件到同步流水表
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusEventPublisher {
|
||||
|
||||
/**
|
||||
* 发布事件(异步)
|
||||
*
|
||||
* @param event 事件对象
|
||||
*/
|
||||
void publish(DatabusEvent event);
|
||||
|
||||
/**
|
||||
* 发布事件(同步,等待入库完成)
|
||||
*
|
||||
* @param event 事件对象
|
||||
* @return 事件记录ID
|
||||
*/
|
||||
Long publishSync(DatabusEvent event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.zt.plat.framework.databus.server.core.publisher;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.zt.plat.framework.databus.server.core.event.DatabusEvent;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventRecordDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncEventRecordMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Databus 事件发布器实现
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DatabusEventPublisherImpl implements DatabusEventPublisher {
|
||||
|
||||
private final DatabusSyncEventRecordMapper eventRecordMapper;
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void publish(DatabusEvent event) {
|
||||
try {
|
||||
saveEventRecord(event);
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] 发布事件失败, eventType={}, eventAction={}",
|
||||
event.getEventType(), event.getEventAction(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long publishSync(DatabusEvent event) {
|
||||
return saveEventRecord(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存事件记录到流水表
|
||||
*/
|
||||
private Long saveEventRecord(DatabusEvent event) {
|
||||
// 查询事件定义ID(这里简化处理,实际应该注入EventMapper查询)
|
||||
// TODO: 根据 eventType 查询 event_id
|
||||
|
||||
DatabusSyncEventRecordDO record = new DatabusSyncEventRecordDO();
|
||||
BeanUtil.copyProperties(event, record);
|
||||
|
||||
eventRecordMapper.insert(record);
|
||||
|
||||
log.info("[Databus] 事件记录已保存, id={}, eventType={}, eventAction={}",
|
||||
record.getId(), event.getEventType(), event.getEventAction());
|
||||
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.zt.plat.framework.databus.server.core.pusher;
|
||||
|
||||
import com.zt.plat.framework.databus.server.core.message.BatchSyncMessage;
|
||||
import com.zt.plat.framework.databus.server.core.message.SyncMessage;
|
||||
|
||||
/**
|
||||
* 消息推送器接口
|
||||
* 支持 MQ 和 HTTP 两种推送方式
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface MessagePusher {
|
||||
|
||||
/**
|
||||
* 通过 MQ 推送消息
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param message 消息内容
|
||||
* @return 消息ID
|
||||
*/
|
||||
String pushByMQ(String topic, SyncMessage message);
|
||||
|
||||
/**
|
||||
* 通过 HTTP 推送消息
|
||||
*
|
||||
* @param endpoint HTTP端点
|
||||
* @param message 消息内容
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean pushByHttp(String endpoint, SyncMessage message);
|
||||
|
||||
/**
|
||||
* 通过 MQ 推送批量消息
|
||||
*
|
||||
* @param topic Topic
|
||||
* @param message 批量消息内容
|
||||
* @return 消息ID
|
||||
*/
|
||||
String pushBatchByMQ(String topic, BatchSyncMessage message);
|
||||
|
||||
/**
|
||||
* 通过 HTTP 推送批量消息
|
||||
*
|
||||
* @param endpoint HTTP端点
|
||||
* @param message 批量消息内容
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean pushBatchByHttp(String endpoint, BatchSyncMessage message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.zt.plat.framework.databus.server.core.pusher;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.databus.server.core.message.BatchSyncMessage;
|
||||
import com.zt.plat.framework.databus.server.core.message.SyncMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.client.producer.SendStatus;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
|
||||
/**
|
||||
* 消息推送器实现
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
public class MessagePusherImpl implements MessagePusher {
|
||||
|
||||
private RocketMQTemplate rocketMQTemplate;
|
||||
|
||||
public void setRocketMQTemplate(RocketMQTemplate rocketMQTemplate) {
|
||||
this.rocketMQTemplate = rocketMQTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pushByMQ(String topic, SyncMessage message) {
|
||||
if (rocketMQTemplate == null) {
|
||||
log.warn("[Databus] RocketMQTemplate未配置,无法推送MQ消息");
|
||||
throw new RuntimeException("RocketMQTemplate未配置");
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用 RocketMQ 发送消息
|
||||
SendResult sendResult = rocketMQTemplate.syncSend(topic, message);
|
||||
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
|
||||
log.error("[Databus] MQ推送失败, topic={}, syncId={}, status={}, msgId={}",
|
||||
topic, message.getSyncId(), sendResult.getSendStatus(), sendResult.getMsgId());
|
||||
throw new RuntimeException("MQ推送失败: " + sendResult.getSendStatus());
|
||||
}
|
||||
log.info("[Databus] MQ推送成功, topic={}, syncId={}, msgId={}",
|
||||
topic, message.getSyncId(), sendResult.getMsgId());
|
||||
return message.getSyncId();
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] MQ推送失败, topic={}, syncId={}", topic, message.getSyncId(), e);
|
||||
throw new RuntimeException("MQ推送失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushByHttp(String endpoint, SyncMessage message) {
|
||||
try {
|
||||
String jsonBody = JSONUtil.toJsonStr(message);
|
||||
String response = HttpUtil.post(endpoint, jsonBody);
|
||||
log.info("[Databus] HTTP推送成功, endpoint={}, syncId={}", endpoint, message.getSyncId());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] HTTP推送失败, endpoint={}, syncId={}", endpoint, message.getSyncId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pushBatchByMQ(String topic, BatchSyncMessage message) {
|
||||
if (rocketMQTemplate == null) {
|
||||
log.warn("[Databus] RocketMQTemplate未配置,无法推送MQ消息");
|
||||
throw new RuntimeException("RocketMQTemplate未配置");
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用 RocketMQ 发送批量消息
|
||||
SendResult sendResult = rocketMQTemplate.syncSend(topic, message);
|
||||
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
|
||||
log.error("[Databus] 批量MQ推送失败, topic={}, messageId={}, status={}, mqMsgId={}",
|
||||
topic, message.getMessageId(), sendResult.getSendStatus(), sendResult.getMsgId());
|
||||
throw new RuntimeException("批量MQ推送失败: " + sendResult.getSendStatus());
|
||||
}
|
||||
log.info("[Databus] 批量MQ推送成功, topic={}, messageId={}, count={}, mqMsgId={}",
|
||||
topic, message.getMessageId(), message.getCount(), sendResult.getMsgId());
|
||||
return message.getMessageId();
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] 批量MQ推送失败, topic={}, messageId={}",
|
||||
topic, message.getMessageId(), e);
|
||||
throw new RuntimeException("批量MQ推送失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushBatchByHttp(String endpoint, BatchSyncMessage message) {
|
||||
try {
|
||||
String jsonBody = JSONUtil.toJsonStr(message);
|
||||
String response = HttpUtil.post(endpoint, jsonBody);
|
||||
log.info("[Databus] 批量HTTP推送成功, endpoint={}, messageId={}, count={}",
|
||||
endpoint, message.getMessageId(), message.getCount());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] 批量HTTP推送失败, endpoint={}, messageId={}",
|
||||
endpoint, message.getMessageId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zt.plat.framework.databus.server.core.sync;
|
||||
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO;
|
||||
|
||||
/**
|
||||
* Databus 全量同步服务接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusFullSyncService {
|
||||
|
||||
/**
|
||||
* 创建全量同步任务
|
||||
*
|
||||
* @param subscriptionId 订阅关系ID
|
||||
* @param remark 备注
|
||||
* @return 任务ID
|
||||
*/
|
||||
Long createFullSyncTask(Long subscriptionId, String remark);
|
||||
|
||||
/**
|
||||
* 执行全量同步任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
*/
|
||||
void executeFullSyncTask(Long taskId);
|
||||
|
||||
/**
|
||||
* 取消全量同步任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
*/
|
||||
void cancelFullSyncTask(Long taskId);
|
||||
|
||||
/**
|
||||
* 获取任务详情
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 任务详情
|
||||
*/
|
||||
DatabusSyncFullTaskDO getTaskById(Long taskId);
|
||||
|
||||
/**
|
||||
* 根据任务编号获取任务详情
|
||||
*
|
||||
* @param taskNo 任务编号
|
||||
* @return 任务详情
|
||||
*/
|
||||
DatabusSyncFullTaskDO getTaskByTaskNo(String taskNo);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zt.plat.framework.databus.server.core.sync;
|
||||
|
||||
import com.zt.plat.framework.databus.server.core.event.DatabusEvent;
|
||||
|
||||
/**
|
||||
* Databus 增量同步服务接口
|
||||
* 用于实时处理业务MQ消息,进行三态判断、记录流水、推送到客户端Topic
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusIncrementalSyncService {
|
||||
|
||||
/**
|
||||
* 处理增量同步事件
|
||||
* <p>
|
||||
* 核心流程:
|
||||
* 1. 根据 eventType 查询事件定义,判断是否启用
|
||||
* 2. 查询所有启用的订阅关系
|
||||
* 3. 对每个订阅进行三态判断(事件/客户端/订阅是否启用)
|
||||
* 4. 记录到 databus_sync_event_record 流水表
|
||||
* 5. 推送到客户端专属 Topic(databus-sync-{eventType}-{clientCode})
|
||||
*
|
||||
* @param event 业务变更事件
|
||||
*/
|
||||
void processEvent(DatabusEvent event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package com.zt.plat.framework.databus.server.core.sync;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.zt.plat.framework.databus.server.core.event.DatabusEvent;
|
||||
import com.zt.plat.framework.databus.server.core.message.SyncMessage;
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusher;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.*;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.*;
|
||||
import com.zt.plat.framework.databus.server.enums.SyncStatusEnum;
|
||||
import com.zt.plat.framework.databus.server.enums.TransportTypeEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus 增量同步服务实现
|
||||
* <p>
|
||||
* 核心流程:
|
||||
* 1. 根据 eventType 查询事件定义,判断是否启用
|
||||
* 2. 记录到 event_record 流水表
|
||||
* 3. 查询所有启用的订阅关系
|
||||
* 4. 对每个订阅进行三态判断(事件/客户端/订阅是否启用)
|
||||
* 5. 推送到客户端专属 Topic(databus-sync-{eventType}-{clientCode})
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DatabusIncrementalSyncServiceImpl implements DatabusIncrementalSyncService {
|
||||
|
||||
private final DatabusSyncEventMapper eventMapper;
|
||||
private final DatabusSyncEventRecordMapper eventRecordMapper;
|
||||
private final DatabusSyncSubscriptionMapper subscriptionMapper;
|
||||
private final DatabusSyncClientMapper clientMapper;
|
||||
private final DatabusSyncLogMapper syncLogMapper;
|
||||
private final MessagePusher messagePusher;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void processEvent(DatabusEvent event) {
|
||||
log.info("[Databus增量同步] 开始处理事件, eventType={}, eventAction={}",
|
||||
event.getEventType(), event.getEventAction());
|
||||
|
||||
// 1. 查询事件定义
|
||||
DatabusSyncEventDO eventDef = eventMapper.selectByEventType(event.getEventType());
|
||||
if (eventDef == null) {
|
||||
log.warn("[Databus增量同步] 事件类型未定义, eventType={}", event.getEventType());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 判断事件是否启用(三态判断之一)
|
||||
if (eventDef.getEnabled() != 1) {
|
||||
log.debug("[Databus增量同步] 事件未启用, eventType={}", event.getEventType());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 判断事件是否支持增量同步
|
||||
if (eventDef.getSupportIncrementalSync() != 1) {
|
||||
log.debug("[Databus增量同步] 事件不支持增量同步, eventType={}", event.getEventType());
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 记录到 event_record 流水表
|
||||
DatabusSyncEventRecordDO eventRecord = saveEventRecord(event, eventDef);
|
||||
|
||||
// 5. 查询所有启用的订阅关系
|
||||
List<DatabusSyncSubscriptionDO> subscriptions = subscriptionMapper.selectEnabledByEventId(eventDef.getId());
|
||||
if (subscriptions.isEmpty()) {
|
||||
log.debug("[Databus增量同步] 无启用的订阅, eventType={}", event.getEventType());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[Databus增量同步] 找到{}个订阅需要推送, eventType={}",
|
||||
subscriptions.size(), event.getEventType());
|
||||
|
||||
// 6. 对每个订阅进行推送
|
||||
for (DatabusSyncSubscriptionDO subscription : subscriptions) {
|
||||
try {
|
||||
pushToSubscriber(subscription, eventDef, eventRecord);
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus增量同步] 推送到订阅者失败, subscriptionId={}, eventType={}",
|
||||
subscription.getId(), event.getEventType(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("[Databus增量同步] 事件处理完成, eventType={}, eventAction={}, recordId={}",
|
||||
event.getEventType(), event.getEventAction(), eventRecord.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存事件记录到流水表
|
||||
*/
|
||||
private DatabusSyncEventRecordDO saveEventRecord(DatabusEvent event, DatabusSyncEventDO eventDef) {
|
||||
DatabusSyncEventRecordDO record = new DatabusSyncEventRecordDO();
|
||||
BeanUtil.copyProperties(event, record);
|
||||
record.setEventId(eventDef.getId());
|
||||
|
||||
eventRecordMapper.insert(record);
|
||||
|
||||
log.info("[Databus增量同步] 事件记录已保存, recordId={}, eventType={}, eventAction={}",
|
||||
record.getId(), event.getEventType(), event.getEventAction());
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送到单个订阅者
|
||||
*/
|
||||
private void pushToSubscriber(DatabusSyncSubscriptionDO subscription,
|
||||
DatabusSyncEventDO eventDef,
|
||||
DatabusSyncEventRecordDO eventRecord) {
|
||||
// 查询客户端信息
|
||||
DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId());
|
||||
if (client == null) {
|
||||
log.warn("[Databus增量同步] 客户端不存在, clientId={}", subscription.getClientId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 三态判断之二:客户端是否启用
|
||||
if (client.getEnabled() != 1) {
|
||||
log.debug("[Databus增量同步] 客户端未启用, clientCode={}", client.getClientCode());
|
||||
return;
|
||||
}
|
||||
|
||||
// 三态判断之三:订阅是否启用(已在查询时过滤,这里再确认一次)
|
||||
if (subscription.getEnabled() != 1) {
|
||||
log.debug("[Databus增量同步] 订阅未启用, subscriptionId={}", subscription.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
String syncId = IdUtil.fastSimpleUUID();
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
|
||||
// 构建同步消息
|
||||
SyncMessage message = SyncMessage.builder()
|
||||
.syncId(syncId)
|
||||
.eventRecordId(eventRecord.getId())
|
||||
.eventType(eventRecord.getEventType())
|
||||
.eventAction(eventRecord.getEventAction())
|
||||
.dataSnapshot(eventRecord.getDataSnapshot())
|
||||
.dataVersion(eventRecord.getDataVersion())
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
// 构建同步日志
|
||||
DatabusSyncLogDO syncLog = DatabusSyncLogDO.builder()
|
||||
.syncId(syncId)
|
||||
.eventRecordId(eventRecord.getId())
|
||||
.subscriptionId(subscription.getId())
|
||||
.clientCode(client.getClientCode())
|
||||
.eventType(eventRecord.getEventType())
|
||||
.syncMode(2) // 增量同步
|
||||
.startTime(startTime)
|
||||
.status(SyncStatusEnum.PENDING.getStatus())
|
||||
.retryCount(0)
|
||||
.dataCount(1)
|
||||
.tenantId(client.getTenantId())
|
||||
.build();
|
||||
|
||||
try {
|
||||
// 根据客户端配置选择推送方式
|
||||
if (TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1) {
|
||||
// MQ 推送:Topic格式 = databus-sync-{eventType}-{clientCode}
|
||||
String topic = buildClientTopic(client.getMqTopicBase(), eventDef.getEventType(), client.getClientCode());
|
||||
String mqMsgId = messagePusher.pushByMQ(topic, message);
|
||||
|
||||
syncLog.setTransportType(TransportTypeEnum.MQ_FIRST.getType());
|
||||
syncLog.setMqTopic(topic);
|
||||
syncLog.setMqMsgId(mqMsgId);
|
||||
|
||||
log.info("[Databus增量同步] MQ推送成功, topic={}, syncId={}", topic, syncId);
|
||||
|
||||
} else if (client.getHttpEnabled() == 1) {
|
||||
// HTTP 推送
|
||||
boolean success = messagePusher.pushByHttp(client.getHttpEndpoint(), message);
|
||||
if (!success) {
|
||||
throw new RuntimeException("HTTP推送失败");
|
||||
}
|
||||
|
||||
syncLog.setTransportType(TransportTypeEnum.HTTP_ONLY.getType());
|
||||
|
||||
log.info("[Databus增量同步] HTTP推送成功, endpoint={}, syncId={}",
|
||||
client.getHttpEndpoint(), syncId);
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("无可用的推送方式");
|
||||
}
|
||||
|
||||
// 更新日志状态为成功
|
||||
syncLog.setStatus(SyncStatusEnum.SUCCESS.getStatus());
|
||||
syncLog.setEndTime(LocalDateTime.now());
|
||||
syncLog.setDuration((int) (System.currentTimeMillis() -
|
||||
startTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()));
|
||||
|
||||
// 更新订阅断点
|
||||
updateSubscriptionCheckpoint(subscription, eventRecord.getId());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus增量同步] 推送失败, syncId={}, clientCode={}", syncId, client.getClientCode(), e);
|
||||
|
||||
syncLog.setStatus(SyncStatusEnum.FAILED.getStatus());
|
||||
syncLog.setErrorMessage(e.getMessage());
|
||||
syncLog.setEndTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 保存同步日志
|
||||
syncLogMapper.insert(syncLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建客户端专属Topic
|
||||
* 格式: {topicBase}-{module}-{entity}-{action}-{clientCode}
|
||||
* 示例: databus-sync-system-org-create-company-a
|
||||
*
|
||||
* @param topicBase 基础Topic名称(如 databus-sync)
|
||||
* @param eventType 事件类型(格式: system-org-create)
|
||||
* @param clientCode 客户端编码
|
||||
*/
|
||||
private String buildClientTopic(String topicBase, String eventType, String clientCode) {
|
||||
// 默认topicBase为 databus-sync
|
||||
if (topicBase == null || topicBase.isEmpty()) {
|
||||
topicBase = "databus-sync";
|
||||
}
|
||||
// eventType 格式已经是 system-org-create,直接拼接
|
||||
return String.format("%s-%s-%s", topicBase, eventType.toLowerCase(), clientCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订阅断点
|
||||
*/
|
||||
private void updateSubscriptionCheckpoint(DatabusSyncSubscriptionDO subscription, Long eventRecordId) {
|
||||
subscription.setLastSyncEventId(eventRecordId);
|
||||
subscription.setLastSyncTime(LocalDateTime.now());
|
||||
subscription.setTotalSyncCount((subscription.getTotalSyncCount() == null ? 0 : subscription.getTotalSyncCount()) + 1);
|
||||
subscription.setTotalSuccessCount((subscription.getTotalSuccessCount() == null ? 0 : subscription.getTotalSuccessCount()) + 1);
|
||||
subscriptionMapper.updateById(subscription);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.framework.databus.server.core.sync;
|
||||
|
||||
/**
|
||||
* Databus 同步服务接口
|
||||
* 负责扫描事件记录并推送到客户端
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusSyncService {
|
||||
|
||||
/**
|
||||
* 执行同步任务
|
||||
* 扫描所有启用的订阅关系,推送新事件到客户端
|
||||
*/
|
||||
void executeSyncTask();
|
||||
|
||||
/**
|
||||
* 手动触发指定订阅的同步
|
||||
*
|
||||
* @param subscriptionId 订阅关系ID
|
||||
*/
|
||||
void triggerSync(Long subscriptionId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.zt.plat.framework.databus.server.core.sync;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.zt.plat.framework.databus.server.config.DatabusSyncServerProperties;
|
||||
import com.zt.plat.framework.databus.server.core.message.SyncMessage;
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusher;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.*;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.*;
|
||||
import com.zt.plat.framework.databus.server.enums.DeadLetterStatusEnum;
|
||||
import com.zt.plat.framework.databus.server.enums.SyncStatusEnum;
|
||||
import com.zt.plat.framework.databus.server.enums.TransportTypeEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus 同步服务实现
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DatabusSyncServiceImpl implements DatabusSyncService {
|
||||
|
||||
private final DatabusSyncSubscriptionMapper subscriptionMapper;
|
||||
private final DatabusSyncEventRecordMapper eventRecordMapper;
|
||||
private final DatabusSyncClientMapper clientMapper;
|
||||
private final DatabusSyncEventMapper eventMapper;
|
||||
private final DatabusSyncLogMapper syncLogMapper;
|
||||
private final DatabusSyncDeadLetterMapper deadLetterMapper;
|
||||
private final MessagePusher messagePusher;
|
||||
private final DatabusSyncServerProperties properties;
|
||||
|
||||
@Override
|
||||
public void executeSyncTask() {
|
||||
log.info("[Databus] 开始执行同步任务");
|
||||
|
||||
// 查询所有启用的订阅关系
|
||||
List<DatabusSyncSubscriptionDO> subscriptions = subscriptionMapper.selectList(
|
||||
new LambdaQueryWrapper<DatabusSyncSubscriptionDO>()
|
||||
.eq(DatabusSyncSubscriptionDO::getEnabled, 1)
|
||||
);
|
||||
|
||||
for (DatabusSyncSubscriptionDO subscription : subscriptions) {
|
||||
try {
|
||||
processSubscription(subscription);
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] 处理订阅失败, subscriptionId={}", subscription.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("[Databus] 同步任务执行完成, 处理订阅数={}", subscriptions.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerSync(Long subscriptionId) {
|
||||
DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectById(subscriptionId);
|
||||
if (subscription == null) {
|
||||
log.warn("[Databus] 订阅不存在, subscriptionId={}", subscriptionId);
|
||||
return;
|
||||
}
|
||||
|
||||
processSubscription(subscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个订阅
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
protected void processSubscription(DatabusSyncSubscriptionDO subscription) {
|
||||
// 三状态启用检查
|
||||
DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId());
|
||||
if (client == null || client.getEnabled() != 1) {
|
||||
log.debug("[Databus] 客户端未启用, clientId={}", subscription.getClientId());
|
||||
return;
|
||||
}
|
||||
|
||||
DatabusSyncEventDO event = eventMapper.selectById(subscription.getEventId());
|
||||
if (event == null || event.getEnabled() != 1) {
|
||||
log.debug("[Databus] 事件未启用, eventId={}", subscription.getEventId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询新事件记录(断点续传)
|
||||
LambdaQueryWrapper<DatabusSyncEventRecordDO> queryWrapper = new LambdaQueryWrapper<DatabusSyncEventRecordDO>()
|
||||
.eq(DatabusSyncEventRecordDO::getEventId, subscription.getEventId())
|
||||
.orderByAsc(DatabusSyncEventRecordDO::getId);
|
||||
|
||||
if (subscription.getLastSyncEventId() != null) {
|
||||
queryWrapper.gt(DatabusSyncEventRecordDO::getId, subscription.getLastSyncEventId());
|
||||
}
|
||||
|
||||
// 批量查询 - 使用 Page 分页,兼容达梦等数据库
|
||||
Page<DatabusSyncEventRecordDO> page = new Page<>(1, subscription.getBatchSize(), false);
|
||||
List<DatabusSyncEventRecordDO> records = eventRecordMapper.selectPage(page, queryWrapper).getRecords();
|
||||
|
||||
if (records.isEmpty()) {
|
||||
log.debug("[Databus] 无新事件, subscriptionId={}", subscription.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[Databus] 开始同步, subscriptionId={}, eventCount={}", subscription.getId(), records.size());
|
||||
|
||||
// 推送事件
|
||||
for (DatabusSyncEventRecordDO record : records) {
|
||||
boolean success = pushEvent(subscription, client, record);
|
||||
if (success) {
|
||||
// 更新断点
|
||||
subscription.setLastSyncEventId(record.getId());
|
||||
subscription.setLastSyncTime(LocalDateTime.now());
|
||||
subscription.setTotalSyncCount(subscription.getTotalSyncCount() + 1);
|
||||
subscription.setTotalSuccessCount(subscription.getTotalSuccessCount() + 1);
|
||||
} else {
|
||||
subscription.setTotalFailCount(subscription.getTotalFailCount() + 1);
|
||||
break; // 失败则停止当前批次
|
||||
}
|
||||
}
|
||||
|
||||
// 更新订阅统计
|
||||
subscriptionMapper.updateById(subscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送单个事件
|
||||
*/
|
||||
private boolean pushEvent(DatabusSyncSubscriptionDO subscription,
|
||||
DatabusSyncClientDO client,
|
||||
DatabusSyncEventRecordDO record) {
|
||||
String syncId = IdUtil.fastSimpleUUID();
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
|
||||
// 构建消息
|
||||
SyncMessage message = SyncMessage.builder()
|
||||
.syncId(syncId)
|
||||
.eventRecordId(record.getId())
|
||||
.eventType(record.getEventType())
|
||||
.eventAction(record.getEventAction())
|
||||
.dataSnapshot(record.getDataSnapshot())
|
||||
.dataVersion(record.getDataVersion())
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
// 记录日志
|
||||
DatabusSyncLogDO syncLog = DatabusSyncLogDO.builder()
|
||||
.syncId(syncId)
|
||||
.eventRecordId(record.getId())
|
||||
.subscriptionId(subscription.getId())
|
||||
.clientCode(client.getClientCode())
|
||||
.eventType(record.getEventType())
|
||||
.syncMode(subscription.getSyncMode())
|
||||
.startTime(startTime)
|
||||
.status(SyncStatusEnum.PENDING.getStatus())
|
||||
.retryCount(0)
|
||||
.tenantId(client.getTenantId())
|
||||
.build();
|
||||
|
||||
try {
|
||||
// 推送消息
|
||||
if (TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1) {
|
||||
// MQ 推送
|
||||
String topic = String.format("%s-%s-%s",
|
||||
client.getMqTopicBase(), record.getEventType(), client.getClientCode());
|
||||
String mqMsgId = messagePusher.pushByMQ(topic, message);
|
||||
|
||||
syncLog.setTransportType(TransportTypeEnum.MQ_FIRST.getType());
|
||||
syncLog.setMqTopic(topic);
|
||||
syncLog.setMqMsgId(mqMsgId);
|
||||
} else if (client.getHttpEnabled() == 1) {
|
||||
// HTTP 推送
|
||||
boolean success = messagePusher.pushByHttp(client.getHttpEndpoint(), message);
|
||||
if (!success) {
|
||||
throw new RuntimeException("HTTP推送失败");
|
||||
}
|
||||
|
||||
syncLog.setTransportType(TransportTypeEnum.HTTP_ONLY.getType());
|
||||
} else {
|
||||
throw new RuntimeException("无可用的推送方式");
|
||||
}
|
||||
|
||||
// 更新日志状态
|
||||
syncLog.setStatus(SyncStatusEnum.SUCCESS.getStatus());
|
||||
syncLog.setEndTime(LocalDateTime.now());
|
||||
syncLog.setDuration((int) (System.currentTimeMillis() - startTime.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli()));
|
||||
syncLogMapper.insert(syncLog);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] 推送失败, syncId={}, eventRecordId={}", syncId, record.getId(), e);
|
||||
|
||||
// 更新日志状态
|
||||
syncLog.setStatus(SyncStatusEnum.FAILED.getStatus());
|
||||
syncLog.setErrorMessage(e.getMessage());
|
||||
syncLog.setEndTime(LocalDateTime.now());
|
||||
syncLogMapper.insert(syncLog);
|
||||
|
||||
// 检查重试次数,超过限制则进入死信队列
|
||||
if (syncLog.getRetryCount() >= properties.getRetry().getMaxAttempts()) {
|
||||
saveToDeadLetter(syncLog, message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存到死信队列
|
||||
*/
|
||||
private void saveToDeadLetter(DatabusSyncLogDO syncLog, SyncMessage message) {
|
||||
DatabusSyncDeadLetterDO deadLetter = DatabusSyncDeadLetterDO.builder()
|
||||
.syncLogId(syncLog.getId())
|
||||
.syncId(syncLog.getSyncId())
|
||||
.clientCode(syncLog.getClientCode())
|
||||
.eventType(syncLog.getEventType())
|
||||
.eventRecordId(syncLog.getEventRecordId())
|
||||
.retryCount(syncLog.getRetryCount())
|
||||
.lastErrorMessage(syncLog.getErrorMessage())
|
||||
.lastErrorTime(LocalDateTime.now())
|
||||
.messageBody(cn.hutool.json.JSONUtil.toJsonStr(message))
|
||||
.status(DeadLetterStatusEnum.PENDING.getStatus())
|
||||
.handled(0)
|
||||
.tenantId(syncLog.getTenantId())
|
||||
.build();
|
||||
|
||||
deadLetterMapper.insert(deadLetter);
|
||||
|
||||
log.warn("[Databus] 消息已进入死信队列, syncId={}", syncLog.getSyncId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 数据同步客户端配置 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_client")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncClientDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 客户端编码
|
||||
*/
|
||||
private String clientCode;
|
||||
|
||||
/**
|
||||
* 客户端名称
|
||||
*/
|
||||
private String clientName;
|
||||
|
||||
/**
|
||||
* 启用状态(0-禁用 1-启用)
|
||||
*/
|
||||
private Integer enabled;
|
||||
|
||||
/**
|
||||
* 通信方式(1-MQ优先 2-仅HTTP)
|
||||
*/
|
||||
private Integer transportType;
|
||||
|
||||
/**
|
||||
* MQ是否启用(0-否 1-是)
|
||||
*/
|
||||
private Integer mqEnabled;
|
||||
|
||||
/**
|
||||
* MQ NameServer地址
|
||||
*/
|
||||
private String mqNamesrvAddr;
|
||||
|
||||
/**
|
||||
* MQ Topic基础名称
|
||||
*/
|
||||
private String mqTopicBase;
|
||||
|
||||
/**
|
||||
* HTTP是否启用(0-否 1-是)
|
||||
*/
|
||||
private Integer httpEnabled;
|
||||
|
||||
/**
|
||||
* HTTP推送端点
|
||||
*/
|
||||
private String httpEndpoint;
|
||||
|
||||
/**
|
||||
* 应用Key
|
||||
*/
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
* 应用Secret(加密存储)
|
||||
*/
|
||||
private String appSecret;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据同步死信队列 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_dead_letter")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncDeadLetterDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 同步日志ID
|
||||
*/
|
||||
private Long syncLogId;
|
||||
|
||||
/**
|
||||
* 同步ID
|
||||
*/
|
||||
private String syncId;
|
||||
|
||||
/**
|
||||
* 客户端编码
|
||||
*/
|
||||
private String clientCode;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件记录ID
|
||||
*/
|
||||
private Long eventRecordId;
|
||||
|
||||
/**
|
||||
* 已重试次数
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 最后错误信息
|
||||
*/
|
||||
private String lastErrorMessage;
|
||||
|
||||
/**
|
||||
* 最后错误时间
|
||||
*/
|
||||
private LocalDateTime lastErrorTime;
|
||||
|
||||
/**
|
||||
* 消息内容(JSON格式)
|
||||
*/
|
||||
private String messageBody;
|
||||
|
||||
/**
|
||||
* 状态(0-待处理 1-已重新投递 2-已忽略)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 是否已处理(0-否 1-是)
|
||||
*/
|
||||
private Integer handled;
|
||||
|
||||
/**
|
||||
* 处理时间
|
||||
*/
|
||||
private LocalDateTime handleTime;
|
||||
|
||||
/**
|
||||
* 处理人
|
||||
*/
|
||||
private String handler;
|
||||
|
||||
/**
|
||||
* 处理备注
|
||||
*/
|
||||
private String handleRemark;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 数据同步事件定义 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_event")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncEventDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 事件类型编码
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件名称
|
||||
*/
|
||||
private String eventName;
|
||||
|
||||
/**
|
||||
* 事件描述
|
||||
*/
|
||||
private String eventDesc;
|
||||
|
||||
/**
|
||||
* 启用状态(0-禁用 1-启用)
|
||||
*/
|
||||
private Integer enabled;
|
||||
|
||||
/**
|
||||
* 是否支持全量同步(0-否 1-是)
|
||||
*/
|
||||
private Integer supportFullSync;
|
||||
|
||||
/**
|
||||
* 是否支持增量同步(0-否 1-是)
|
||||
*/
|
||||
private Integer supportIncrementalSync;
|
||||
|
||||
/**
|
||||
* 数据结构版本
|
||||
*/
|
||||
private Integer dataVersion;
|
||||
|
||||
/**
|
||||
* 数据提供者服务名(Feign服务名)
|
||||
* 用于全量同步时调用对应服务获取数据
|
||||
*/
|
||||
private String dataProviderService;
|
||||
|
||||
/**
|
||||
* 数据提供者方法标识
|
||||
* 如: ORG(组织)、USER(用户)
|
||||
*/
|
||||
private String dataProviderMethod;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据同步事件流水 DO
|
||||
* 用于断点续传
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_event_record")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncEventRecordDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID(雪花算法,全局唯一递增)
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 事件定义ID
|
||||
*/
|
||||
private Long eventId;
|
||||
|
||||
/**
|
||||
* 事件类型编码
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件动作(create-创建 update-更新 delete-删除)
|
||||
*/
|
||||
private String eventAction;
|
||||
|
||||
/**
|
||||
* 完整业务数据快照(JSON格式)
|
||||
*/
|
||||
private String dataSnapshot;
|
||||
|
||||
/**
|
||||
* 数据版本号
|
||||
*/
|
||||
private Integer dataVersion;
|
||||
|
||||
/**
|
||||
* 来源服务名
|
||||
*/
|
||||
private String sourceService;
|
||||
|
||||
/**
|
||||
* 来源MQ Topic
|
||||
*/
|
||||
private String sourceTopic;
|
||||
|
||||
/**
|
||||
* 来源MQ消息ID
|
||||
*/
|
||||
private String sourceMsgId;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 操作人
|
||||
*/
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 事件发生时间
|
||||
*/
|
||||
private LocalDateTime eventTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据全量同步任务 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_full_task")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncFullTaskDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务编号(唯一标识)
|
||||
*/
|
||||
private String taskNo;
|
||||
|
||||
/**
|
||||
* 订阅关系ID
|
||||
*/
|
||||
private Long subscriptionId;
|
||||
|
||||
/**
|
||||
* 客户端编码
|
||||
*/
|
||||
private String clientCode;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 任务状态(0-待执行 1-执行中 2-已完成 3-失败 4-已取消)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 总数据量
|
||||
*/
|
||||
private Long totalCount;
|
||||
|
||||
/**
|
||||
* 已处理数据量
|
||||
*/
|
||||
private Long processedCount;
|
||||
|
||||
/**
|
||||
* 成功数据量
|
||||
*/
|
||||
private Long successCount;
|
||||
|
||||
/**
|
||||
* 失败数据量
|
||||
*/
|
||||
private Long failCount;
|
||||
|
||||
/**
|
||||
* 总批次数
|
||||
*/
|
||||
private Integer totalBatch;
|
||||
|
||||
/**
|
||||
* 当前批次号
|
||||
*/
|
||||
private Integer currentBatch;
|
||||
|
||||
/**
|
||||
* 批量大小
|
||||
*/
|
||||
private Integer batchSize;
|
||||
|
||||
/**
|
||||
* 游标时间(断点续传)
|
||||
*/
|
||||
private LocalDateTime cursorTime;
|
||||
|
||||
/**
|
||||
* 游标ID(断点续传)
|
||||
*/
|
||||
private Long cursorId;
|
||||
|
||||
/**
|
||||
* 任务开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 任务结束时间
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 最后一次错误信息
|
||||
*/
|
||||
private String lastErrorMessage;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据同步日志 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_log")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 同步ID(唯一标识)
|
||||
*/
|
||||
private String syncId;
|
||||
|
||||
/**
|
||||
* 事件记录ID
|
||||
*/
|
||||
private Long eventRecordId;
|
||||
|
||||
/**
|
||||
* 订阅关系ID
|
||||
*/
|
||||
private Long subscriptionId;
|
||||
|
||||
/**
|
||||
* 客户端编码
|
||||
*/
|
||||
private String clientCode;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 同步模式(1-全量 2-增量)
|
||||
*/
|
||||
private Integer syncMode;
|
||||
|
||||
/**
|
||||
* 传输方式(1-MQ优先 2-仅HTTP)
|
||||
*/
|
||||
private Integer transportType;
|
||||
|
||||
/**
|
||||
* MQ Topic
|
||||
*/
|
||||
private String mqTopic;
|
||||
|
||||
/**
|
||||
* MQ消息ID
|
||||
*/
|
||||
private String mqMsgId;
|
||||
|
||||
/**
|
||||
* 状态(0-待处理 1-成功 2-失败 3-重试中)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 重试次数
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 耗时(毫秒)
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 本批次数据条数
|
||||
*/
|
||||
private Integer dataCount;
|
||||
|
||||
/**
|
||||
* 批次号(全量同步时使用)
|
||||
*/
|
||||
private Integer batchNo;
|
||||
|
||||
/**
|
||||
* 全量任务ID(全量同步时关联)
|
||||
*/
|
||||
private Long fullTaskId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.zt.plat.framework.databus.server.dal.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据同步订阅关系 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("databus_sync_subscription")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusSyncSubscriptionDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 客户端ID
|
||||
*/
|
||||
private Long clientId;
|
||||
|
||||
/**
|
||||
* 事件ID
|
||||
*/
|
||||
private Long eventId;
|
||||
|
||||
/**
|
||||
* 订阅启用状态(0-禁用 1-启用)
|
||||
*/
|
||||
private Integer enabled;
|
||||
|
||||
/**
|
||||
* 同步模式(1-全量 2-增量)
|
||||
*/
|
||||
private Integer syncMode;
|
||||
|
||||
/**
|
||||
* 时效性(1-实时 2-准实时 3-批量)
|
||||
*/
|
||||
private Integer timeliness;
|
||||
|
||||
/**
|
||||
* 时效值(秒)
|
||||
*/
|
||||
private Integer timelinessValue;
|
||||
|
||||
/**
|
||||
* 批量大小
|
||||
*/
|
||||
private Integer batchSize;
|
||||
|
||||
/**
|
||||
* 最后同步的事件记录ID(断点续传)
|
||||
*/
|
||||
private Long lastSyncEventId;
|
||||
|
||||
/**
|
||||
* 最后同步时间
|
||||
*/
|
||||
private LocalDateTime lastSyncTime;
|
||||
|
||||
/**
|
||||
* 总同步次数
|
||||
*/
|
||||
private Long totalSyncCount;
|
||||
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private Long totalSuccessCount;
|
||||
|
||||
/**
|
||||
* 失败次数
|
||||
*/
|
||||
private Long totalFailCount;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据同步客户端 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncClientMapper extends BaseMapperX<DatabusSyncClientDO> {
|
||||
|
||||
default DatabusSyncClientDO selectByClientCode(String clientCode) {
|
||||
return selectOne(DatabusSyncClientDO::getClientCode, clientCode);
|
||||
}
|
||||
|
||||
default PageResult<DatabusSyncClientDO> selectPage(DatabusSyncClientPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DatabusSyncClientDO>()
|
||||
.likeIfPresent(DatabusSyncClientDO::getClientCode, reqVO.getClientCode())
|
||||
.likeIfPresent(DatabusSyncClientDO::getClientName, reqVO.getClientName())
|
||||
.eqIfPresent(DatabusSyncClientDO::getEnabled, reqVO.getEnabled())
|
||||
.eqIfPresent(DatabusSyncClientDO::getTransportType, reqVO.getTransportType())
|
||||
.orderByDesc(DatabusSyncClientDO::getId));
|
||||
}
|
||||
|
||||
default List<DatabusSyncClientDO> selectList() {
|
||||
return selectList(new LambdaQueryWrapperX<DatabusSyncClientDO>()
|
||||
.orderByDesc(DatabusSyncClientDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 数据同步死信队列 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncDeadLetterMapper extends BaseMapperX<DatabusSyncDeadLetterDO> {
|
||||
|
||||
default PageResult<DatabusSyncDeadLetterDO> selectPage(DatabusSyncDeadLetterPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DatabusSyncDeadLetterDO>()
|
||||
.eqIfPresent(DatabusSyncDeadLetterDO::getSyncId, reqVO.getSyncId())
|
||||
.likeIfPresent(DatabusSyncDeadLetterDO::getClientCode, reqVO.getClientCode())
|
||||
.likeIfPresent(DatabusSyncDeadLetterDO::getEventType, reqVO.getEventType())
|
||||
.eqIfPresent(DatabusSyncDeadLetterDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(DatabusSyncDeadLetterDO::getHandled, reqVO.getHandled())
|
||||
.betweenIfPresent(DatabusSyncDeadLetterDO::getCreateTime, reqVO.getCreateTimeStart(), reqVO.getCreateTimeEnd())
|
||||
.orderByDesc(DatabusSyncDeadLetterDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据同步事件定义 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncEventMapper extends BaseMapperX<DatabusSyncEventDO> {
|
||||
|
||||
default DatabusSyncEventDO selectByEventType(String eventType) {
|
||||
return selectOne(DatabusSyncEventDO::getEventType, eventType);
|
||||
}
|
||||
|
||||
default PageResult<DatabusSyncEventDO> selectPage(DatabusSyncEventPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DatabusSyncEventDO>()
|
||||
.likeIfPresent(DatabusSyncEventDO::getEventType, reqVO.getEventType())
|
||||
.likeIfPresent(DatabusSyncEventDO::getEventName, reqVO.getEventName())
|
||||
.eqIfPresent(DatabusSyncEventDO::getEnabled, reqVO.getEnabled())
|
||||
.orderByDesc(DatabusSyncEventDO::getId));
|
||||
}
|
||||
|
||||
default List<DatabusSyncEventDO> selectList() {
|
||||
return selectList(new LambdaQueryWrapperX<DatabusSyncEventDO>()
|
||||
.orderByDesc(DatabusSyncEventDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventRecordDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 数据同步事件流水 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncEventRecordMapper extends BaseMapperX<DatabusSyncEventRecordDO> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据全量同步任务 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncFullTaskMapper extends BaseMapperX<DatabusSyncFullTaskDO> {
|
||||
|
||||
/**
|
||||
* 根据任务编号查询
|
||||
*/
|
||||
default DatabusSyncFullTaskDO selectByTaskNo(String taskNo) {
|
||||
return selectOne(DatabusSyncFullTaskDO::getTaskNo, taskNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定订阅的进行中任务
|
||||
*/
|
||||
default DatabusSyncFullTaskDO selectRunningBySubscriptionId(Long subscriptionId) {
|
||||
return selectOne(new LambdaQueryWrapperX<DatabusSyncFullTaskDO>()
|
||||
.eq(DatabusSyncFullTaskDO::getSubscriptionId, subscriptionId)
|
||||
.in(DatabusSyncFullTaskDO::getStatus, 0, 1)); // 待执行或执行中
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询待执行的任务列表
|
||||
* 使用 MyBatis-Plus 分页机制,兼容达梦等数据库
|
||||
*/
|
||||
default List<DatabusSyncFullTaskDO> selectPendingTasks(int limit) {
|
||||
// 使用 Page 分页而不是 LIMIT 语法,以兼容达梦数据库
|
||||
Page<DatabusSyncFullTaskDO> page = new Page<>(1, limit, false);
|
||||
return selectPage(page, new LambdaQueryWrapperX<DatabusSyncFullTaskDO>()
|
||||
.eq(DatabusSyncFullTaskDO::getStatus, 0)
|
||||
.orderByAsc(DatabusSyncFullTaskDO::getCreateTime)).getRecords();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*/
|
||||
default PageResult<DatabusSyncFullTaskDO> selectPage(DatabusSyncFullTaskPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DatabusSyncFullTaskDO>()
|
||||
.likeIfPresent(DatabusSyncFullTaskDO::getTaskNo, reqVO.getTaskNo())
|
||||
.eqIfPresent(DatabusSyncFullTaskDO::getSubscriptionId, reqVO.getSubscriptionId())
|
||||
.eqIfPresent(DatabusSyncFullTaskDO::getClientCode, reqVO.getClientCode())
|
||||
.eqIfPresent(DatabusSyncFullTaskDO::getEventType, reqVO.getEventType())
|
||||
.eqIfPresent(DatabusSyncFullTaskDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(DatabusSyncFullTaskDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 数据同步日志 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncLogMapper extends BaseMapperX<DatabusSyncLogDO> {
|
||||
|
||||
default PageResult<DatabusSyncLogDO> selectPage(DatabusSyncPushLogPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DatabusSyncLogDO>()
|
||||
.eqIfPresent(DatabusSyncLogDO::getSyncId, reqVO.getSyncId())
|
||||
.eqIfPresent(DatabusSyncLogDO::getSubscriptionId, reqVO.getSubscriptionId())
|
||||
.likeIfPresent(DatabusSyncLogDO::getClientCode, reqVO.getClientCode())
|
||||
.likeIfPresent(DatabusSyncLogDO::getEventType, reqVO.getEventType())
|
||||
.eqIfPresent(DatabusSyncLogDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(DatabusSyncLogDO::getTransportType, reqVO.getTransportType())
|
||||
.betweenIfPresent(DatabusSyncLogDO::getCreateTime, reqVO.getCreateTimeStart(), reqVO.getCreateTimeEnd())
|
||||
.orderByDesc(DatabusSyncLogDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.zt.plat.framework.databus.server.dal.mapper;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 数据同步订阅关系 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabusSyncSubscriptionMapper extends BaseMapperX<DatabusSyncSubscriptionDO> {
|
||||
|
||||
default DatabusSyncSubscriptionDO selectByClientIdAndEventId(Long clientId, Long eventId) {
|
||||
return selectOne(new LambdaQueryWrapperX<DatabusSyncSubscriptionDO>()
|
||||
.eq(DatabusSyncSubscriptionDO::getClientId, clientId)
|
||||
.eq(DatabusSyncSubscriptionDO::getEventId, eventId));
|
||||
}
|
||||
|
||||
default PageResult<DatabusSyncSubscriptionDO> selectPage(DatabusSyncSubscriptionPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DatabusSyncSubscriptionDO>()
|
||||
.eqIfPresent(DatabusSyncSubscriptionDO::getClientId, reqVO.getClientId())
|
||||
.eqIfPresent(DatabusSyncSubscriptionDO::getEventId, reqVO.getEventId())
|
||||
.eqIfPresent(DatabusSyncSubscriptionDO::getEnabled, reqVO.getEnabled())
|
||||
.orderByDesc(DatabusSyncSubscriptionDO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据事件ID查询所有启用的订阅
|
||||
*/
|
||||
default java.util.List<DatabusSyncSubscriptionDO> selectEnabledByEventId(Long eventId) {
|
||||
return selectList(new LambdaQueryWrapperX<DatabusSyncSubscriptionDO>()
|
||||
.eq(DatabusSyncSubscriptionDO::getEventId, eventId)
|
||||
.eq(DatabusSyncSubscriptionDO::getEnabled, 1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 数据提供者类型枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataProviderTypeEnum {
|
||||
|
||||
ORG("ORG", "组织机构"),
|
||||
USER("USER", "用户");
|
||||
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
private final String code;
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
public static DataProviderTypeEnum getByCode(String code) {
|
||||
for (DataProviderTypeEnum value : values()) {
|
||||
if (value.getCode().equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 死信队列状态枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DeadLetterStatusEnum implements ArrayValuable<Integer> {
|
||||
|
||||
PENDING(0, "待处理"),
|
||||
REDELIVERED(1, "已重新投递"),
|
||||
IGNORED(2, "已忽略");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DeadLetterStatusEnum::getStatus).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 状态值
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isPending(Integer status) {
|
||||
return ObjUtil.equal(PENDING.status, status);
|
||||
}
|
||||
|
||||
public static boolean isRedelivered(Integer status) {
|
||||
return ObjUtil.equal(REDELIVERED.status, status);
|
||||
}
|
||||
|
||||
public static boolean isIgnored(Integer status) {
|
||||
return ObjUtil.equal(IGNORED.status, status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import com.zt.plat.framework.common.exception.ErrorCode;
|
||||
|
||||
/**
|
||||
* Databus 同步服务错误码枚举类
|
||||
* <p>
|
||||
* databus 系统,使用 1-010-000-000 段
|
||||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 数据同步事件 1-010-001-000 ==========
|
||||
ErrorCode EVENT_NOT_EXISTS = new ErrorCode(1_010_001_000, "数据同步事件不存在");
|
||||
ErrorCode EVENT_TYPE_DUPLICATE = new ErrorCode(1_010_001_001, "事件类型编码已存在");
|
||||
|
||||
// ========== 数据同步客户端 1-010-002-000 ==========
|
||||
ErrorCode CLIENT_NOT_EXISTS = new ErrorCode(1_010_002_000, "数据同步客户端不存在");
|
||||
ErrorCode CLIENT_CODE_DUPLICATE = new ErrorCode(1_010_002_001, "客户端编码已存在");
|
||||
|
||||
// ========== 数据同步订阅 1-010-003-000 ==========
|
||||
ErrorCode SUBSCRIPTION_NOT_EXISTS = new ErrorCode(1_010_003_000, "数据同步订阅不存在");
|
||||
ErrorCode SUBSCRIPTION_DUPLICATE = new ErrorCode(1_010_003_001, "该客户端已订阅该事件,不能重复订阅");
|
||||
|
||||
// ========== 数据同步推送日志 1-010-004-000 ==========
|
||||
ErrorCode PUSH_LOG_NOT_EXISTS = new ErrorCode(1_010_004_000, "数据同步推送日志不存在");
|
||||
|
||||
// ========== 数据同步死信队列 1-010-005-000 ==========
|
||||
ErrorCode DEAD_LETTER_NOT_EXISTS = new ErrorCode(1_010_005_000, "数据同步死信队列记录不存在");
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 全量任务状态枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum FullTaskStatusEnum implements ArrayValuable<Integer> {
|
||||
|
||||
PENDING(0, "待执行"),
|
||||
RUNNING(1, "执行中"),
|
||||
COMPLETED(2, "已完成"),
|
||||
FAILED(3, "失败"),
|
||||
CANCELLED(4, "已取消");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(FullTaskStatusEnum::getStatus).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 状态值
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isPending(Integer status) {
|
||||
return ObjUtil.equal(PENDING.status, status);
|
||||
}
|
||||
|
||||
public static boolean isRunning(Integer status) {
|
||||
return ObjUtil.equal(RUNNING.status, status);
|
||||
}
|
||||
|
||||
public static boolean isCompleted(Integer status) {
|
||||
return ObjUtil.equal(COMPLETED.status, status);
|
||||
}
|
||||
|
||||
public static boolean isFailed(Integer status) {
|
||||
return ObjUtil.equal(FAILED.status, status);
|
||||
}
|
||||
|
||||
public static boolean isCancelled(Integer status) {
|
||||
return ObjUtil.equal(CANCELLED.status, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以取消
|
||||
*/
|
||||
public static boolean canCancel(Integer status) {
|
||||
return isPending(status) || isRunning(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已终止(完成/失败/取消)
|
||||
*/
|
||||
public static boolean isTerminated(Integer status) {
|
||||
return isCompleted(status) || isFailed(status) || isCancelled(status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 同步模式枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SyncModeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
FULL(1, "全量同步"),
|
||||
INCREMENTAL(2, "增量同步");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(SyncModeEnum::getMode).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 模式值
|
||||
*/
|
||||
private final Integer mode;
|
||||
/**
|
||||
* 模式名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isFull(Integer mode) {
|
||||
return ObjUtil.equal(FULL.mode, mode);
|
||||
}
|
||||
|
||||
public static boolean isIncremental(Integer mode) {
|
||||
return ObjUtil.equal(INCREMENTAL.mode, mode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 同步状态枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SyncStatusEnum implements ArrayValuable<Integer> {
|
||||
|
||||
PENDING(0, "待处理"),
|
||||
SUCCESS(1, "成功"),
|
||||
FAILED(2, "失败"),
|
||||
RETRYING(3, "重试中");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(SyncStatusEnum::getStatus).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 状态值
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isPending(Integer status) {
|
||||
return ObjUtil.equal(PENDING.status, status);
|
||||
}
|
||||
|
||||
public static boolean isSuccess(Integer status) {
|
||||
return ObjUtil.equal(SUCCESS.status, status);
|
||||
}
|
||||
|
||||
public static boolean isFailed(Integer status) {
|
||||
return ObjUtil.equal(FAILED.status, status);
|
||||
}
|
||||
|
||||
public static boolean isRetrying(Integer status) {
|
||||
return ObjUtil.equal(RETRYING.status, status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 时效性枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TimelinessEnum implements ArrayValuable<Integer> {
|
||||
|
||||
REALTIME(1, "实时"),
|
||||
NEAR_REALTIME(2, "准实时"),
|
||||
BATCH(3, "批量");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TimelinessEnum::getType).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 类型值
|
||||
*/
|
||||
private final Integer type;
|
||||
/**
|
||||
* 类型名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isRealtime(Integer type) {
|
||||
return ObjUtil.equal(REALTIME.type, type);
|
||||
}
|
||||
|
||||
public static boolean isNearRealtime(Integer type) {
|
||||
return ObjUtil.equal(NEAR_REALTIME.type, type);
|
||||
}
|
||||
|
||||
public static boolean isBatch(Integer type) {
|
||||
return ObjUtil.equal(BATCH.type, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.zt.plat.framework.databus.server.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 传输方式枚举
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TransportTypeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
MQ_FIRST(1, "MQ优先"),
|
||||
HTTP_ONLY(2, "仅HTTP");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TransportTypeEnum::getType).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 类型值
|
||||
*/
|
||||
private final Integer type;
|
||||
/**
|
||||
* 类型名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static boolean isMqFirst(Integer type) {
|
||||
return ObjUtil.equal(MQ_FIRST.type, type);
|
||||
}
|
||||
|
||||
public static boolean isHttpOnly(Integer type) {
|
||||
return ObjUtil.equal(HTTP_ONLY.type, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.zt.plat.framework.databus.server.producer;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.databus.server.config.DatabusServerProperties;
|
||||
import com.zt.plat.module.databus.api.message.DatabusBatchMessage;
|
||||
import com.zt.plat.module.databus.api.message.DatabusMessage;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.client.producer.DefaultMQProducer;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus 消息生产者
|
||||
* <p>
|
||||
* 负责将数据变更消息发送到各个客户端的 Topic
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class DatabusMessageProducer {
|
||||
|
||||
private final DatabusServerProperties properties;
|
||||
|
||||
private DefaultMQProducer producer;
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws MQClientException {
|
||||
if (!properties.isEnabled() || !properties.getMq().isEnabled()) {
|
||||
log.info("[Databus Server] MQ 发送未启用");
|
||||
return;
|
||||
}
|
||||
|
||||
producer = new DefaultMQProducer(properties.getMq().getProducerGroup());
|
||||
producer.setNamesrvAddr(properties.getMq().getNameServer());
|
||||
producer.setSendMsgTimeout(properties.getMq().getSendMsgTimeout());
|
||||
producer.start();
|
||||
|
||||
log.info("[Databus Server] 消息生产者启动成功, nameServer={}, producerGroup={}",
|
||||
properties.getMq().getNameServer(), properties.getMq().getProducerGroup());
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (producer != null) {
|
||||
producer.shutdown();
|
||||
log.info("[Databus Server] 消息生产者已关闭");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送增量消息到所有客户端
|
||||
*
|
||||
* @param message 消息
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public <T> void send(DatabusMessage<T> message) {
|
||||
if (producer == null) {
|
||||
log.warn("[Databus Server] Producer 未初始化,无法发送消息");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> clients = properties.getClients();
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
log.warn("[Databus Server] 未配置客户端列表,无法发送消息");
|
||||
return;
|
||||
}
|
||||
|
||||
String messageJson = JSONUtil.toJsonStr(message);
|
||||
DatabusEventType eventType = message.getEventType();
|
||||
|
||||
for (String clientCode : clients) {
|
||||
try {
|
||||
String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode);
|
||||
Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8));
|
||||
mqMessage.setKeys(message.getMessageId());
|
||||
|
||||
SendResult result = producer.send(mqMessage);
|
||||
log.debug("[Databus Server] 消息发送成功, topic={}, messageId={}, result={}",
|
||||
topic, message.getMessageId(), result.getMsgId());
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Server] 消息发送失败, clientCode={}, eventType={}, messageId={}",
|
||||
clientCode, eventType.name(), message.getMessageId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送增量消息到指定客户端
|
||||
*
|
||||
* @param message 消息
|
||||
* @param clientCode 客户端编码
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public <T> void sendTo(DatabusMessage<T> message, String clientCode) {
|
||||
if (producer == null) {
|
||||
log.warn("[Databus Server] Producer 未初始化,无法发送消息");
|
||||
return;
|
||||
}
|
||||
|
||||
String messageJson = JSONUtil.toJsonStr(message);
|
||||
DatabusEventType eventType = message.getEventType();
|
||||
String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode);
|
||||
|
||||
try {
|
||||
Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8));
|
||||
mqMessage.setKeys(message.getMessageId());
|
||||
|
||||
SendResult result = producer.send(mqMessage);
|
||||
log.info("[Databus Server] 消息发送成功, topic={}, messageId={}, result={}",
|
||||
topic, message.getMessageId(), result.getMsgId());
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Server] 消息发送失败, clientCode={}, eventType={}, messageId={}",
|
||||
clientCode, eventType.name(), message.getMessageId(), e);
|
||||
throw new RuntimeException("消息发送失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送批量消息到所有客户端
|
||||
*
|
||||
* @param message 批量消息
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public <T> void sendBatch(DatabusBatchMessage<T> message) {
|
||||
if (producer == null) {
|
||||
log.warn("[Databus Server] Producer 未初始化,无法发送消息");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> clients = properties.getClients();
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
log.warn("[Databus Server] 未配置客户端列表,无法发送消息");
|
||||
return;
|
||||
}
|
||||
|
||||
String messageJson = JSONUtil.toJsonStr(message);
|
||||
DatabusEventType eventType = message.getEventType();
|
||||
|
||||
for (String clientCode : clients) {
|
||||
try {
|
||||
String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode);
|
||||
Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8));
|
||||
mqMessage.setKeys(message.getMessageId());
|
||||
|
||||
SendResult result = producer.send(mqMessage);
|
||||
log.debug("[Databus Server] 批量消息发送成功, topic={}, batchNo={}/{}, result={}",
|
||||
topic, message.getBatchNo(), message.getTotalBatch(), result.getMsgId());
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Server] 批量消息发送失败, clientCode={}, eventType={}, batchNo={}",
|
||||
clientCode, eventType.name(), message.getBatchNo(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送批量消息到指定客户端
|
||||
*
|
||||
* @param message 批量消息
|
||||
* @param clientCode 客户端编码
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public <T> void sendBatchTo(DatabusBatchMessage<T> message, String clientCode) {
|
||||
if (producer == null) {
|
||||
log.warn("[Databus Server] Producer 未初始化,无法发送消息");
|
||||
return;
|
||||
}
|
||||
|
||||
String messageJson = JSONUtil.toJsonStr(message);
|
||||
DatabusEventType eventType = message.getEventType();
|
||||
String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode);
|
||||
|
||||
try {
|
||||
Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8));
|
||||
mqMessage.setKeys(message.getMessageId());
|
||||
|
||||
SendResult result = producer.send(mqMessage);
|
||||
log.info("[Databus Server] 批量消息发送成功, topic={}, batchNo={}/{}, result={}",
|
||||
topic, message.getBatchNo(), message.getTotalBatch(), result.getMsgId());
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus Server] 批量消息发送失败, clientCode={}, eventType={}, batchNo={}",
|
||||
clientCode, eventType.name(), message.getBatchNo(), e);
|
||||
throw new RuntimeException("批量消息发送失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.zt.plat.framework.databus.server.service;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据同步客户端 Service 接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusSyncClientService {
|
||||
|
||||
/**
|
||||
* 创建数据同步客户端
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createClient(DatabusSyncClientSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新数据同步客户端
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateClient(DatabusSyncClientSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除数据同步客户端
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteClient(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步客户端
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据同步客户端
|
||||
*/
|
||||
DatabusSyncClientDO getClient(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步客户端分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 数据同步客户端分页
|
||||
*/
|
||||
PageResult<DatabusSyncClientDO> getClientPage(DatabusSyncClientPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得数据同步客户端列表
|
||||
*
|
||||
* @return 数据同步客户端列表
|
||||
*/
|
||||
List<DatabusSyncClientDO> getClientList();
|
||||
|
||||
/**
|
||||
* 更新客户端启用状态
|
||||
*
|
||||
* @param id 编号
|
||||
* @param enabled 启用状态
|
||||
*/
|
||||
void updateClientStatus(Long id, Integer enabled);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.zt.plat.framework.databus.server.service;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据同步死信队列 Service 接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusSyncDeadLetterService {
|
||||
|
||||
/**
|
||||
* 获得数据同步死信队列
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据同步死信队列
|
||||
*/
|
||||
DatabusSyncDeadLetterDO getDeadLetter(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步死信队列分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 数据同步死信队列分页
|
||||
*/
|
||||
PageResult<DatabusSyncDeadLetterDO> getDeadLetterPage(DatabusSyncDeadLetterPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 重新投递死信消息
|
||||
*
|
||||
* @param id 死信ID
|
||||
*/
|
||||
void reprocessDeadLetter(Long id);
|
||||
|
||||
/**
|
||||
* 批量重新投递死信消息
|
||||
*
|
||||
* @param ids 死信ID列表
|
||||
*/
|
||||
void batchReprocessDeadLetter(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 标记为已处理
|
||||
*
|
||||
* @param id 死信ID
|
||||
* @param remark 处理备注
|
||||
*/
|
||||
void markHandled(Long id, String remark);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.zt.plat.framework.databus.server.service;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据同步事件 Service 接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusSyncEventService {
|
||||
|
||||
/**
|
||||
* 创建数据同步事件
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createEvent(DatabusSyncEventSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新数据同步事件
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateEvent(DatabusSyncEventSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除数据同步事件
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteEvent(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步事件
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据同步事件
|
||||
*/
|
||||
DatabusSyncEventDO getEvent(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步事件分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 数据同步事件分页
|
||||
*/
|
||||
PageResult<DatabusSyncEventDO> getEventPage(DatabusSyncEventPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得数据同步事件列表
|
||||
*
|
||||
* @return 数据同步事件列表
|
||||
*/
|
||||
List<DatabusSyncEventDO> getEventList();
|
||||
|
||||
/**
|
||||
* 更新事件启用状态
|
||||
*
|
||||
* @param id 编号
|
||||
* @param enabled 启用状态
|
||||
*/
|
||||
void updateEventStatus(Long id, Integer enabled);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.zt.plat.framework.databus.server.service;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO;
|
||||
|
||||
/**
|
||||
* 数据同步推送日志 Service 接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusSyncLogService {
|
||||
|
||||
/**
|
||||
* 获得数据同步推送日志
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据同步推送日志
|
||||
*/
|
||||
DatabusSyncLogDO getPushLog(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步推送日志分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 数据同步推送日志分页
|
||||
*/
|
||||
PageResult<DatabusSyncLogDO> getPushLogPage(DatabusSyncPushLogPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 重试推送
|
||||
*
|
||||
* @param id 日志ID
|
||||
*/
|
||||
void retryPush(Long id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.zt.plat.framework.databus.server.service;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO;
|
||||
|
||||
/**
|
||||
* 数据同步订阅 Service 接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface DatabusSyncSubscriptionService {
|
||||
|
||||
/**
|
||||
* 创建数据同步订阅
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createSubscription(DatabusSyncSubscriptionSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新数据同步订阅
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateSubscription(DatabusSyncSubscriptionSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除数据同步订阅
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteSubscription(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步订阅
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据同步订阅
|
||||
*/
|
||||
DatabusSyncSubscriptionDO getSubscription(Long id);
|
||||
|
||||
/**
|
||||
* 获得数据同步订阅分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 数据同步订阅分页
|
||||
*/
|
||||
PageResult<DatabusSyncSubscriptionDO> getSubscriptionPage(DatabusSyncSubscriptionPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 更新订阅启用状态
|
||||
*
|
||||
* @param id 编号
|
||||
* @param enabled 启用状态
|
||||
*/
|
||||
void updateSubscriptionStatus(Long id, Integer enabled);
|
||||
|
||||
/**
|
||||
* 重置订阅断点
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void resetCheckpoint(Long id);
|
||||
|
||||
/**
|
||||
* 手动触发同步
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void triggerSync(Long id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
package com.zt.plat.framework.databus.server.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.databus.server.config.DatabusSyncServerProperties;
|
||||
import com.zt.plat.framework.databus.server.core.message.BatchSyncMessage;
|
||||
import com.zt.plat.framework.databus.server.core.provider.DataProvider;
|
||||
import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry;
|
||||
import com.zt.plat.framework.databus.server.core.pusher.MessagePusher;
|
||||
import com.zt.plat.framework.databus.server.core.sync.DatabusFullSyncService;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.*;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.*;
|
||||
import com.zt.plat.framework.databus.server.enums.*;
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus Full Sync Service Implementation
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DatabusFullSyncServiceImpl implements DatabusFullSyncService {
|
||||
|
||||
private final DatabusSyncFullTaskMapper fullTaskMapper;
|
||||
private final DatabusSyncSubscriptionMapper subscriptionMapper;
|
||||
private final DatabusSyncClientMapper clientMapper;
|
||||
private final DatabusSyncEventMapper eventMapper;
|
||||
private final DatabusSyncLogMapper syncLogMapper;
|
||||
private final MessagePusher messagePusher;
|
||||
private final DatabusSyncServerProperties properties;
|
||||
private final DataProviderRegistry dataProviderRegistry;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createFullSyncTask(Long subscriptionId, String remark) {
|
||||
DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectById(subscriptionId);
|
||||
if (subscription == null) {
|
||||
throw new RuntimeException("Subscription not found");
|
||||
}
|
||||
|
||||
DatabusSyncFullTaskDO runningTask = fullTaskMapper.selectRunningBySubscriptionId(subscriptionId);
|
||||
if (runningTask != null) {
|
||||
throw new RuntimeException("Full sync task already running: " + runningTask.getTaskNo());
|
||||
}
|
||||
|
||||
DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId());
|
||||
DatabusSyncEventDO event = eventMapper.selectById(subscription.getEventId());
|
||||
|
||||
if (client == null || event == null) {
|
||||
throw new RuntimeException("Client or event config not found");
|
||||
}
|
||||
|
||||
if (event.getSupportFullSync() != 1) {
|
||||
throw new RuntimeException("Event does not support full sync");
|
||||
}
|
||||
|
||||
DatabusSyncFullTaskDO task = DatabusSyncFullTaskDO.builder()
|
||||
.taskNo(IdUtil.fastSimpleUUID())
|
||||
.subscriptionId(subscriptionId)
|
||||
.clientCode(client.getClientCode())
|
||||
.eventType(event.getEventType())
|
||||
.status(FullTaskStatusEnum.PENDING.getStatus())
|
||||
.totalCount(0L)
|
||||
.processedCount(0L)
|
||||
.successCount(0L)
|
||||
.failCount(0L)
|
||||
.totalBatch(0)
|
||||
.currentBatch(0)
|
||||
.batchSize(subscription.getBatchSize())
|
||||
.tenantId(client.getTenantId())
|
||||
.remark(remark)
|
||||
.build();
|
||||
|
||||
fullTaskMapper.insert(task);
|
||||
log.info("[Databus] Full sync task created, taskId={}, taskNo={}", task.getId(), task.getTaskNo());
|
||||
|
||||
return task.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void executeFullSyncTask(Long taskId) {
|
||||
DatabusSyncFullTaskDO task = fullTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
log.error("[Databus] Task not found, taskId={}", taskId);
|
||||
return;
|
||||
}
|
||||
// 允许 PENDING、RUNNING、FAILED 状态执行(支持重试)
|
||||
if (FullTaskStatusEnum.isCompleted(task.getStatus()) ||
|
||||
FullTaskStatusEnum.isCancelled(task.getStatus())) {
|
||||
log.warn("[Databus] Task status not allowed, taskId={}, status={}", taskId, task.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
task.setStatus(FullTaskStatusEnum.RUNNING.getStatus());
|
||||
task.setStartTime(LocalDateTime.now());
|
||||
fullTaskMapper.updateById(task);
|
||||
|
||||
try {
|
||||
DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectById(task.getSubscriptionId());
|
||||
DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId());
|
||||
DatabusSyncEventDO event = eventMapper.selectById(subscription.getEventId());
|
||||
|
||||
String providerType = event.getDataProviderMethod();
|
||||
if (providerType == null) {
|
||||
throw new RuntimeException("Event data provider type not configured");
|
||||
}
|
||||
|
||||
DataProvider<?> dataProvider = dataProviderRegistry.getProvider(providerType);
|
||||
if (dataProvider == null) {
|
||||
throw new RuntimeException("Data provider not found: " + providerType);
|
||||
}
|
||||
|
||||
executeGenericFullSync(task, subscription, client, event, dataProvider);
|
||||
|
||||
task.setStatus(FullTaskStatusEnum.COMPLETED.getStatus());
|
||||
task.setEndTime(LocalDateTime.now());
|
||||
fullTaskMapper.updateById(task);
|
||||
|
||||
log.info("[Databus] Full sync task completed, taskId={}, totalCount={}, successCount={}",
|
||||
taskId, task.getTotalCount(), task.getSuccessCount());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] Full sync task failed, taskId={}", taskId, e);
|
||||
|
||||
task.setStatus(FullTaskStatusEnum.FAILED.getStatus());
|
||||
task.setEndTime(LocalDateTime.now());
|
||||
task.setLastErrorMessage(e.getMessage());
|
||||
fullTaskMapper.updateById(task);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void executeGenericFullSync(DatabusSyncFullTaskDO task,
|
||||
DatabusSyncSubscriptionDO subscription,
|
||||
DatabusSyncClientDO client,
|
||||
DatabusSyncEventDO event,
|
||||
DataProvider<T> dataProvider) {
|
||||
long totalCount = dataProvider.count(task.getTenantId());
|
||||
int totalBatch = (int) Math.ceil((double) totalCount / task.getBatchSize());
|
||||
|
||||
task.setTotalCount(totalCount);
|
||||
task.setTotalBatch(totalBatch);
|
||||
fullTaskMapper.updateById(task);
|
||||
|
||||
LocalDateTime cursorTime = task.getCursorTime();
|
||||
Long cursorId = task.getCursorId();
|
||||
int batchNo = task.getCurrentBatch();
|
||||
|
||||
while (true) {
|
||||
DatabusSyncFullTaskDO currentTask = fullTaskMapper.selectById(task.getId());
|
||||
if (FullTaskStatusEnum.isCancelled(currentTask.getStatus())) {
|
||||
log.info("[Databus] Task cancelled, taskId={}", task.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
batchNo++;
|
||||
DataProvider.CursorPageData<T> page = dataProvider.getPageByCursor(
|
||||
cursorTime, cursorId, task.getBatchSize(), task.getTenantId());
|
||||
|
||||
if (CollUtil.isEmpty(page.getList())) {
|
||||
break;
|
||||
}
|
||||
|
||||
boolean isLastBatch = !page.isHasMore();
|
||||
BatchSyncMessage message = buildBatchMessage(
|
||||
task, event, page.getList(), dataProvider, batchNo, totalBatch, isLastBatch, totalCount);
|
||||
|
||||
boolean success = pushBatchMessage(client, event.getEventType(), message);
|
||||
|
||||
recordSyncLog(task, subscription, client, event, message, success, batchNo);
|
||||
|
||||
task.setCurrentBatch(batchNo);
|
||||
task.setCursorTime(page.getNextCursorTime());
|
||||
task.setCursorId(page.getNextCursorId());
|
||||
task.setProcessedCount(task.getProcessedCount() + page.getCount());
|
||||
if (success) {
|
||||
task.setSuccessCount(task.getSuccessCount() + page.getCount());
|
||||
} else {
|
||||
task.setFailCount(task.getFailCount() + page.getCount());
|
||||
}
|
||||
fullTaskMapper.updateById(task);
|
||||
|
||||
if (isLastBatch) {
|
||||
break;
|
||||
}
|
||||
cursorTime = page.getNextCursorTime();
|
||||
cursorId = page.getNextCursorId();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> BatchSyncMessage buildBatchMessage(DatabusSyncFullTaskDO task,
|
||||
DatabusSyncEventDO event,
|
||||
List<T> dataList,
|
||||
DataProvider<T> dataProvider,
|
||||
int batchNo,
|
||||
int totalBatch,
|
||||
boolean isLastBatch,
|
||||
long totalCount) {
|
||||
List<BatchSyncMessage.SyncDataItem> items = new ArrayList<>();
|
||||
for (T data : dataList) {
|
||||
Long uid = dataProvider.extractUid(data);
|
||||
items.add(BatchSyncMessage.SyncDataItem.builder()
|
||||
.action("FULL")
|
||||
.uid(uid)
|
||||
.data(JSONUtil.toJsonStr(data))
|
||||
.build());
|
||||
}
|
||||
|
||||
return BatchSyncMessage.builder()
|
||||
.messageId(IdUtil.fastSimpleUUID())
|
||||
.requestId(task.getTaskNo())
|
||||
.eventType(event.getEventType())
|
||||
.syncMode(SyncModeEnum.FULL.getMode())
|
||||
.dataVersion(event.getDataVersion())
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.tenantId(task.getTenantId())
|
||||
.dataList(items)
|
||||
.count(items.size())
|
||||
.fullTaskId(task.getId())
|
||||
.batchNo(batchNo)
|
||||
.totalBatch(totalBatch)
|
||||
.isLastBatch(isLastBatch)
|
||||
.totalCount(totalCount)
|
||||
.build();
|
||||
}
|
||||
|
||||
private boolean pushBatchMessage(DatabusSyncClientDO client, String eventType,
|
||||
BatchSyncMessage message) {
|
||||
try {
|
||||
if (TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1) {
|
||||
// 使用 getByTopicSuffix 支持数据库存储的 topic 格式(如 system-post-full)
|
||||
DatabusEventType databusEventType = DatabusEventType.getByTopicSuffix(eventType);
|
||||
if (databusEventType == null) {
|
||||
log.error("[Databus] Unknown event type: {}", eventType);
|
||||
return false;
|
||||
}
|
||||
String topicBase = client.getMqTopicBase();
|
||||
if (topicBase == null || topicBase.isEmpty()) {
|
||||
topicBase = "databus-sync";
|
||||
}
|
||||
String topic = databusEventType.getTopic(topicBase, client.getClientCode());
|
||||
log.info("[Databus] Pushing batch message, topic={}, eventType={}, clientCode={}, messageId={}",
|
||||
topic, eventType, client.getClientCode(), message.getMessageId());
|
||||
messagePusher.pushBatchByMQ(topic, message);
|
||||
} else if (client.getHttpEnabled() == 1) {
|
||||
return messagePusher.pushBatchByHttp(client.getHttpEndpoint(), message);
|
||||
} else {
|
||||
throw new RuntimeException("No available push method");
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("[Databus] Push batch message failed, messageId={}", message.getMessageId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void recordSyncLog(DatabusSyncFullTaskDO task,
|
||||
DatabusSyncSubscriptionDO subscription,
|
||||
DatabusSyncClientDO client,
|
||||
DatabusSyncEventDO event,
|
||||
BatchSyncMessage message,
|
||||
boolean success,
|
||||
int batchNo) {
|
||||
DatabusSyncLogDO syncLog = DatabusSyncLogDO.builder()
|
||||
.syncId(message.getMessageId())
|
||||
.eventRecordId(0L)
|
||||
.subscriptionId(subscription.getId())
|
||||
.clientCode(client.getClientCode())
|
||||
.eventType(event.getEventType())
|
||||
.syncMode(SyncModeEnum.FULL.getMode())
|
||||
.transportType(TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1
|
||||
? TransportTypeEnum.MQ_FIRST.getType()
|
||||
: TransportTypeEnum.HTTP_ONLY.getType())
|
||||
.status(success ? SyncStatusEnum.SUCCESS.getStatus() : SyncStatusEnum.FAILED.getStatus())
|
||||
.retryCount(0)
|
||||
.startTime(LocalDateTime.now())
|
||||
.endTime(LocalDateTime.now())
|
||||
.tenantId(task.getTenantId())
|
||||
.dataCount(message.getCount())
|
||||
.batchNo(batchNo)
|
||||
.fullTaskId(task.getId())
|
||||
.build();
|
||||
|
||||
syncLogMapper.insert(syncLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelFullSyncTask(Long taskId) {
|
||||
DatabusSyncFullTaskDO task = fullTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
throw new RuntimeException("Task not found");
|
||||
}
|
||||
|
||||
if (!FullTaskStatusEnum.canCancel(task.getStatus())) {
|
||||
throw new RuntimeException("Task status not allowed to cancel");
|
||||
}
|
||||
|
||||
task.setStatus(FullTaskStatusEnum.CANCELLED.getStatus());
|
||||
task.setEndTime(LocalDateTime.now());
|
||||
fullTaskMapper.updateById(task);
|
||||
|
||||
log.info("[Databus] Full sync task cancelled, taskId={}", taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabusSyncFullTaskDO getTaskById(Long taskId) {
|
||||
return fullTaskMapper.selectById(taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabusSyncFullTaskDO getTaskByTaskNo(String taskNo) {
|
||||
return fullTaskMapper.selectByTaskNo(taskNo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.zt.plat.framework.databus.server.service.impl;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncClientConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncClientMapper;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncClientService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 数据同步客户端 Service 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class DatabusSyncClientServiceImpl implements DatabusSyncClientService {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncClientMapper clientMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createClient(DatabusSyncClientSaveReqVO createReqVO) {
|
||||
// 校验客户端编码唯一性
|
||||
validateClientCodeUnique(null, createReqVO.getClientCode());
|
||||
|
||||
// 插入
|
||||
DatabusSyncClientDO client = DatabusSyncClientConvert.INSTANCE.convert(createReqVO);
|
||||
clientMapper.insert(client);
|
||||
return client.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateClient(DatabusSyncClientSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateClientExists(updateReqVO.getId());
|
||||
// 校验客户端编码唯一性
|
||||
validateClientCodeUnique(updateReqVO.getId(), updateReqVO.getClientCode());
|
||||
|
||||
// 更新
|
||||
DatabusSyncClientDO updateObj = DatabusSyncClientConvert.INSTANCE.convert(updateReqVO);
|
||||
clientMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteClient(Long id) {
|
||||
// 校验存在
|
||||
validateClientExists(id);
|
||||
// 删除
|
||||
clientMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateClientExists(Long id) {
|
||||
if (clientMapper.selectById(id) == null) {
|
||||
throw exception(CLIENT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateClientCodeUnique(Long id, String clientCode) {
|
||||
DatabusSyncClientDO client = clientMapper.selectByClientCode(clientCode);
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 id 的客户端
|
||||
if (id == null) {
|
||||
throw exception(CLIENT_CODE_DUPLICATE);
|
||||
}
|
||||
if (!client.getId().equals(id)) {
|
||||
throw exception(CLIENT_CODE_DUPLICATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabusSyncClientDO getClient(Long id) {
|
||||
return clientMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<DatabusSyncClientDO> getClientPage(DatabusSyncClientPageReqVO pageReqVO) {
|
||||
return clientMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DatabusSyncClientDO> getClientList() {
|
||||
return clientMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateClientStatus(Long id, Integer enabled) {
|
||||
// 校验存在
|
||||
validateClientExists(id);
|
||||
// 更新状态
|
||||
DatabusSyncClientDO updateObj = new DatabusSyncClientDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setEnabled(enabled);
|
||||
clientMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.zt.plat.framework.databus.server.service.impl;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncDeadLetterMapper;
|
||||
import com.zt.plat.framework.databus.server.enums.DeadLetterStatusEnum;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncDeadLetterService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.DEAD_LETTER_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 数据同步死信队列 Service 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class DatabusSyncDeadLetterServiceImpl implements DatabusSyncDeadLetterService {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncDeadLetterMapper deadLetterMapper;
|
||||
|
||||
@Override
|
||||
public DatabusSyncDeadLetterDO getDeadLetter(Long id) {
|
||||
return deadLetterMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<DatabusSyncDeadLetterDO> getDeadLetterPage(DatabusSyncDeadLetterPageReqVO pageReqVO) {
|
||||
return deadLetterMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void reprocessDeadLetter(Long id) {
|
||||
// 校验存在
|
||||
DatabusSyncDeadLetterDO deadLetter = deadLetterMapper.selectById(id);
|
||||
if (deadLetter == null) {
|
||||
throw exception(DEAD_LETTER_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// TODO: 实现重新投递逻辑,将消息重新发送到 MQ 或 HTTP
|
||||
log.info("[reprocessDeadLetter] 重新投递死信消息,ID: {}, 同步ID: {}", id, deadLetter.getSyncId());
|
||||
|
||||
// 更新状态为已重新投递
|
||||
DatabusSyncDeadLetterDO updateObj = new DatabusSyncDeadLetterDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setStatus(DeadLetterStatusEnum.REDELIVERED.getStatus());
|
||||
updateObj.setHandled(1);
|
||||
updateObj.setHandleTime(LocalDateTime.now());
|
||||
deadLetterMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void batchReprocessDeadLetter(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Long id : ids) {
|
||||
try {
|
||||
reprocessDeadLetter(id);
|
||||
} catch (Exception e) {
|
||||
log.error("[batchReprocessDeadLetter] 重新投递失败,ID: {}", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void markHandled(Long id, String remark) {
|
||||
// 校验存在
|
||||
DatabusSyncDeadLetterDO deadLetter = deadLetterMapper.selectById(id);
|
||||
if (deadLetter == null) {
|
||||
throw exception(DEAD_LETTER_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 标记为已处理(忽略)
|
||||
DatabusSyncDeadLetterDO updateObj = new DatabusSyncDeadLetterDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setStatus(DeadLetterStatusEnum.IGNORED.getStatus());
|
||||
updateObj.setHandled(1);
|
||||
updateObj.setHandleTime(LocalDateTime.now());
|
||||
updateObj.setHandleRemark(remark);
|
||||
deadLetterMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.zt.plat.framework.databus.server.service.impl;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncEventConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncEventMapper;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncEventService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 数据同步事件 Service 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class DatabusSyncEventServiceImpl implements DatabusSyncEventService {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncEventMapper eventMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createEvent(DatabusSyncEventSaveReqVO createReqVO) {
|
||||
// 校验事件类型唯一性
|
||||
validateEventTypeUnique(null, createReqVO.getEventType());
|
||||
|
||||
// 插入
|
||||
DatabusSyncEventDO event = DatabusSyncEventConvert.INSTANCE.convert(createReqVO);
|
||||
eventMapper.insert(event);
|
||||
return event.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateEvent(DatabusSyncEventSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateEventExists(updateReqVO.getId());
|
||||
// 校验事件类型唯一性
|
||||
validateEventTypeUnique(updateReqVO.getId(), updateReqVO.getEventType());
|
||||
|
||||
// 更新
|
||||
DatabusSyncEventDO updateObj = DatabusSyncEventConvert.INSTANCE.convert(updateReqVO);
|
||||
eventMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteEvent(Long id) {
|
||||
// 校验存在
|
||||
validateEventExists(id);
|
||||
// 删除
|
||||
eventMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateEventExists(Long id) {
|
||||
if (eventMapper.selectById(id) == null) {
|
||||
throw exception(EVENT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateEventTypeUnique(Long id, String eventType) {
|
||||
DatabusSyncEventDO event = eventMapper.selectByEventType(eventType);
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 id 的事件
|
||||
if (id == null) {
|
||||
throw exception(EVENT_TYPE_DUPLICATE);
|
||||
}
|
||||
if (!event.getId().equals(id)) {
|
||||
throw exception(EVENT_TYPE_DUPLICATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabusSyncEventDO getEvent(Long id) {
|
||||
return eventMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<DatabusSyncEventDO> getEventPage(DatabusSyncEventPageReqVO pageReqVO) {
|
||||
return eventMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DatabusSyncEventDO> getEventList() {
|
||||
return eventMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateEventStatus(Long id, Integer enabled) {
|
||||
// 校验存在
|
||||
validateEventExists(id);
|
||||
// 更新状态
|
||||
DatabusSyncEventDO updateObj = new DatabusSyncEventDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setEnabled(enabled);
|
||||
eventMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.zt.plat.framework.databus.server.service.impl;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncLogMapper;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.PUSH_LOG_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 数据同步推送日志 Service 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class DatabusSyncLogServiceImpl implements DatabusSyncLogService {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncLogMapper pushLogMapper;
|
||||
|
||||
@Override
|
||||
public DatabusSyncLogDO getPushLog(Long id) {
|
||||
return pushLogMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<DatabusSyncLogDO> getPushLogPage(DatabusSyncPushLogPageReqVO pageReqVO) {
|
||||
return pushLogMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retryPush(Long id) {
|
||||
// 校验存在
|
||||
DatabusSyncLogDO pushLog = pushLogMapper.selectById(id);
|
||||
if (pushLog == null) {
|
||||
throw exception(PUSH_LOG_NOT_EXISTS);
|
||||
}
|
||||
// TODO: 实现重试逻辑,将日志重新入队或触发推送
|
||||
log.info("[retryPush] 重试推送,日志ID: {}, 同步ID: {}", id, pushLog.getSyncId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.zt.plat.framework.databus.server.service.impl;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO;
|
||||
import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO;
|
||||
import com.zt.plat.framework.databus.server.convert.DatabusSyncSubscriptionConvert;
|
||||
import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO;
|
||||
import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncSubscriptionMapper;
|
||||
import com.zt.plat.framework.databus.server.service.DatabusSyncSubscriptionService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 数据同步订阅 Service 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class DatabusSyncSubscriptionServiceImpl implements DatabusSyncSubscriptionService {
|
||||
|
||||
@Resource
|
||||
private DatabusSyncSubscriptionMapper subscriptionMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createSubscription(DatabusSyncSubscriptionSaveReqVO createReqVO) {
|
||||
// 校验订阅唯一性(同一个客户端不能重复订阅同一个事件)
|
||||
validateSubscriptionUnique(null, createReqVO.getClientId(), createReqVO.getEventId());
|
||||
|
||||
// 插入
|
||||
DatabusSyncSubscriptionDO subscription = DatabusSyncSubscriptionConvert.INSTANCE.convert(createReqVO);
|
||||
subscriptionMapper.insert(subscription);
|
||||
return subscription.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateSubscription(DatabusSyncSubscriptionSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateSubscriptionExists(updateReqVO.getId());
|
||||
// 校验订阅唯一性
|
||||
validateSubscriptionUnique(updateReqVO.getId(), updateReqVO.getClientId(), updateReqVO.getEventId());
|
||||
|
||||
// 更新
|
||||
DatabusSyncSubscriptionDO updateObj = DatabusSyncSubscriptionConvert.INSTANCE.convert(updateReqVO);
|
||||
subscriptionMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteSubscription(Long id) {
|
||||
// 校验存在
|
||||
validateSubscriptionExists(id);
|
||||
// 删除
|
||||
subscriptionMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateSubscriptionExists(Long id) {
|
||||
if (subscriptionMapper.selectById(id) == null) {
|
||||
throw exception(SUBSCRIPTION_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateSubscriptionUnique(Long id, Long clientId, Long eventId) {
|
||||
DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectByClientIdAndEventId(clientId, eventId);
|
||||
if (subscription == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 id 的订阅
|
||||
if (id == null) {
|
||||
throw exception(SUBSCRIPTION_DUPLICATE);
|
||||
}
|
||||
if (!subscription.getId().equals(id)) {
|
||||
throw exception(SUBSCRIPTION_DUPLICATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabusSyncSubscriptionDO getSubscription(Long id) {
|
||||
return subscriptionMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<DatabusSyncSubscriptionDO> getSubscriptionPage(DatabusSyncSubscriptionPageReqVO pageReqVO) {
|
||||
return subscriptionMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateSubscriptionStatus(Long id, Integer enabled) {
|
||||
// 校验存在
|
||||
validateSubscriptionExists(id);
|
||||
// 更新状态
|
||||
DatabusSyncSubscriptionDO updateObj = new DatabusSyncSubscriptionDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setEnabled(enabled);
|
||||
subscriptionMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void resetCheckpoint(Long id) {
|
||||
// 校验存在
|
||||
validateSubscriptionExists(id);
|
||||
// 重置断点
|
||||
DatabusSyncSubscriptionDO updateObj = new DatabusSyncSubscriptionDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setLastSyncEventId(null);
|
||||
updateObj.setLastSyncTime(null);
|
||||
subscriptionMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerSync(Long id) {
|
||||
// 校验存在
|
||||
validateSubscriptionExists(id);
|
||||
// TODO: 发送消息到 MQ 或调用异步任务触发同步
|
||||
log.info("[triggerSync] 手动触发同步,订阅ID: {}", id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.zt.plat.framework.databus.server.config.DatabusSyncServerAutoConfiguration
|
||||
@@ -0,0 +1,2 @@
|
||||
com.zt.plat.framework.databus.server.config.DatabusServerAutoConfiguration
|
||||
com.zt.plat.framework.databus.server.config.DatabusSyncServerAutoConfiguration
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.zt.plat.module.databus.api.message;
|
||||
|
||||
import com.zt.plat.module.databus.enums.DatabusEventType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Databus 全量同步批量消息
|
||||
* <p>
|
||||
* 用于全量同步场景,支持分批传输
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DatabusBatchMessage<T> implements Serializable {
|
||||
|
||||
/**
|
||||
* 消息ID(用于幂等)
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 全量同步任务ID
|
||||
*/
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private DatabusEventType eventType;
|
||||
|
||||
/**
|
||||
* 当前批次号(从1开始)
|
||||
*/
|
||||
private Integer batchNo;
|
||||
|
||||
/**
|
||||
* 总批次数
|
||||
*/
|
||||
private Integer totalBatch;
|
||||
|
||||
/**
|
||||
* 当前批次数据条数
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
/**
|
||||
* 总数据条数
|
||||
*/
|
||||
private Integer totalCount;
|
||||
|
||||
/**
|
||||
* 是否最后一批
|
||||
*/
|
||||
private Boolean isLastBatch;
|
||||
|
||||
/**
|
||||
* 数据列<E68DAE><E58897>
|
||||
*/
|
||||
private List<T> dataList;
|
||||
|
||||
/**
|
||||
* 消息产生时间
|
||||
*/
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
/**
|
||||
* 来源系统
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 创建批量消息
|
||||
*/
|
||||
public static <T> DatabusBatchMessage<T> of(DatabusEventType eventType, String taskId,
|
||||
int batchNo, int totalBatch,
|
||||
List<T> dataList, int totalCount) {
|
||||
DatabusBatchMessage<T> msg = new DatabusBatchMessage<>();
|
||||
msg.setMessageId(java.util.UUID.randomUUID().toString());
|
||||
msg.setTaskId(taskId);
|
||||
msg.setEventType(eventType);
|
||||
msg.setBatchNo(batchNo);
|
||||
msg.setTotalBatch(totalBatch);
|
||||
msg.setCount(dataList != null ? dataList.size() : 0);
|
||||
msg.setTotalCount(totalCount);
|
||||
msg.setIsLastBatch(batchNo >= totalBatch);
|
||||
msg.setDataList(dataList);
|
||||
msg.setTimestamp(LocalDateTime.now());
|
||||
return msg;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user