增加构建通知

This commit is contained in:
dengqichen 2025-11-13 10:31:58 +08:00
parent e5e20a4dbb
commit 8b6b4652ce
3 changed files with 137 additions and 39 deletions

View File

@ -35,19 +35,15 @@ import static com.qqchen.deploy.backend.framework.annotation.ServiceType.Type.DA
@Slf4j @Slf4j
@Service @Service
@ServiceType(DATABASE) @ServiceType(DATABASE)
public class NotificationChannelServiceImpl public class NotificationChannelServiceImpl extends BaseServiceImpl<NotificationChannel, NotificationChannelDTO, NotificationChannelQuery, Long> implements INotificationChannelService {
extends BaseServiceImpl<NotificationChannel, NotificationChannelDTO, NotificationChannelQuery, Long>
implements INotificationChannelService, INotificationSendService {
@Resource @Resource
private INotificationChannelRepository notificationChannelRepository; private INotificationChannelRepository notificationChannelRepository;
@Resource
private NotificationChannelConverter notificationChannelConverter;
@Resource @Resource
private NotificationChannelAdapterFactory adapterFactory; private NotificationChannelAdapterFactory adapterFactory;
@Override @Override
protected void validateUniqueConstraints(NotificationChannelDTO dto) { protected void validateUniqueConstraints(NotificationChannelDTO dto) {
// 检查渠道名称唯一性 // 检查渠道名称唯一性
@ -55,84 +51,84 @@ public class NotificationChannelServiceImpl
throw new UniqueConstraintException(ResponseCode.CONFLICT, "name", dto.getName()); throw new UniqueConstraintException(ResponseCode.CONFLICT, "name", dto.getName());
} }
} }
@Override @Override
@Transactional @Transactional
public boolean testConnection(Long id) { public boolean testConnection(Long id) {
// 1. 查询渠道配置 // 1. 查询渠道配置
NotificationChannel channel = notificationChannelRepository.findById(id) NotificationChannel channel = notificationChannelRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND)); .orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
log.info("开始测试通知渠道连接: id={}, name={}, type={}", log.info("开始测试通知渠道连接: id={}, name={}, type={}",
id, channel.getName(), channel.getChannelType()); id, channel.getName(), channel.getChannelType());
// 2. 获取对应的适配器 // 2. 获取对应的适配器
INotificationChannelAdapter adapter; INotificationChannelAdapter adapter;
try { try {
adapter = adapterFactory.getAdapter(channel.getChannelType()); adapter = adapterFactory.getAdapter(channel.getChannelType());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
log.error("获取通知渠道适配器失败: {}", e.getMessage()); log.error("获取通知渠道适配器失败: {}", e.getMessage());
throw new BusinessException(ResponseCode.ERROR, new Object[]{"不支持的渠道类型: " + channel.getChannelType()}); throw new BusinessException(ResponseCode.ERROR, new Object[] {"不支持的渠道类型: " + channel.getChannelType()});
} }
// 3. 转换配置 // 3. 转换配置
BaseNotificationConfig config = convertConfig(channel); BaseNotificationConfig config = convertConfig(channel);
// 4. 执行连接测试 // 4. 执行连接测试
try { try {
adapter.testConnection(config); adapter.testConnection(config);
log.info("通知渠道连接测试成功: id={}, name={}", id, channel.getName()); log.info("通知渠道连接测试成功: id={}, name={}", id, channel.getName());
return true; return true;
} catch (Exception e) { } catch (Exception e) {
log.error("通知渠道连接测试失败: id={}, name={}, 错误: {}", log.error("通知渠道连接测试失败: id={}, name={}, 错误: {}",
id, channel.getName(), e.getMessage(), e); id, channel.getName(), e.getMessage(), e);
throw new BusinessException(ResponseCode.ERROR, new Object[]{"连接测试失败: " + e.getMessage()}); throw new BusinessException(ResponseCode.ERROR, new Object[] {"连接测试失败: " + e.getMessage()});
} }
} }
@Override @Override
@Transactional @Transactional
public void enable(Long id) { public void enable(Long id) {
NotificationChannel channel = notificationChannelRepository.findById(id) NotificationChannel channel = notificationChannelRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND)); .orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
channel.setEnabled(true); channel.setEnabled(true);
notificationChannelRepository.save(channel); notificationChannelRepository.save(channel);
log.info("启用通知渠道: id={}, name={}", id, channel.getName()); log.info("启用通知渠道: id={}, name={}", id, channel.getName());
} }
@Override @Override
@Transactional @Transactional
public void disable(Long id) { public void disable(Long id) {
NotificationChannel channel = notificationChannelRepository.findById(id) NotificationChannel channel = notificationChannelRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND)); .orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
channel.setEnabled(false); channel.setEnabled(false);
notificationChannelRepository.save(channel); notificationChannelRepository.save(channel);
log.info("禁用通知渠道: id={}, name={}", id, channel.getName()); log.info("禁用通知渠道: id={}, name={}", id, channel.getName());
} }
public void send(BaseSendNotificationRequest request) { public void send(BaseSendNotificationRequest request) {
// 1. 参数校验 // 1. 参数校验
if (request == null || request.getChannelId() == null) { if (request == null || request.getChannelId() == null) {
throw new BusinessException(ResponseCode.INVALID_PARAM); throw new BusinessException(ResponseCode.INVALID_PARAM);
} }
if (request.getContent() == null || request.getContent().isEmpty()) { if (request.getContent() == null || request.getContent().isEmpty()) {
throw new BusinessException(ResponseCode.INVALID_PARAM); throw new BusinessException(ResponseCode.INVALID_PARAM);
} }
// 2. 查询渠道配置 // 2. 查询渠道配置
NotificationChannel channel = notificationChannelRepository.findById(request.getChannelId()) NotificationChannel channel = notificationChannelRepository.findById(request.getChannelId())
.orElseThrow(() -> new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_NOT_FOUND)); .orElseThrow(() -> new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_NOT_FOUND));
// 3. 校验渠道状态 // 3. 校验渠道状态
if (!channel.getEnabled()) { if (!channel.getEnabled()) {
throw new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_DISABLED); throw new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_DISABLED);
} }
// 4. 获取对应的适配器 // 4. 获取对应的适配器
INotificationChannelAdapter adapter; INotificationChannelAdapter adapter;
try { try {
@ -141,28 +137,28 @@ public class NotificationChannelServiceImpl
log.error("获取通知渠道适配器失败: {}", e.getMessage()); log.error("获取通知渠道适配器失败: {}", e.getMessage());
throw new BusinessException(ResponseCode.ERROR); throw new BusinessException(ResponseCode.ERROR);
} }
// 5. 转换配置 // 5. 转换配置
BaseNotificationConfig config = convertConfig(channel); BaseNotificationConfig config = convertConfig(channel);
// 6. 发送通知 // 6. 发送通知
try { try {
log.info("发送通知 - 渠道ID: {}, 渠道类型: {}, 内容: {}", log.info("发送通知 - 渠道ID: {}, 渠道类型: {}, 内容: {}",
channel.getId(), channel.getChannelType(), request.getContent()); channel.getId(), channel.getChannelType(), request.getContent());
adapter.send(config, request); adapter.send(config, request);
log.info("通知发送成功 - 渠道ID: {}", channel.getId()); log.info("通知发送成功 - 渠道ID: {}", channel.getId());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// 配置错误 Webhook Key 未配置 // 配置错误 Webhook Key 未配置
log.error("通知渠道配置错误 - 渠道ID: {}, 错误: {}", channel.getId(), e.getMessage()); log.error("通知渠道配置错误 - 渠道ID: {}, 错误: {}", channel.getId(), e.getMessage());
throw new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_CONFIG_ERROR, new Object[]{e.getMessage()}); throw new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_CONFIG_ERROR, new Object[] {e.getMessage()});
} catch (Exception e) { } catch (Exception e) {
log.error("通知发送失败 - 渠道ID: {}, 错误: {}", channel.getId(), e.getMessage(), e); log.error("通知发送失败 - 渠道ID: {}, 错误: {}", channel.getId(), e.getMessage(), e);
throw new BusinessException(ResponseCode.NOTIFICATION_SEND_FAILED, new Object[]{e.getMessage()}); throw new BusinessException(ResponseCode.NOTIFICATION_SEND_FAILED, new Object[] {e.getMessage()});
} }
} }
/** /**
* 将Map配置转换为具体的配置类型 * 将Map配置转换为具体的配置类型
*/ */

View File

@ -0,0 +1,102 @@
package com.qqchen.deploy.backend.notification.service.impl;
import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.exception.UniqueConstraintException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
import com.qqchen.deploy.backend.notification.adapter.INotificationChannelAdapter;
import com.qqchen.deploy.backend.notification.dto.BaseSendNotificationRequest;
import com.qqchen.deploy.backend.notification.dto.NotificationChannelDTO;
import com.qqchen.deploy.backend.notification.dto.NotificationChannelQuery;
import com.qqchen.deploy.backend.notification.entity.NotificationChannel;
import com.qqchen.deploy.backend.notification.entity.config.BaseNotificationConfig;
import com.qqchen.deploy.backend.notification.entity.config.EmailNotificationConfig;
import com.qqchen.deploy.backend.notification.entity.config.WeworkNotificationConfig;
import com.qqchen.deploy.backend.notification.factory.NotificationChannelAdapterFactory;
import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository;
import com.qqchen.deploy.backend.notification.service.INotificationChannelService;
import com.qqchen.deploy.backend.notification.service.INotificationSendService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static com.qqchen.deploy.backend.framework.annotation.ServiceType.Type.DATABASE;
/**
* 通知渠道Service实现
*
* @author qqchen
* @since 2025-10-22
*/
@Slf4j
@Service
@ServiceType(DATABASE)
public class NotificationSendServiceImpl implements INotificationSendService {
@Resource
private INotificationChannelRepository notificationChannelRepository;
@Resource
private NotificationChannelAdapterFactory adapterFactory;
public void send(BaseSendNotificationRequest request) {
// 1. 参数校验
if (request == null || request.getChannelId() == null) {
throw new BusinessException(ResponseCode.INVALID_PARAM);
}
if (request.getContent() == null || request.getContent().isEmpty()) {
throw new BusinessException(ResponseCode.INVALID_PARAM);
}
// 2. 查询渠道配置
NotificationChannel channel = notificationChannelRepository.findById(request.getChannelId()).orElseThrow(() -> new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_NOT_FOUND));
// 3. 校验渠道状态
if (!channel.getEnabled()) {
throw new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_DISABLED);
}
// 4. 获取对应的适配器
INotificationChannelAdapter adapter;
try {
adapter = adapterFactory.getAdapter(channel.getChannelType());
} catch (IllegalArgumentException e) {
log.error("获取通知渠道适配器失败: {}", e.getMessage());
throw new BusinessException(ResponseCode.ERROR);
}
// 5. 转换配置
BaseNotificationConfig config = convertConfig(channel);
// 6. 发送通知
try {
log.info("发送通知 - 渠道ID: {}, 渠道类型: {}, 内容: {}", channel.getId(), channel.getChannelType(), request.getContent());
adapter.send(config, request);
log.info("通知发送成功 - 渠道ID: {}", channel.getId());
} catch (IllegalArgumentException e) {
// 配置错误 Webhook Key 未配置
log.error("通知渠道配置错误 - 渠道ID: {}, 错误: {}", channel.getId(), e.getMessage());
throw new BusinessException(ResponseCode.NOTIFICATION_CHANNEL_CONFIG_ERROR, new Object[] {e.getMessage()});
} catch (Exception e) {
log.error("通知发送失败 - 渠道ID: {}, 错误: {}", channel.getId(), e.getMessage(), e);
throw new BusinessException(ResponseCode.NOTIFICATION_SEND_FAILED, new Object[] {e.getMessage()});
}
}
/**
* 将Map配置转换为具体的配置类型
*/
private BaseNotificationConfig convertConfig(NotificationChannel channel) {
return switch (channel.getChannelType()) {
case WEWORK -> JsonUtils.fromMap(channel.getConfig(), WeworkNotificationConfig.class);
case EMAIL -> JsonUtils.fromMap(channel.getConfig(), EmailNotificationConfig.class);
default -> throw new IllegalArgumentException("不支持的渠道类型: " + channel.getChannelType());
};
}
}

View File

@ -71,7 +71,7 @@ public class NotificationServiceImpl implements INotificationService {
} }
// 5. 渲染模板内容 // 5. 渲染模板内容
String content = notificationTemplateService.renderTemplate(template.getCode(), request.getParams()); String content = notificationTemplateService.renderByCode(template.getCode(), request.getParams());
// 6. 根据渠道类型发送通知 // 6. 根据渠道类型发送通知
switch (template.getChannelType()) { switch (template.getChannelType()) {