package vip.mate.channel;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import vip.mate.channel.dingtalk.DingTalkChannelAdapter;
import vip.mate.channel.discord.DiscordChannelAdapter;
import vip.mate.channel.feishu.FeishuChannelAdapter;
import vip.mate.channel.model.ChannelEntity;
import vip.mate.channel.qq.QQChannelAdapter;
import vip.mate.channel.service.ChannelService;
import vip.mate.channel.telegram.TelegramChannelAdapter;
import vip.mate.channel.web.WebChannelAdapter;
import vip.mate.channel.wecom.WeComChannelAdapter;
import vip.mate.channel.weixin.WeixinChannelAdapter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 渠道管理器
*
* 实现渠道生命周期管理 + 热替换机制:
* - 管理所有渠道适配器的生命周期(启动/停止/热替换)
* - 维护渠道类型注册表,根据 channelType 创建对应适配器
* - 支持动态增删渠道(通过 API 启用/禁用时自动 start/stop)
* - 应用启动时自动加载并启动所有 enabled 渠道
* - activeAdapters 使用 ReadWriteLock 保护,读操作并发安全,热替换使用写锁
*
* @author MateClaw Team
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ChannelManager {
private final ChannelService channelService;
private final ChannelMessageRouter messageRouter;
private final ChannelSessionStore channelSessionStore;
private final ObjectMapper objectMapper;
/** 运行中的渠道适配器:channelId -> adapter */
private final Map activeAdapters = new HashMap<>();
/** 读写锁:读操作(getAdapter 等)用读锁,写操作(start/stop/replace)用写锁 */
private final ReadWriteLock adapterLock = new ReentrantReadWriteLock();
/** 旧 Adapter stop() 的超时线程池 */
private final ExecutorService stopExecutor = Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r, "channel-stop");
t.setDaemon(true);
return t;
});
/** 旧 Adapter stop() 超时时间(秒) */
private static final int STOP_TIMEOUT_SECONDS = 5;
/** 支持的渠道类型 */
private static final Set SUPPORTED_TYPES = Set.of(
"web", "dingtalk", "feishu", "telegram", "discord", "wecom", "qq", "weixin", "slack", "webchat"
);
/**
* 应用启动完成后自动加载并启动所有已启用的渠道
* 使用 ApplicationReadyEvent 确保数据库 schema/data 初始化完成
*/
@EventListener(ApplicationReadyEvent.class)
public void init() {
log.info("Initializing ChannelManager...");
List channels = channelService.listEnabledChannels();
int started = 0;
for (ChannelEntity channel : channels) {
try {
startChannel(channel);
started++;
} catch (Exception e) {
log.error("Failed to start channel {}: {}", channel.getName(), e.getMessage());
}
}
log.info("ChannelManager initialized: {}/{} channels started", started, channels.size());
}
/**
* 应用关闭时停止所有渠道
*/
@PreDestroy
public void destroy() {
log.info("Shutting down ChannelManager, stopping {} active channels...", activeAdapters.size());
stopAll();
stopExecutor.shutdownNow();
messageRouter.shutdown();
}
// ==================== 渠道生命周期管理 ====================
/**
* 启动指定渠道
*/
public void startChannel(ChannelEntity channel) {
adapterLock.writeLock().lock();
try {
if (activeAdapters.containsKey(channel.getId())) {
log.info("Channel {} already running, skipping", channel.getName());
return;
}
ChannelAdapter adapter = createAdapter(channel);
adapter.start();
activeAdapters.put(channel.getId(), adapter);
log.info("Channel started: {} (type={}, id={})", channel.getName(), channel.getChannelType(), channel.getId());
} finally {
adapterLock.writeLock().unlock();
}
}
/**
* 停止指定渠道
*/
public void stopChannel(Long channelId) {
ChannelAdapter oldAdapter;
adapterLock.writeLock().lock();
try {
oldAdapter = activeAdapters.remove(channelId);
} finally {
adapterLock.writeLock().unlock();
}
if (oldAdapter != null) {
stopAdapterSafely(oldAdapter, "stopChannel");
}
}
/**
* 热替换渠道(配置变更后调用)
*
* 热替换流程:
* 1. 用新配置创建并启动新 Adapter(在锁外完成,避免长时间持锁)
* 2. 新 Adapter 就绪后,加写锁替换 activeAdapters 中的引用
* 3. 释放锁后,异步停止旧 Adapter(给定超时)
* 4. 如果新 Adapter start() 失败,保留旧的不变
*
* @param channelId 渠道ID
*/
public void restartChannel(Long channelId) {
ChannelEntity channel = channelService.getChannel(channelId);
if (!Boolean.TRUE.equals(channel.getEnabled())) {
// 渠道已禁用,直接停止旧的
log.info("[hot-swap] Channel {} is disabled, stopping old adapter", channel.getName());
stopChannel(channelId);
return;
}
log.info("[hot-swap] Starting hot-swap for channel: {} (type={}, id={})",
channel.getName(), channel.getChannelType(), channelId);
// Step 1: 在锁外创建并启动新 Adapter
ChannelAdapter newAdapter = createAdapter(channel);
try {
log.info("[hot-swap] Starting new adapter for channel: {}", channel.getName());
newAdapter.start();
log.info("[hot-swap] New adapter started successfully: {}", channel.getName());
} catch (Exception e) {
// 新 Adapter 启动失败,保留旧的不变
log.error("[hot-swap] New adapter failed to start for channel {}, keeping old adapter: {}",
channel.getName(), e.getMessage(), e);
return;
}
// Step 2: 加写锁,原子替换
ChannelAdapter oldAdapter;
adapterLock.writeLock().lock();
try {
oldAdapter = activeAdapters.put(channelId, newAdapter);
log.info("[hot-swap] Adapter reference swapped for channel: {} (old={})",
channel.getName(), oldAdapter != null ? "present" : "none");
} finally {
adapterLock.writeLock().unlock();
}
// Step 3: 锁外异步停止旧 Adapter
if (oldAdapter != null) {
log.info("[hot-swap] Stopping old adapter for channel: {}", channel.getName());
stopAdapterAsync(oldAdapter, channel.getName());
}
log.info("[hot-swap] Hot-swap completed for channel: {} (type={}, id={})",
channel.getName(), channel.getChannelType(), channelId);
}
/**
* 停止所有渠道
*/
public void stopAll() {
List adaptersToStop;
adapterLock.writeLock().lock();
try {
adaptersToStop = new ArrayList<>(activeAdapters.values());
activeAdapters.clear();
} finally {
adapterLock.writeLock().unlock();
}
for (ChannelAdapter adapter : adaptersToStop) {
stopAdapterSafely(adapter, "stopAll");
}
}
// ==================== 查询(读锁保护) ====================
/**
* 获取指定渠道的适配器
*/
public Optional getAdapter(Long channelId) {
adapterLock.readLock().lock();
try {
return Optional.ofNullable(activeAdapters.get(channelId));
} finally {
adapterLock.readLock().unlock();
}
}
/**
* 按渠道类型获取适配器(返回第一个匹配的)
*/
public Optional getAdapterByType(String channelType) {
adapterLock.readLock().lock();
try {
return activeAdapters.values().stream()
.filter(a -> a.getChannelType().equals(channelType))
.findFirst();
} finally {
adapterLock.readLock().unlock();
}
}
/**
* 获取所有运行中的渠道适配器
*/
public Collection getActiveAdapters() {
adapterLock.readLock().lock();
try {
return List.copyOf(activeAdapters.values());
} finally {
adapterLock.readLock().unlock();
}
}
/**
* 获取渠道运行状态摘要(含连接状态和最后错误信息)
*/
public Map getStatus() {
adapterLock.readLock().lock();
try {
Map status = new LinkedHashMap<>();
status.put("activeCount", activeAdapters.size());
status.put("supportedTypes", SUPPORTED_TYPES);
List