From 0de08cb09b249289d7b0270334975a65e09168a3 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Wed, 12 Nov 2025 14:23:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9E=84=E5=BB=BA=E9=80=9A?= =?UTF-8?q?=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/JenkinsBuildServiceImpl.java | 51 +++++-- .../adapter/INotificationChannelAdapter.java | 19 +-- .../adapter/impl/EmailChannelAdapter.java | 82 +++-------- .../adapter/impl/WeworkChannelAdapter.java | 131 +++++++++--------- .../api/NotificationChannelApiController.java | 10 +- .../dto/BaseNotificationConfigDTO.java | 7 +- .../dto/BaseSendNotificationRequest.java | 45 ++++++ .../dto/EmailNotificationConfigDTO.java | 10 +- .../dto/EmailSendNotificationRequest.java | 56 ++++++++ .../dto/WeworkNotificationConfigDTO.java | 20 +-- .../dto/WeworkSendNotificationRequest.java | 59 ++++++++ .../config/EmailNotificationConfig.java | 5 - .../config/WeworkNotificationConfig.java | 11 -- .../enums/WeworkMessageTypeEnum.java | 40 ++++++ .../service/INotificationSendService.java | 35 +---- .../impl/NotificationChannelServiceImpl.java | 9 +- .../delegate/NotificationNodeDelegate.java | 53 ++++++- 17 files changed, 413 insertions(+), 230 deletions(-) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseSendNotificationRequest.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailSendNotificationRequest.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkSendNotificationRequest.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/notification/enums/WeworkMessageTypeEnum.java diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java index 826a6874..707ad942 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java @@ -18,7 +18,9 @@ import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService; import com.qqchen.deploy.backend.notification.entity.NotificationChannel; import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository; import com.qqchen.deploy.backend.notification.service.INotificationSendService; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.WeworkSendNotificationRequest; +import com.qqchen.deploy.backend.notification.dto.EmailSendNotificationRequest; +import com.qqchen.deploy.backend.notification.enums.WeworkMessageTypeEnum; import com.qqchen.deploy.backend.deploy.dto.sync.JenkinsSyncContext; import com.qqchen.deploy.backend.framework.enums.ResponseCode; import com.qqchen.deploy.backend.framework.exception.BusinessException; @@ -640,27 +642,15 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl { + WeworkSendNotificationRequest weworkRequest = new WeworkSendNotificationRequest(); + weworkRequest.setChannelId(channel.getId()); + weworkRequest.setContent(title + "\n\n" + content); + weworkRequest.setMessageType(WeworkMessageTypeEnum.MARKDOWN); // 使用Markdown格式 + notificationSendService.send(weworkRequest); + } + case EMAIL -> { + EmailSendNotificationRequest emailRequest = new EmailSendNotificationRequest(); + emailRequest.setChannelId(channel.getId()); + emailRequest.setSubject(title); + emailRequest.setContent(content); + // 这里需要设置收件人,但Jenkins构建通知中没有提供 + // 实际应该从团队环境配置或其他地方获取 + emailRequest.setToReceivers(java.util.Arrays.asList("admin@company.com")); + notificationSendService.send(emailRequest); + } + default -> throw new RuntimeException("不支持的渠道类型: " + channel.getChannelType()); + } + } catch (Exception e) { + log.error("发送通知失败: channelId={}, type={}", channel.getId(), channel.getChannelType(), e); + throw e; + } + } + /** * 发送构建失败日志文件到企业微信 */ diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/INotificationChannelAdapter.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/INotificationChannelAdapter.java index cc6b3d2b..e36b9a24 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/INotificationChannelAdapter.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/INotificationChannelAdapter.java @@ -1,27 +1,28 @@ package com.qqchen.deploy.backend.notification.adapter; import com.qqchen.deploy.backend.notification.entity.config.BaseNotificationConfig; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.BaseSendNotificationRequest; import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; /** * 通知渠道适配器接口 - * 使用泛型约束配置类型,避免类型转换 + * 使用泛型约束配置类型和请求类型,避免类型转换 * - * @param 配置类型,必须继承 BaseNotificationConfig + * @param 配置类型,必须继承 BaseNotificationConfig + * @param 请求类型,必须继承 BaseSendNotificationRequest * @author qqchen * @since 2025-11-03 */ -public interface INotificationChannelAdapter { +public interface INotificationChannelAdapter { /** * 发送通知 * * @param config 渠道配置 - * @param request 通知请求 + * @param request 发送通知请求 * @throws Exception 发送失败时抛出异常 */ - void send(T config, NotificationRequest request) throws Exception; + void send(C config, R request) throws Exception; /** * 发送文件(可选,不是所有渠道都支持) @@ -31,7 +32,7 @@ public interface INotificationChannelAdapter { * @param fileName 文件名称 * @throws Exception 发送失败时抛出异常 */ - default void sendFile(T config, String filePath, String fileName) throws Exception { + default void sendFile(C config, String filePath, String fileName) throws Exception { throw new UnsupportedOperationException("该渠道不支持发送文件"); } @@ -48,7 +49,7 @@ public interface INotificationChannelAdapter { * @param config 渠道配置 * @return 校验结果消息 */ - default String validateConfig(T config) { + default String validateConfig(C config) { return "配置有效"; } @@ -59,6 +60,6 @@ public interface INotificationChannelAdapter { * @param config 渠道配置 * @throws Exception 测试失败时抛出异常 */ - void testConnection(T config) throws Exception; + void testConnection(C config) throws Exception; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/EmailChannelAdapter.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/EmailChannelAdapter.java index 933e2e69..41a60ac4 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/EmailChannelAdapter.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/EmailChannelAdapter.java @@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.notification.adapter.impl; import com.fasterxml.jackson.databind.ObjectMapper; import com.qqchen.deploy.backend.notification.adapter.INotificationChannelAdapter; import com.qqchen.deploy.backend.notification.entity.config.EmailNotificationConfig; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.EmailSendNotificationRequest; import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; import jakarta.mail.internet.InternetAddress; import jakarta.mail.internet.MimeMessage; @@ -24,23 +24,21 @@ import java.util.Properties; */ @Slf4j @Component -public class EmailChannelAdapter implements INotificationChannelAdapter { +public class EmailChannelAdapter implements INotificationChannelAdapter { private final ObjectMapper objectMapper = new ObjectMapper(); @Override - public void send(EmailNotificationConfig emailConfig, NotificationRequest request) throws Exception { + public void send(EmailNotificationConfig emailConfig, EmailSendNotificationRequest request) throws Exception { // 1. 校验配置 validateEmailConfig(emailConfig); // 2. 创建JavaMailSender JavaMailSenderImpl mailSender = createMailSender(emailConfig); - // 3. 确定收件人 - List receivers = determineReceivers(emailConfig, request); - - if (CollectionUtils.isEmpty(receivers)) { - throw new IllegalArgumentException("收件人列表为空,且未配置默认收件人"); + // 3. 校验收件人 + if (CollectionUtils.isEmpty(request.getToReceivers())) { + throw new IllegalArgumentException("收件人列表不能为空"); } // 4. 构建邮件 @@ -55,24 +53,26 @@ public class EmailChannelAdapter implements INotificationChannelAdapter determineReceivers(EmailNotificationConfig config, NotificationRequest request) { - // 优先使用请求中的收件人 - if (!CollectionUtils.isEmpty(request.getReceivers())) { - return request.getReceivers(); - } - - // 使用配置中的默认收件人 - return config.getDefaultReceivers(); - } } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java index a92322b0..7e3db327 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java @@ -3,9 +3,10 @@ package com.qqchen.deploy.backend.notification.adapter.impl; import com.fasterxml.jackson.databind.ObjectMapper; import com.qqchen.deploy.backend.framework.utils.JsonUtils; import com.qqchen.deploy.backend.notification.adapter.INotificationChannelAdapter; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.WeworkSendNotificationRequest; import com.qqchen.deploy.backend.notification.entity.config.WeworkNotificationConfig; import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; +import com.qqchen.deploy.backend.notification.enums.WeworkMessageTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; @@ -34,7 +35,7 @@ import java.util.Map; */ @Slf4j @Component -public class WeworkChannelAdapter implements INotificationChannelAdapter { +public class WeworkChannelAdapter implements INotificationChannelAdapter { private static final String BASE_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook"; @@ -42,59 +43,61 @@ public class WeworkChannelAdapter implements INotificationChannelAdapter sendFileMessage(config, request); + case TEXT, MARKDOWN -> sendTextOrMarkdownMessage(config, request); + } + } + + /** + * 发送文本或Markdown消息 + */ + private void sendTextOrMarkdownMessage(WeworkNotificationConfig config, WeworkSendNotificationRequest request) throws Exception { + // 构建发送消息 URL String webhookUrl = BASE_URL + "/send?key=" + config.getKey(); - // 2. 构建消息内容 - String message = buildMessage(request); - - // 3. 构建@人列表 - List mentionedList = buildMentionedList(config, request); - List mentionedMobileList = buildMentionedMobileList(config, request); - - // 4. 构建企业微信消息体 + // 构建企业微信消息体 Map messageBody = new HashMap<>(); - - // 智能判断消息类型:如果包含 Markdown 标签,使用 markdown 类型 - boolean isMarkdown = isMarkdownMessage(message); - String msgType = isMarkdown ? "markdown" : "text"; + String msgType = request.getMessageType().getApiType(); messageBody.put("msgtype", msgType); - if (isMarkdown) { + if (request.getMessageType() == WeworkMessageTypeEnum.MARKDOWN) { // Markdown 格式消息 Map markdownContent = new HashMap<>(); - markdownContent.put("content", message); + markdownContent.put("content", request.getContent()); messageBody.put("markdown", markdownContent); } else { // 纯文本消息 Map textContent = new HashMap<>(); - textContent.put("content", message); + textContent.put("content", request.getContent()); - if (!CollectionUtils.isEmpty(mentionedList)) { - textContent.put("mentioned_list", mentionedList); + // 添加@人列表 + if (!CollectionUtils.isEmpty(request.getMentionedUserList())) { + textContent.put("mentioned_list", request.getMentionedUserList()); } - if (!CollectionUtils.isEmpty(mentionedMobileList)) { - textContent.put("mentioned_mobile_list", mentionedMobileList); + if (!CollectionUtils.isEmpty(request.getMentionedMobileList())) { + textContent.put("mentioned_mobile_list", request.getMentionedMobileList()); } messageBody.put("text", textContent); } - // 5. 发送请求 + // 发送请求 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); String jsonBody = objectMapper.writeValueAsString(messageBody); HttpEntity entity = new HttpEntity<>(jsonBody, headers); - log.info("发送企业微信通知 - URL: {}, 类型: {}, 消息: {}", webhookUrl, msgType, message); + log.info("发送企业微信通知 - URL: {}, 类型: {}, 消息: {}", webhookUrl, msgType, request.getContent()); String response = restTemplate.exchange( webhookUrl, @@ -106,6 +109,44 @@ public class WeworkChannelAdapter implements INotificationChannelAdapter buildMentionedList(WeworkNotificationConfig config, NotificationRequest request) { - List mentionedList = new ArrayList<>(); - - // 优先使用请求中的mentions - if (!CollectionUtils.isEmpty(request.getMentions())) { - mentionedList.addAll(request.getMentions()); - } else if (!CollectionUtils.isEmpty(config.getMentionedList())) { - // 使用配置中的默认值 - mentionedList.addAll(config.getMentionedList()); - } - - return mentionedList; - } - - /** - * 构建@人列表(手机号) - */ - private List buildMentionedMobileList(WeworkNotificationConfig config, NotificationRequest request) { - List mentionedMobileList = new ArrayList<>(); - - // 使用配置中的默认手机号 - if (!CollectionUtils.isEmpty(config.getMentionedMobileList())) { - mentionedMobileList.addAll(config.getMentionedMobileList()); - } - - return mentionedMobileList; - } /** * 判断是否是 Markdown 格式消息 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/api/NotificationChannelApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/api/NotificationChannelApiController.java index cab235dc..b71127d9 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/api/NotificationChannelApiController.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/api/NotificationChannelApiController.java @@ -4,7 +4,7 @@ import com.qqchen.deploy.backend.framework.api.Response; import com.qqchen.deploy.backend.framework.controller.BaseController; import com.qqchen.deploy.backend.notification.dto.NotificationChannelDTO; import com.qqchen.deploy.backend.notification.dto.NotificationChannelQuery; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.BaseSendNotificationRequest; import com.qqchen.deploy.backend.notification.entity.NotificationChannel; import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; import com.qqchen.deploy.backend.notification.service.INotificationChannelService; @@ -41,7 +41,7 @@ public class NotificationChannelApiController extends BaseController update(@PathVariable Long id,@Validated @RequestBody NotificationChannelDTO dto) { + public Response update(@PathVariable Long id, @Validated @RequestBody NotificationChannelDTO dto) { return super.update(id, dto); } @@ -127,11 +127,11 @@ public class NotificationChannelApiController extends BaseController send( - @Parameter(description = "通知请求", required = true) @RequestBody NotificationRequest request + @Parameter(description = "通知请求", required = true) @RequestBody BaseSendNotificationRequest request ) { notificationSendService.send(request); return Response.success(); diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseNotificationConfigDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseNotificationConfigDTO.java index 37cad7f6..eaa546d7 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseNotificationConfigDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseNotificationConfigDTO.java @@ -8,6 +8,9 @@ import lombok.Data; /** * 通知配置DTO基类 * 用于数据传输层,与Entity层的BaseNotificationConfig对应 + * + * 使用 EXISTING_PROPERTY 方式,利用外层 NotificationChannelDTO 的 channelType 字段 + * 进行多态反序列化,无需在 config 内部添加额外的 type 字段 * * @author qqchen * @since 2025-11-12 @@ -15,7 +18,7 @@ import lombok.Data; @Data @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + include = JsonTypeInfo.As.PROPERTY, property = "channelType" ) @JsonSubTypes({ @@ -25,7 +28,7 @@ import lombok.Data; public abstract class BaseNotificationConfigDTO { /** - * 获取渠道类型 + * 获取渠道类型(与外层 NotificationChannelDTO.channelType 保持一致) */ public abstract NotificationChannelTypeEnum getChannelType(); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseSendNotificationRequest.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseSendNotificationRequest.java new file mode 100644 index 00000000..b24d7271 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/BaseSendNotificationRequest.java @@ -0,0 +1,45 @@ +package com.qqchen.deploy.backend.notification.dto; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 发送通知请求基类 + * 不同渠道有不同的发送参数,使用多态设计 + * + * @author qqchen + * @since 2025-11-12 + */ +@Data +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "channelType" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = WeworkSendNotificationRequest.class, name = "WEWORK"), + @JsonSubTypes.Type(value = EmailSendNotificationRequest.class, name = "EMAIL") +}) +public abstract class BaseSendNotificationRequest { + + /** + * 通知渠道ID(必填) + */ + @NotNull(message = "渠道ID不能为空") + private Long channelId; + + /** + * 消息内容(必填) + */ + @NotBlank(message = "消息内容不能为空") + private String content; + + /** + * 获取渠道类型 + */ + public abstract NotificationChannelTypeEnum getChannelType(); +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailNotificationConfigDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailNotificationConfigDTO.java index 6f856297..549803d8 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailNotificationConfigDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailNotificationConfigDTO.java @@ -16,6 +16,11 @@ import java.util.List; @EqualsAndHashCode(callSuper = true) public class EmailNotificationConfigDTO extends BaseNotificationConfigDTO { + /** + * 渠道类型(用于Jackson反序列化,与外层保持一致) + */ + private final NotificationChannelTypeEnum channelType = NotificationChannelTypeEnum.EMAIL; + /** * SMTP服务器地址(必填) */ @@ -46,11 +51,6 @@ public class EmailNotificationConfigDTO extends BaseNotificationConfigDTO { */ private String fromName; - /** - * 默认收件人列表(可选) - */ - private List defaultReceivers; - /** * 是否使用SSL(可选,默认true) */ diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailSendNotificationRequest.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailSendNotificationRequest.java new file mode 100644 index 00000000..c8c917fb --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/EmailSendNotificationRequest.java @@ -0,0 +1,56 @@ +package com.qqchen.deploy.backend.notification.dto; + +import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 邮件发送通知请求 + * + * @author qqchen + * @since 2025-11-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EmailSendNotificationRequest extends BaseSendNotificationRequest { + + /** + * 渠道类型(用于Jackson反序列化) + */ + private final NotificationChannelTypeEnum channelType = NotificationChannelTypeEnum.EMAIL; + + /** + * 邮件主题(必填) + */ + @NotEmpty(message = "邮件主题不能为空") + private String subject; + + /** + * 收件人列表(必填) + */ + @NotEmpty(message = "收件人不能为空") + private List toReceivers; + + /** + * 抄送列表(可选) + */ + private List ccReceivers; + + /** + * 密送列表(可选) + */ + private List bccReceivers; + + /** + * 是否HTML格式(可选,默认false) + */ + private Boolean isHtml = false; + + @Override + public NotificationChannelTypeEnum getChannelType() { + return NotificationChannelTypeEnum.EMAIL; + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkNotificationConfigDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkNotificationConfigDTO.java index 1f7b429b..2b096dbb 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkNotificationConfigDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkNotificationConfigDTO.java @@ -15,24 +15,18 @@ import java.util.List; @Data @EqualsAndHashCode(callSuper = true) public class WeworkNotificationConfigDTO extends BaseNotificationConfigDTO { - + + /** + * 渠道类型(用于Jackson反序列化,与外层保持一致) + */ + private final NotificationChannelTypeEnum channelType = NotificationChannelTypeEnum.WEWORK; + /** * 企业微信机器人 Webhook Key(必填) * 完整URL格式: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={key} */ private String key; - - /** - * 默认@的手机号列表(可选) - */ - private List mentionedMobileList; - - /** - * 默认@的用户列表(可选) - * 例如:["@all"] 表示@所有人 - */ - private List mentionedList; - + @Override public NotificationChannelTypeEnum getChannelType() { return NotificationChannelTypeEnum.WEWORK; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkSendNotificationRequest.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkSendNotificationRequest.java new file mode 100644 index 00000000..88a0af3a --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/dto/WeworkSendNotificationRequest.java @@ -0,0 +1,59 @@ +package com.qqchen.deploy.backend.notification.dto; + +import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; +import com.qqchen.deploy.backend.notification.enums.WeworkMessageTypeEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 企业微信发送通知请求 + * + * @author qqchen + * @since 2025-11-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WeworkSendNotificationRequest extends BaseSendNotificationRequest { + + /** + * 渠道类型(用于Jackson反序列化) + */ + private final NotificationChannelTypeEnum channelType = NotificationChannelTypeEnum.WEWORK; + + /** + * 消息类型(必填) + * - TEXT: 普通文本消息 + * - MARKDOWN: Markdown格式消息 + * - FILE: 文件消息 + */ + private WeworkMessageTypeEnum messageType = WeworkMessageTypeEnum.TEXT; + + /** + * @的手机号列表(可选) 例如:["13800138000", "13900139000"] + */ + private List mentionedMobileList; + + /** + * @的用户ID列表(可选) 例如:["@all"] 表示@所有人,或具体的userid + */ + private List mentionedUserList; + + /** + * 文件路径(仅当messageType为FILE时使用) + * 服务器本地文件路径,用于文件上传 + */ + private String filePath; + + /** + * 文件名称(仅当messageType为FILE时使用) + * 显示给用户的文件名 + */ + private String fileName; + + @Override + public NotificationChannelTypeEnum getChannelType() { + return NotificationChannelTypeEnum.WEWORK; + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/EmailNotificationConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/EmailNotificationConfig.java index 1cdd8e6d..39acb9f6 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/EmailNotificationConfig.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/EmailNotificationConfig.java @@ -46,11 +46,6 @@ public class EmailNotificationConfig extends BaseNotificationConfig { */ private String fromName; - /** - * 默认收件人列表(可选) - */ - private List defaultReceivers; - /** * 是否使用SSL(可选,默认true) */ diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/WeworkNotificationConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/WeworkNotificationConfig.java index daf34ac7..64aeed9a 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/WeworkNotificationConfig.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/entity/config/WeworkNotificationConfig.java @@ -22,17 +22,6 @@ public class WeworkNotificationConfig extends BaseNotificationConfig { */ private String key; - /** - * 默认@的手机号列表(可选) - */ - private List mentionedMobileList; - - /** - * 默认@的用户列表(可选) - * 例如:["@all"] 表示@所有人 - */ - private List mentionedList; - @Override public NotificationChannelTypeEnum getChannelType() { return NotificationChannelTypeEnum.WEWORK; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/enums/WeworkMessageTypeEnum.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/enums/WeworkMessageTypeEnum.java new file mode 100644 index 00000000..636de1fc --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/enums/WeworkMessageTypeEnum.java @@ -0,0 +1,40 @@ +package com.qqchen.deploy.backend.notification.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 企业微信消息类型枚举 + * + * @author qqchen + * @since 2025-11-12 + */ +@Getter +@AllArgsConstructor +public enum WeworkMessageTypeEnum { + + /** + * 文本消息 + */ + TEXT("text", "文本消息"), + + /** + * Markdown消息 + */ + MARKDOWN("markdown", "Markdown消息"), + + /** + * 文件消息 + */ + FILE("file", "文件消息"); + + /** + * 企业微信API中的消息类型标识 + */ + private final String apiType; + + /** + * 消息类型描述 + */ + private final String description; +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/service/INotificationSendService.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/service/INotificationSendService.java index 8aa4c9fc..e2686584 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/service/INotificationSendService.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/service/INotificationSendService.java @@ -1,6 +1,6 @@ package com.qqchen.deploy.backend.notification.service; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.BaseSendNotificationRequest; /** * 通知发送服务接口 @@ -16,36 +16,9 @@ public interface INotificationSendService { * @param request 通知请求 * @throws com.qqchen.deploy.backend.framework.exception.BusinessException 渠道不存在、渠道已禁用、发送失败 */ - void send(NotificationRequest request); + void send(BaseSendNotificationRequest request); - /** - * 便捷方法:发送简单文本通知 - * - * @param channelId 渠道ID - * @param content 消息内容 - */ - default void sendSimple(Long channelId, String content) { - NotificationRequest request = NotificationRequest.builder() - .channelId(channelId) - .content(content) - .build(); - send(request); - } - - /** - * 便捷方法:发送带标题的通知 - * - * @param channelId 渠道ID - * @param title 标题 - * @param content 内容 - */ - default void send(Long channelId, String title, String content) { - NotificationRequest request = NotificationRequest.builder() - .channelId(channelId) - .title(title) - .content(content) - .build(); - send(request); - } + // TODO: 便捷方法需要重新设计,因为现在需要指定具体的请求类型 + // 暂时注释掉,后续根据需要添加 } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/service/impl/NotificationChannelServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/service/impl/NotificationChannelServiceImpl.java index d9badca9..40b1baf2 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/service/impl/NotificationChannelServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/service/impl/NotificationChannelServiceImpl.java @@ -12,7 +12,7 @@ import com.qqchen.deploy.backend.notification.entity.config.BaseNotificationConf import com.qqchen.deploy.backend.notification.entity.config.EmailNotificationConfig; import com.qqchen.deploy.backend.notification.dto.NotificationChannelDTO; import com.qqchen.deploy.backend.notification.dto.NotificationChannelQuery; -import com.qqchen.deploy.backend.notification.dto.NotificationRequest; +import com.qqchen.deploy.backend.notification.dto.BaseSendNotificationRequest; import com.qqchen.deploy.backend.notification.entity.config.WeworkNotificationConfig; import com.qqchen.deploy.backend.notification.entity.NotificationChannel; import com.qqchen.deploy.backend.notification.enums.NotificationChannelStatusEnum; @@ -115,8 +115,7 @@ public class NotificationChannelServiceImpl log.info("禁用通知渠道: id={}, name={}", id, channel.getName()); } - @Override - public void send(NotificationRequest request) { + public void send(BaseSendNotificationRequest request) { // 1. 参数校验 if (request == null || request.getChannelId() == null) { throw new BusinessException(ResponseCode.INVALID_PARAM); @@ -149,8 +148,8 @@ public class NotificationChannelServiceImpl // 6. 发送通知 try { - log.info("发送通知 - 渠道ID: {}, 渠道类型: {}, 标题: {}", - channel.getId(), channel.getChannelType(), request.getTitle()); + log.info("发送通知 - 渠道ID: {}, 渠道类型: {}, 内容: {}", + channel.getId(), channel.getChannelType(), request.getContent()); adapter.send(config, request); diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java index cf9e590d..12f406fc 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java @@ -1,5 +1,11 @@ package com.qqchen.deploy.backend.workflow.delegate; +import com.qqchen.deploy.backend.notification.dto.EmailSendNotificationRequest; +import com.qqchen.deploy.backend.notification.dto.WeworkSendNotificationRequest; +import com.qqchen.deploy.backend.notification.entity.NotificationChannel; +import com.qqchen.deploy.backend.notification.enums.NotificationChannelTypeEnum; +import com.qqchen.deploy.backend.notification.enums.WeworkMessageTypeEnum; +import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository; import com.qqchen.deploy.backend.notification.service.INotificationSendService; import com.qqchen.deploy.backend.workflow.dto.inputmapping.NotificationInputMapping; import com.qqchen.deploy.backend.workflow.dto.outputs.NotificationOutputs; @@ -9,6 +15,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.engine.delegate.DelegateExecution; import org.springframework.stereotype.Component; +import java.util.Arrays; import java.util.Map; /** @@ -23,6 +30,9 @@ public class NotificationNodeDelegate extends BaseNodeDelegate configs, NotificationInputMapping input) { @@ -30,6 +40,47 @@ public class NotificationNodeDelegate extends BaseNodeDelegate new RuntimeException("通知渠道不存在: " + input.getChannelId())); + + // 2. 根据渠道类型构建对应的请求对象 + switch (channel.getChannelType()) { + case WEWORK -> { + WeworkSendNotificationRequest weworkRequest = new WeworkSendNotificationRequest(); + weworkRequest.setChannelId(input.getChannelId()); + weworkRequest.setContent(buildWeworkContent(input.getTitle(), input.getContent())); + weworkRequest.setMessageType(WeworkMessageTypeEnum.TEXT); + notificationSendService.send(weworkRequest); + } + case EMAIL -> { + EmailSendNotificationRequest emailRequest = new EmailSendNotificationRequest(); + emailRequest.setChannelId(input.getChannelId()); + emailRequest.setSubject(input.getTitle()); + emailRequest.setContent(input.getContent()); + // 这里需要设置收件人,但工作流中没有提供,需要从其他地方获取 + // 暂时使用一个默认值,实际应该从工作流变量或配置中获取 + emailRequest.setToReceivers(Arrays.asList("admin@company.com")); + notificationSendService.send(emailRequest); + } + default -> throw new RuntimeException("不支持的渠道类型: " + channel.getChannelType()); + } + + log.info("工作流通知发送成功 - 渠道ID: {}, 类型: {}", input.getChannelId(), channel.getChannelType()); + } catch (Exception e) { + logError("工作流通知发送失败: " + e.getMessage()); + throw new RuntimeException("通知发送失败", e); + } + } + + /** + * 构建企业微信消息内容(标题+内容) + */ + private String buildWeworkContent(String title, String content) { + if (StringUtils.isNotEmpty(title)) { + return title + "\n\n" + content; + } + return content; } }