From fc6fc9642bb944ed6c45d391bb053985bf9dffe5 Mon Sep 17 00:00:00 2001 From: caozehui <2427765068@qq.com> Date: Thu, 9 Apr 2026 13:39:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E8=AF=95=E9=80=BB=E8=BE=91=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- msgpush-framework/msgpush-common/pom.xml | 4 +- .../enums/ServiceErrorCodeRange.java | 9 +- .../MsgpushSwaggerAutoConfiguration.java | 3 +- .../admin/blacklist/vo/BlacklistReqVO.java | 2 +- .../vo/ChannelProviderConfigReqVO.java | 2 +- .../credential/CredentialController.java | 35 +++ .../message/MessageRecordController.java | 25 ++- .../admin/message/vo/MessageRecordReqVO.java | 13 +- .../ratelimit/VO/RateLimitConfigReqVO.java | 2 +- .../retry/vo/MessageRetryQueueReqVO.java | 2 +- .../retry/vo/RetryStrategyConfigReqVO.java | 2 +- .../dal/dataobject/blacklist/BlacklistDO.java | 2 +- .../channel/ChannelProviderConfigDO.java | 2 +- .../channel/ProviderErrorCodeMappingDO.java | 2 +- .../dataobject/credential/SystemSecretDO.java | 14 ++ .../credential/dto/CredentialReqDTO.java | 35 +++ .../credential/dto/CredentialRespDTO.java | 37 +++ .../dataobject/message/MessageRecordDO.java | 3 +- .../dataobject/quota/SystemQuotaConfigDO.java | 2 +- .../ratelimit/RateLimitConfigDO.java | 2 +- .../retry/MessageRetryHistoryDO.java | 5 + .../retry/RetryStrategyConfigDO.java | 2 +- .../mysql/credential/SystemSecretMappper.java | 11 + .../module/push/enums/ChannelTypeEnum.java | 2 +- .../module/push/enums/RetrySourceEnum.java | 30 +++ .../CredentialAuthenticationFilter.java | 115 ++++++++++ .../module/push/job/MessageRetryJob.java | 8 +- .../channel/ChannelProviderConfigService.java | 2 + .../ChannelProviderConfigServiceImpl.java | 5 + .../credential/CredentialServiceImpl.java | 212 ++++++++++++++++++ .../credential/ICredentialService.java | 26 +++ .../credential/ISystemSecretService.java | 12 + .../credential/SystemSecretServiceImpl.java | 18 ++ .../service/message/MessageRecordService.java | 13 +- .../message/MessageRecordServiceImpl.java | 76 ++++--- .../retry/MessageRetryQueueServiceImpl.java | 32 ++- .../module/push/sms/MsgPushClientTest.java | 5 +- .../msgpush-module-system-api/pom.xml | 4 +- pom.xml | 7 + 39 files changed, 690 insertions(+), 93 deletions(-) create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/credential/CredentialController.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/SystemSecretDO.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialReqDTO.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialRespDTO.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/mysql/credential/SystemSecretMappper.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/RetrySourceEnum.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/filter/CredentialAuthenticationFilter.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/CredentialServiceImpl.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ICredentialService.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ISystemSecretService.java create mode 100644 msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/SystemSecretServiceImpl.java diff --git a/msgpush-framework/msgpush-common/pom.xml b/msgpush-framework/msgpush-common/pom.xml index e83a647..ee7cf08 100644 --- a/msgpush-framework/msgpush-common/pom.xml +++ b/msgpush-framework/msgpush-common/pom.xml @@ -72,7 +72,7 @@ io.swagger.core.v3 - swagger-annotations + swagger-annotations-jakarta @@ -180,4 +180,4 @@ - \ No newline at end of file + diff --git a/msgpush-framework/msgpush-common/src/main/java/com/njcn/msgpush/framework/common/exception/enums/ServiceErrorCodeRange.java b/msgpush-framework/msgpush-common/src/main/java/com/njcn/msgpush/framework/common/exception/enums/ServiceErrorCodeRange.java index 75f1158..eb831fb 100644 --- a/msgpush-framework/msgpush-common/src/main/java/com/njcn/msgpush/framework/common/exception/enums/ServiceErrorCodeRange.java +++ b/msgpush-framework/msgpush-common/src/main/java/com/njcn/msgpush/framework/common/exception/enums/ServiceErrorCodeRange.java @@ -1,5 +1,7 @@ package com.njcn.msgpush.framework.common.exception.enums; +import com.njcn.msgpush.framework.common.exception.ErrorCode; + /** * 业务异常的错误码区间,解决:解决各模块错误码定义,避免重复,在此只声明不做实际使用 * @@ -27,7 +29,7 @@ package com.njcn.msgpush.framework.common.exception.enums; * * @author hongawen */ -public class ServiceErrorCodeRange { +public interface ServiceErrorCodeRange { // 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000) // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000) @@ -43,6 +45,7 @@ public class ServiceErrorCodeRange { // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000) - // 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) - + // 模块 push 错误码区间 [1-022-000-000 ~ 1-023-000-000) + ErrorCode VALIDATE_SYSTEM_SECRET_FAIL = new ErrorCode(1-022-000-000, "校验系统密钥失败"); + ErrorCode GENERATE_CREDENTIAL_FAIL = new ErrorCode(1-022-000-001, "生成凭证失败"); } diff --git a/msgpush-framework/msgpush-spring-boot-starter-web/src/main/java/com/njcn/msgpush/framework/swagger/config/MsgpushSwaggerAutoConfiguration.java b/msgpush-framework/msgpush-spring-boot-starter-web/src/main/java/com/njcn/msgpush/framework/swagger/config/MsgpushSwaggerAutoConfiguration.java index 88a7157..0201f03 100644 --- a/msgpush-framework/msgpush-spring-boot-starter-web/src/main/java/com/njcn/msgpush/framework/swagger/config/MsgpushSwaggerAutoConfiguration.java +++ b/msgpush-framework/msgpush-spring-boot-starter-web/src/main/java/com/njcn/msgpush/framework/swagger/config/MsgpushSwaggerAutoConfiguration.java @@ -93,9 +93,10 @@ public class MsgpushSwaggerAutoConfiguration { } public static GroupedOpenApi buildGroupedOpenApi(String group, String path) { + String pathSuffix = path == null || path.isBlank() ? "" : "/" + path; return GroupedOpenApi.builder() .group(group) - .pathsToMatch("/admin-api/" + path + "/**", "/app-api/" + path + "/**") + .pathsToMatch("/admin-api" + pathSuffix + "/**", "/app-api" + pathSuffix + "/**") .addOperationCustomizer((operation, handlerMethod) -> operation .addParametersItem(buildSecurityHeaderParameter())) .addOperationCustomizer(buildOperationIdCustomizer()) diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/blacklist/vo/BlacklistReqVO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/blacklist/vo/BlacklistReqVO.java index d840377..344a2fa 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/blacklist/vo/BlacklistReqVO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/blacklist/vo/BlacklistReqVO.java @@ -19,7 +19,7 @@ public class BlacklistReqVO extends PageParam { @Schema(description = "主键 ID", example = "123444") private Long id; - @Schema(description = "渠道类型:sms/email/app_push", example = "sms") + @Schema(description = "渠道类型:sms/email/app", example = "sms") @InEnum(value = com.njcn.msgpush.module.push.enums.ChannelTypeEnum.class, message = "渠道类型必须是 {value}") private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/channel/vo/ChannelProviderConfigReqVO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/channel/vo/ChannelProviderConfigReqVO.java index 4000d42..1557858 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/channel/vo/ChannelProviderConfigReqVO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/channel/vo/ChannelProviderConfigReqVO.java @@ -13,7 +13,7 @@ public class ChannelProviderConfigReqVO extends PageParam { @Schema(description = "渠道ID") private String id; - @Schema(description = "渠道类型:sms/email/app_push", example = "sms") + @Schema(description = "渠道类型:sms/email/app", example = "sms") @InEnum(value = com.njcn.msgpush.module.push.enums.ChannelTypeEnum.class, message = "渠道类型必须是 {value}") private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/credential/CredentialController.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/credential/CredentialController.java new file mode 100644 index 0000000..5f53bb7 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/credential/CredentialController.java @@ -0,0 +1,35 @@ +package com.njcn.msgpush.module.push.controller.admin.credential; + +import com.njcn.msgpush.framework.common.pojo.CommonResult; +import com.njcn.msgpush.module.push.dal.dataobject.credential.dto.CredentialReqDTO; +import com.njcn.msgpush.module.push.dal.dataobject.credential.dto.CredentialRespDTO; +import com.njcn.msgpush.module.push.service.credential.ICredentialService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 系统凭证 API 接口 + * + * @author msgpush + */ +@Tag(name = "系统凭证管理") +@RestController +@RequestMapping("/credential") +public class CredentialController { + + @Autowired + private ICredentialService credentialService; + + @PostMapping("/generate") + @Operation(summary = "生成系统凭证") + public CommonResult generateCredential(@Valid @RequestBody CredentialReqDTO reqDTO) { + CredentialRespDTO respDTO = credentialService.generateCredential(reqDTO); + return CommonResult.success(respDTO); + } +} \ No newline at end of file diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/MessageRecordController.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/MessageRecordController.java index 5b612e8..9cf1a9e 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/MessageRecordController.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/MessageRecordController.java @@ -7,6 +7,7 @@ import com.njcn.msgpush.framework.idempotent.core.annotation.Idempotent; import com.njcn.msgpush.module.push.controller.admin.message.vo.MessageRecordReqVO; import com.njcn.msgpush.module.push.controller.admin.message.vo.MessageSendResultVO; import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO; +import com.njcn.msgpush.module.push.enums.ChannelTypeEnum; import com.njcn.msgpush.module.push.service.message.MessageRecordService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -32,11 +33,27 @@ public class MessageRecordController { private MessageRecordService messageRecordService; @PermitAll - @PostMapping("/send") - @Operation(summary = "消息推送") + @PostMapping("/send/sms") + @Operation(summary = "短信推送") @Idempotent(timeout = 2) - public CommonResult> send(@Valid @RequestBody List reqVOList) { - return success(messageRecordService.send(reqVOList)); + public CommonResult> sendSms(@Valid @RequestBody List reqVOList) { + return success(messageRecordService.send(reqVOList, ChannelTypeEnum.SMS)); + } + + @PermitAll + @PostMapping("/send/email") + @Operation(summary = "邮箱推送") + @Idempotent(timeout = 2) + public CommonResult> sendEmail(@Valid @RequestBody List reqVOList) { + return success(messageRecordService.send(reqVOList, ChannelTypeEnum.EMAIL)); + } + + @PermitAll + @PostMapping("/send/app") + @Operation(summary = "app推送") + @Idempotent(timeout = 2) + public CommonResult> sendApp(@Valid @RequestBody List reqVOList) { + return success(messageRecordService.send(reqVOList, ChannelTypeEnum.APP)); } @PostMapping("/page") diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/vo/MessageRecordReqVO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/vo/MessageRecordReqVO.java index f9737e3..a143c85 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/vo/MessageRecordReqVO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/message/vo/MessageRecordReqVO.java @@ -19,11 +19,7 @@ public class MessageRecordReqVO extends PageParam { @Schema(description = "主键ID") private Long id; - @Schema(description = "应用名称/来源系统标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "NPQS-9500") - @NotBlank(message = "应用名称/来源系统标识不能为空") - private String appName; - - @Schema(description = "渠道类型:sms/email/app_push", example = "sms") + @Schema(description = "渠道类型:sms/email/app", example = "sms") @InEnum(value = com.njcn.msgpush.module.push.enums.ChannelTypeEnum.class, message = "渠道类型必须是 {value}") private String channel; @@ -48,13 +44,6 @@ public class MessageRecordReqVO extends PageParam { @Schema(description = "模板参数") private String templateParams; - @Schema(description = "服务商类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "telecom/cmcc/aliyun/twilio") - @InEnum(value = com.njcn.msgpush.module.push.enums.ProviderTypeEnum.class, message = "服务商类型必须是 {value}") - private String providerType; - @Schema(description = "第三方消息ID") private String thirdPartyId; - - @Schema(description = "额外信息") - private String extraInfo; } diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/ratelimit/VO/RateLimitConfigReqVO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/ratelimit/VO/RateLimitConfigReqVO.java index ed84304..a87717d 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/ratelimit/VO/RateLimitConfigReqVO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/ratelimit/VO/RateLimitConfigReqVO.java @@ -18,7 +18,7 @@ public class RateLimitConfigReqVO extends PageParam { @Schema(description = "主键 ID") private Long id; - @Schema(description = "渠道类型:sms/email/app_push", example = "sms") + @Schema(description = "渠道类型:sms/email/app", example = "sms") @InEnum(value = com.njcn.msgpush.module.push.enums.ChannelTypeEnum.class, message = "渠道类型必须是 {value}") private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/MessageRetryQueueReqVO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/MessageRetryQueueReqVO.java index 992b48c..fb3647f 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/MessageRetryQueueReqVO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/MessageRetryQueueReqVO.java @@ -16,7 +16,7 @@ public class MessageRetryQueueReqVO extends PageParam { @Schema(description = "消息ID") private Long messageId; - @Schema(description = "渠道类型", example = "sms/email/app_push") + @Schema(description = "渠道类型", example = "sms/email/app") @NotBlank(message = "渠道类型不能为空") private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/RetryStrategyConfigReqVO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/RetryStrategyConfigReqVO.java index 4213874..18c599b 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/RetryStrategyConfigReqVO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/controller/admin/retry/vo/RetryStrategyConfigReqVO.java @@ -17,7 +17,7 @@ public class RetryStrategyConfigReqVO { @Schema(description = "主键 ID") private Long id; - @Schema(description = "渠道类型:sms/email/app_push", example = "sms") + @Schema(description = "渠道类型:sms/email/app", example = "sms") @InEnum(value = com.njcn.msgpush.module.push.enums.ChannelTypeEnum.class, message = "渠道类型必须是 {value}") private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/blacklist/BlacklistDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/blacklist/BlacklistDO.java index a95bb38..21d236f 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/blacklist/BlacklistDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/blacklist/BlacklistDO.java @@ -19,7 +19,7 @@ public class BlacklistDO extends BaseDO { private Long id; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ChannelProviderConfigDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ChannelProviderConfigDO.java index 2f4c7a2..9c60f4f 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ChannelProviderConfigDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ChannelProviderConfigDO.java @@ -22,7 +22,7 @@ public class ChannelProviderConfigDO extends BaseDO { private Long id; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ProviderErrorCodeMappingDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ProviderErrorCodeMappingDO.java index 903d354..9e640cc 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ProviderErrorCodeMappingDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/channel/ProviderErrorCodeMappingDO.java @@ -25,7 +25,7 @@ public class ProviderErrorCodeMappingDO extends BaseDO { private String providerType; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/SystemSecretDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/SystemSecretDO.java new file mode 100644 index 0000000..70c4a54 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/SystemSecretDO.java @@ -0,0 +1,14 @@ +package com.njcn.msgpush.module.push.dal.dataobject.credential; + +import lombok.Data; + +/** + * @author caozehui + * @data 2026-04-09 + */ +@Data +public class SystemSecretDO { + private String id; + private String systemName; + private String secret; +} diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialReqDTO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialReqDTO.java new file mode 100644 index 0000000..e140ae7 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialReqDTO.java @@ -0,0 +1,35 @@ +package com.njcn.msgpush.module.push.dal.dataobject.credential.dto; + +/** + * @author caozehui + * @data 2026-03-31 + */ + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 系统凭证请求 DTO + * + * @author msgpush + */ +@Data +public class CredentialReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 上游系统名称 + */ + @NotEmpty(message = "上游系统名称不能为空") + private String systemName; + + /** + * 密钥(用于生成凭证) + */ + @NotEmpty(message = "密钥不能为空") + private String secretKey; + +} \ No newline at end of file diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialRespDTO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialRespDTO.java new file mode 100644 index 0000000..9965c7b --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/credential/dto/CredentialRespDTO.java @@ -0,0 +1,37 @@ +package com.njcn.msgpush.module.push.dal.dataobject.credential.dto; + +/** + * @author caozehui + * @data 2026-03-31 + */ +import lombok.Data; +import lombok.experimental.Accessors; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 系统凭证响应 DTO + * + * @author msgpush + */ +@Data +public class CredentialRespDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 凭证令牌 + */ + private String credentialToken; + + /** + * 上游系统名称 + */ + private String systemName; + + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} \ No newline at end of file diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/message/MessageRecordDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/message/MessageRecordDO.java index aa15938..566bbd1 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/message/MessageRecordDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/message/MessageRecordDO.java @@ -1,5 +1,6 @@ package com.njcn.msgpush.module.push.dal.dataobject.message; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO; import lombok.Data; @@ -27,7 +28,7 @@ public class MessageRecordDO extends BaseDO { private String appName; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/quota/SystemQuotaConfigDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/quota/SystemQuotaConfigDO.java index 2cda705..a1924d1 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/quota/SystemQuotaConfigDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/quota/SystemQuotaConfigDO.java @@ -19,7 +19,7 @@ public class SystemQuotaConfigDO extends BaseDO { private Long id; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/ratelimit/RateLimitConfigDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/ratelimit/RateLimitConfigDO.java index 6649797..9ed558a 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/ratelimit/RateLimitConfigDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/ratelimit/RateLimitConfigDO.java @@ -17,7 +17,7 @@ public class RateLimitConfigDO extends BaseDO { private Long id; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/MessageRetryHistoryDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/MessageRetryHistoryDO.java index a3a1230..47d7eb0 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/MessageRetryHistoryDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/MessageRetryHistoryDO.java @@ -61,4 +61,9 @@ public class MessageRetryHistoryDO extends BaseDO { * 第三方消息ID */ private String thirdPartyId; + + /** + * 重试来源:AUTO_RETRY-系统自动重试,MANUAL_RETRY-人工手动重试 + */ + private String retrySource; } diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/RetryStrategyConfigDO.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/RetryStrategyConfigDO.java index 702081e..b24f0fd 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/RetryStrategyConfigDO.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/dataobject/retry/RetryStrategyConfigDO.java @@ -19,7 +19,7 @@ public class RetryStrategyConfigDO extends BaseDO { private Long id; /** - * 渠道类型:sms/email/app_push + * 渠道类型:sms/email/app */ private String channel; diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/mysql/credential/SystemSecretMappper.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/mysql/credential/SystemSecretMappper.java new file mode 100644 index 0000000..7df1f26 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/dal/mysql/credential/SystemSecretMappper.java @@ -0,0 +1,11 @@ +package com.njcn.msgpush.module.push.dal.mysql.credential; + +import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX; +import com.njcn.msgpush.module.push.dal.dataobject.credential.SystemSecretDO; + +/** + * @author caozehui + * @data 2026-04-09 + */ +public interface SystemSecretMappper extends BaseMapperX { +} diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/ChannelTypeEnum.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/ChannelTypeEnum.java index f2281b1..9e728e9 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/ChannelTypeEnum.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/ChannelTypeEnum.java @@ -18,7 +18,7 @@ import java.util.Arrays; public enum ChannelTypeEnum implements ArrayValuable { SMS("sms", "短信"), EMAIL("email", "邮箱"), - APP_PUSH("app_push", "APP 推送"),; + APP("app", "APP 推送"),; public static final String[] ARRAYS = Arrays.stream(values()).map(ChannelTypeEnum::getCode).toArray(String[]::new); diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/RetrySourceEnum.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/RetrySourceEnum.java new file mode 100644 index 0000000..16e20ba --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/enums/RetrySourceEnum.java @@ -0,0 +1,30 @@ +package com.njcn.msgpush.module.push.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author caozehui + * @data 2026-04-07 + */ +@Getter +@AllArgsConstructor +public enum RetrySourceEnum { + AUTO_RETRY("AUTO_RETRY", "系统自动重试"), + MANUAL_RETRY("MANUAL_RETRY", "人工手动重试"); + + private final String code; + private final String desc; + + public static RetrySourceEnum getByCode(String code) { + if (code == null) { + return null; + } + for (RetrySourceEnum value : values()) { + if (value.getCode().equals(code)) { + return value; + } + } + return null; + } +} \ No newline at end of file diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/filter/CredentialAuthenticationFilter.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/filter/CredentialAuthenticationFilter.java new file mode 100644 index 0000000..ebbdbf5 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/filter/CredentialAuthenticationFilter.java @@ -0,0 +1,115 @@ +package com.njcn.msgpush.module.push.filter; + +/** + * @author caozehui + * @data 2026-03-31 + */ + +import cn.hutool.core.util.StrUtil; +import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants; +import com.njcn.msgpush.framework.common.pojo.CommonResult; +import com.njcn.msgpush.framework.common.util.servlet.ServletUtils; +import com.njcn.msgpush.framework.web.config.WebProperties; +import com.njcn.msgpush.framework.web.core.filter.ApiRequestFilter; +import com.njcn.msgpush.module.push.service.credential.CredentialServiceImpl; +import com.njcn.msgpush.module.push.service.credential.ICredentialService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * 凭证认证过滤器 + * + * @author msgpush + */ +@Component +@Slf4j +public class CredentialAuthenticationFilter extends ApiRequestFilter implements Ordered { + + @Autowired + private ICredentialService credentialService; + + /** + * 凭证 Header 名称 + */ + private static final String CREDENTIAL_HEADER = "X-Credential-Token"; + + /** + * 需要凭证认证的接口路径 + */ + private static final String[] NOT_IGNORE_PATHS = { + "/admin-api/push/message/send/**", + }; + + private static final String[] IGNORE_PATHS = { + "/admin-api/push/credential/generate", + }; + + public CredentialAuthenticationFilter(WebProperties webProperties) { + super(webProperties); + } + + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + // 检查是否在排除列表中 + String path = request.getRequestURI().substring(request.getContextPath().length()); + for (String notExcludePath : NOT_IGNORE_PATHS) { + if (path.equals(notExcludePath)) { + return false; + } + if (notExcludePath.endsWith("/**") && path.startsWith(notExcludePath.substring(0, notExcludePath.length() - 3))) { + return false; + } + } + for (String excludePath : IGNORE_PATHS){ + if (path.equals(excludePath)) { + return true; + } + if (excludePath.endsWith("/**") && path.startsWith(excludePath.substring(0, excludePath.length() - 3))) { + return true; + } + } + + return super.shouldNotFilter(request); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // 1. 获取凭证 token + String credentialToken = request.getHeader(CREDENTIAL_HEADER); + if (StrUtil.isEmpty(credentialToken)) { + credentialToken = request.getParameter("credential"); + } + + // 2. 如果没有凭证,继续过滤链 + if (StrUtil.isEmpty(credentialToken)) { + filterChain.doFilter(request, response); + return; + } + + // 3. 验证凭证 + CredentialServiceImpl.CredentialInfo credentialInfo = credentialService.verifyCredential(credentialToken); + + if (credentialInfo == null) { + // 校验失败 + ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "凭证无效或已过期")); + return; + } + + filterChain.doFilter(request, response); + } + + + @Override + public int getOrder() { + return 1000; + } +} \ No newline at end of file diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/job/MessageRetryJob.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/job/MessageRetryJob.java index 5cab0d0..9d0436d 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/job/MessageRetryJob.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/job/MessageRetryJob.java @@ -1,5 +1,6 @@ package com.njcn.msgpush.module.push.job; +import com.njcn.msgpush.module.push.enums.ChannelTypeEnum; import com.njcn.msgpush.module.push.service.retry.MessageRetryQueueService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,8 +26,7 @@ public class MessageRetryJob { */ @Scheduled(fixedRate = 10000) public void processSmsRetryQueue() { - log.info("开始处理短信重试队列:{}", LocalDateTime.now()); - messageRetryQueueService.processRetryBatch("sms"); + messageRetryQueueService.processRetryBatch(ChannelTypeEnum.SMS.getCode()); } /** @@ -34,7 +34,7 @@ public class MessageRetryJob { */ //@Scheduled(fixedRate = 10000) public void processEmailRetryQueue() { - messageRetryQueueService.processRetryBatch("email"); + messageRetryQueueService.processRetryBatch(ChannelTypeEnum.EMAIL.getCode()); } /** @@ -42,7 +42,7 @@ public class MessageRetryJob { */ //@Scheduled(fixedRate = 10000) public void processAppPushRetryQueue() { - messageRetryQueueService.processRetryBatch("app_push"); + messageRetryQueueService.processRetryBatch(ChannelTypeEnum.APP.getCode()); } } diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigService.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigService.java index 79565f2..2a5ed25 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigService.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigService.java @@ -32,4 +32,6 @@ public interface ChannelProviderConfigService extends IService getEnabledProviders(String channel); } diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigServiceImpl.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigServiceImpl.java index 0ef88f9..9a9fb96 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigServiceImpl.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/channel/ChannelProviderConfigServiceImpl.java @@ -61,4 +61,9 @@ public class ChannelProviderConfigServiceImpl extends ServiceImpl getEnabledProviders(String channel) { + return this.lambdaQuery().eq(ChannelProviderConfigDO::getChannel, channel).list(); + } } diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/CredentialServiceImpl.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/CredentialServiceImpl.java new file mode 100644 index 0000000..ea107e8 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/CredentialServiceImpl.java @@ -0,0 +1,212 @@ +package com.njcn.msgpush.module.push.service.credential; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.AES; +import com.njcn.msgpush.framework.common.exception.ServiceException; +import com.njcn.msgpush.framework.common.exception.enums.ServiceErrorCodeRange; +import com.njcn.msgpush.framework.common.util.json.JsonUtils; +import com.njcn.msgpush.module.push.dal.dataobject.credential.SystemSecretDO; +import com.njcn.msgpush.module.push.dal.dataobject.credential.dto.CredentialReqDTO; +import com.njcn.msgpush.module.push.dal.dataobject.credential.dto.CredentialRespDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; + +/** + * @author caozehui + * @data 2026-03-31 + */ +@Service +@Slf4j +public class CredentialServiceImpl implements ICredentialService { + /** + * 凭证加密密钥(生产环境应通过配置中心或环境变量注入) + */ + private String credentialSecretKey = "88888888"; + @Autowired + private ISystemSecretService systemSecretService; + + private final StringRedisTemplate stringRedisTemplate; + + public CredentialServiceImpl(StringRedisTemplate stringRedisTemplate) { + this.stringRedisTemplate = stringRedisTemplate; + } + + + @Override + public CredentialRespDTO generateCredential(CredentialReqDTO reqDTO) { + try { + // 1. 验证系统密钥 + boolean validatedSecretRes = validateSecret(reqDTO.getSystemName(), reqDTO.getSecretKey()); + if (!validatedSecretRes) { + throw new ServiceException(ServiceErrorCodeRange.VALIDATE_SYSTEM_SECRET_FAIL); + } + + // 2. 创建凭证信息 + CredentialInfo credentialInfo = new CredentialInfo( + reqDTO.getSystemName(), + LocalDateTime.now().plusHours(24) + ); + + // 3. 生成凭证 token(加密后的 JSON) + String token = encryptCredential(credentialInfo); + + // 4. 缓存凭证信息到 Redis + cacheCredential(token, credentialInfo); + + // 5. 构建响应 + return new CredentialRespDTO() + .setCredentialToken(token) + .setSystemName(reqDTO.getSystemName()) + .setExpiresTime(credentialInfo.getExpiresTime()); + } catch (Exception e) { + throw new ServiceException(ServiceErrorCodeRange.GENERATE_CREDENTIAL_FAIL); + } + } + + public CredentialInfo verifyCredential(String token) { + if (StrUtil.isEmpty(token)) { + return null; + } + + try { + // 1. 从 Redis 获取凭证信息 + CredentialInfo credentialInfo = getCredentialFromRedis(token); + if (credentialInfo == null) { + return null; + } + + // 2. 检查是否过期 + if (credentialInfo.getExpiresTime().isBefore(LocalDateTime.now())) { + // 删除过期的凭证 + deleteCredential(token); + return null; + } + + return credentialInfo; + } catch (Exception e) { + log.warn("[verifyCredential] 验证凭证失败,token={}", token, e); + return null; + } + } + + /** + * 验证系统密钥 + * + * @param systemName 系统名称 + * @param secretKey 密钥 + */ + private boolean validateSecret(String systemName, String secretKey) { + SystemSecretDO systemSecretDO = systemSecretService.getBySystemName(systemName); + + String storedSecret = systemSecretDO.getSecret(); + if (!storedSecret.equals(secretKey)) { + return false; + } else { + return true; + } + } + + /** + * 加密凭证信息 + * + * @param info 凭证信息 + * @return 加密后的 token + */ + private String encryptCredential(CredentialInfo info) { + AES aes = SecureUtil.aes(credentialSecretKey.getBytes(StandardCharsets.UTF_8)); + String json = JsonUtils.toJsonString(info); + byte[] encrypted = aes.encrypt(json.getBytes(StandardCharsets.UTF_8)); + return Base64.encode(encrypted); + } + + /** + * 解密凭证信息 + * + * @param token 凭证 token + * @return 凭证信息 + */ + private CredentialInfo decryptCredential(String token) { + try { + AES aes = SecureUtil.aes(credentialSecretKey.getBytes(StandardCharsets.UTF_8)); + byte[] decrypted = aes.decrypt(Base64.decode(token)); + String json = new String(decrypted, StandardCharsets.UTF_8); + return JsonUtils.parseObject(json, CredentialInfo.class); + } catch (Exception e) { + log.error("[decryptCredential] 解密凭证失败", e); + return null; + } + } + + /** + * 缓存凭证信息到 Redis + * + * @param token 凭证 token + * @param info 凭证信息 + */ + private void cacheCredential(String token, CredentialInfo info) { + String redisKey = formatRedisKey(token); + String jsonValue = JsonUtils.toJsonString(info); + + // 计算剩余过期时间(秒) + long remainingSeconds = Duration.between(LocalDateTime.now(), info.getExpiresTime()).getSeconds(); + if (remainingSeconds > 0) { + stringRedisTemplate.opsForValue().set(redisKey, jsonValue, remainingSeconds, TimeUnit.SECONDS); + } + } + + /** + * 从 Redis 获取凭证信息 + * + * @param token 凭证 token + * @return 凭证信息 + */ + private CredentialInfo getCredentialFromRedis(String token) { + String redisKey = formatRedisKey(token); + String jsonValue = stringRedisTemplate.opsForValue().get(redisKey); + if (StrUtil.isEmpty(jsonValue)) { + return null; + } + return JsonUtils.parseObject(jsonValue, CredentialInfo.class); + } + + /** + * 删除凭证 + * + * @param token 凭证 token + */ + private void deleteCredential(String token) { + String redisKey = formatRedisKey(token); + stringRedisTemplate.delete(redisKey); + } + + /** + * 格式化 Redis Key + * + * @param token 凭证 token + * @return Redis Key + */ + private String formatRedisKey(String token) { + return "credential:token:" + token; + } + + /** + * 凭证信息内部类 + */ + @Data + @AllArgsConstructor + public static class CredentialInfo { + private String systemName; + private LocalDateTime expiresTime; + } +} diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ICredentialService.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ICredentialService.java new file mode 100644 index 0000000..305d722 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ICredentialService.java @@ -0,0 +1,26 @@ +package com.njcn.msgpush.module.push.service.credential; + +import com.njcn.msgpush.module.push.dal.dataobject.credential.dto.CredentialReqDTO; +import com.njcn.msgpush.module.push.dal.dataobject.credential.dto.CredentialRespDTO; + +/** + * @author caozehui + * @data 2026-03-31 + */ +public interface ICredentialService { + /** + * 生成系统凭证 + * + * @param reqDTO 请求参数 + * @return 凭证响应 + */ + CredentialRespDTO generateCredential(CredentialReqDTO reqDTO); + + /** + * 验证凭证是否有效 + * + * @param token 凭证 token + * @return 凭证信息 + */ + CredentialServiceImpl.CredentialInfo verifyCredential(String token); +} diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ISystemSecretService.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ISystemSecretService.java new file mode 100644 index 0000000..312c1e3 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/ISystemSecretService.java @@ -0,0 +1,12 @@ +package com.njcn.msgpush.module.push.service.credential; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.msgpush.module.push.dal.dataobject.credential.SystemSecretDO; + +/** + * @author caozehui + * @data 2026-04-09 + */ +public interface ISystemSecretService extends IService { + SystemSecretDO getBySystemName(String systemName); +} diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/SystemSecretServiceImpl.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/SystemSecretServiceImpl.java new file mode 100644 index 0000000..6143e09 --- /dev/null +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/credential/SystemSecretServiceImpl.java @@ -0,0 +1,18 @@ +package com.njcn.msgpush.module.push.service.credential; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.njcn.msgpush.module.push.dal.dataobject.credential.SystemSecretDO; +import com.njcn.msgpush.module.push.dal.mysql.credential.SystemSecretMappper; +import org.springframework.stereotype.Service; + +/** + * @author caozehui + * @data 2026-04-09 + */ +@Service +public class SystemSecretServiceImpl extends ServiceImpl implements ISystemSecretService { + @Override + public SystemSecretDO getBySystemName(String systemName) { + return this.lambdaQuery().eq(SystemSecretDO::getSystemName, systemName).one(); + } +} diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordService.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordService.java index f3a8331..9601363 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordService.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordService.java @@ -5,29 +5,30 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.njcn.msgpush.module.push.controller.admin.message.vo.MessageRecordReqVO; import com.njcn.msgpush.module.push.controller.admin.message.vo.MessageSendResultVO; import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO; +import com.njcn.msgpush.module.push.enums.ChannelTypeEnum; +import com.njcn.msgpush.module.push.enums.RetrySourceEnum; -import java.io.Serializable; -import java.time.LocalDateTime; -import java.util.Collection; import java.util.List; public interface MessageRecordService extends IService { /** - * 发送消息(包括email、sms、app_push) + * 发送消息(包括email、sms、app) * * @param reqVOList + * @return channelTypeEnum * @return 发送的结果 */ - List send(List reqVOList); + List send(List reqVOList, ChannelTypeEnum channelTypeEnum); /** * 处理发送消息 * * @param messageRecordDOList + * @param retrySource * @return 发送的结果 */ - List processSendMsg(List messageRecordDOList); + List processSendMsg(List messageRecordDOList, RetrySourceEnum retrySource); /** * 添加消息记录 diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordServiceImpl.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordServiceImpl.java index ae58c8d..8f903f8 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordServiceImpl.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/message/MessageRecordServiceImpl.java @@ -1,6 +1,7 @@ package com.njcn.msgpush.module.push.service.message; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -23,6 +24,7 @@ import com.njcn.msgpush.module.push.dal.redis.MessageConfirmRedisDAO; import com.njcn.msgpush.module.push.dal.redis.RateLimitRedisDAO; import com.njcn.msgpush.module.push.dal.redis.SystemQuotaRedisDAO; import com.njcn.msgpush.module.push.enums.ChannelTypeEnum; +import com.njcn.msgpush.module.push.enums.RetrySourceEnum; import com.njcn.msgpush.module.push.service.channel.ChannelProviderConfigService; import com.njcn.msgpush.module.push.service.retry.MessageRetryHistoryService; import com.njcn.msgpush.module.push.service.retry.MessageRetryQueueService; @@ -69,15 +71,16 @@ public class MessageRecordServiceImpl extends ServiceImpl send(List reqVOList) { - List messageRecordDOList = this.createMessageRecords(reqVOList); - return this.processSendMsg(messageRecordDOList); + public List send(List reqVOList, ChannelTypeEnum channelTypeEnum) { + List messageRecordDOList = this.createMessageRecords(reqVOList, channelTypeEnum); + return this.processSendMsg(messageRecordDOList, null); } @Transactional(rollbackFor = Exception.class) - public List createMessageRecords(List reqVOList) { + public List createMessageRecords(List reqVOList, ChannelTypeEnum channelTypeEnum) { List messageRecordDOList = BeanUtil.copyToList(reqVOList, MessageRecordDO.class); messageRecordDOList.forEach(messageRecordDO -> { + messageRecordDO.setChannel(channelTypeEnum.getCode()); messageRecordDO.setStatus(MsgStatusConstant.PENDING); // messageRecordDO.setRetryCount(0); }); @@ -90,12 +93,12 @@ public class MessageRecordServiceImpl extends ServiceImpl processSendMsg(List messageRecordDOList) { + public List processSendMsg(List messageRecordDOList, RetrySourceEnum retrySource) { msgPushGuardChain.checkAll(messageRecordDOList); List resultList = new ArrayList<>(); for (MessageRecordDO messageRecordDO : messageRecordDOList) { try { - MessageSendResultVO resultVO = this.processSingleMessage(messageRecordDO); + MessageSendResultVO resultVO = this.processSingleMessage(messageRecordDO, retrySource); resultList.add(resultVO); } catch (Exception e) { MessageSendResultVO failedVO = new MessageSendResultVO(); @@ -108,7 +111,7 @@ public class MessageRecordServiceImpl extends ServiceImpl enabledProviders = channelProviderConfigService.getEnabledProviders(messageRecordDO.getChannel()); + SendResult sendResult = null; + if (CollectionUtil.isNotEmpty(enabledProviders)) { + ChannelProviderConfigDO channelProviderConfigDO = enabledProviders.get(0); + MessageProviderFactory messageProviderFactory = messageProviderFactoryMap.get(channelProviderConfigDO.getProviderType()); + sendResult = this.sendMessage(messageRecordDO, channelProviderConfigDO, messageProviderFactory); + this.applySendResult(messageRecordDO, sendResult); + this.recordRetryHistory(messageRecordDO, retrySource); + } else { + sendResult = this.sendMessage(messageRecordDO, null, null); + this.applySendResult(messageRecordDO, sendResult); + } + + if (CollectionUtil.isNotEmpty(enabledProviders)) { + ChannelProviderConfigDO channelProviderConfigDO = enabledProviders.get(0); + MessageProviderFactory messageProviderFactory = messageProviderFactoryMap.get(channelProviderConfigDO.getProviderType()); + sendResult = this.validateProviderAndChannel(messageRecordDO, channelProviderConfigDO, messageProviderFactory); + + if (sendResult == null) { + sendResult = this.sendMessage(messageRecordDO, channelProviderConfigDO, messageProviderFactory); + } + this.applySendResult(messageRecordDO, sendResult); + this.recordRetryHistory(messageRecordDO, retrySource); + } else { + sendResult = this.validateProviderAndChannel(messageRecordDO, null, null); + this.applySendResult(messageRecordDO, sendResult); + } - ChannelProviderConfigDO channelProviderConfigDO = channelProviderConfigService - .getByTypeAndChannel(messageRecordDO.getProviderType(), messageRecordDO.getChannel()); - MessageProviderFactory messageProviderFactory = messageProviderFactoryMap.get(messageRecordDO.getProviderType()); - SendResult sendResult = this.sendMessage(messageRecordDO, channelProviderConfigDO, messageProviderFactory); - this.applySendResult(messageRecordDO, sendResult); - this.recordRetryHistory(messageRecordDO); // 在这里修改接口返回的是投递结果,还是调用接口结果 resultVO.setResult(this.isSuccessOutcome(sendResult.getOutcome())); @@ -132,26 +156,29 @@ public class MessageRecordServiceImpl extends ServiceImpl messageProviderFactory.createSmsSender(channelProviderConfigDO, sender).sendSms(messageRecordDO); case EMAIL -> messageProviderFactory.createEmailSender(channelProviderConfigDO, sender) .sendEmail(messageRecordDO, new HashMap<>()); - case APP_PUSH -> messageProviderFactory.createAppPushSender(channelProviderConfigDO, sender).appPush(messageRecordDO); + case APP -> messageProviderFactory.createAppPushSender(channelProviderConfigDO, sender).appPush(messageRecordDO); }; } @@ -192,11 +219,12 @@ public class MessageRecordServiceImpl extends ServiceImpl getPage(MessageRecordReqVO reqVO) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda() - .eq(StrUtil.isNotBlank(reqVO.getChannel()), MessageRecordDO::getChannel, reqVO.getChannel()) - .eq(StrUtil.isNotBlank(reqVO.getProviderType()), MessageRecordDO::getProviderType, reqVO.getProviderType()) - .eq(StrUtil.isNotBlank(reqVO.getAppName()), MessageRecordDO::getAppName, reqVO.getAppName()); + .eq(StrUtil.isNotBlank(reqVO.getChannel()), MessageRecordDO::getChannel, reqVO.getChannel()); return this.page(new Page<>(PageUtils.getPageNum(reqVO), PageUtils.getPageSize(reqVO)), wrapper); } } diff --git a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/retry/MessageRetryQueueServiceImpl.java b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/retry/MessageRetryQueueServiceImpl.java index a1af3a1..9dad9cb 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/retry/MessageRetryQueueServiceImpl.java +++ b/msgpush-module-push/msgpush-module-push-server/src/main/java/com/njcn/msgpush/module/push/service/retry/MessageRetryQueueServiceImpl.java @@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.njcn.msgpush.framework.common.pojo.PageResult; import com.njcn.msgpush.module.push.constant.MsgStatusConstant; -import com.njcn.msgpush.module.push.controller.admin.message.vo.MessageSendResultVO; import com.njcn.msgpush.module.push.controller.admin.retry.vo.MessageRetryQueueReqVO; import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO; import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO; @@ -16,6 +15,7 @@ import com.njcn.msgpush.module.push.dal.mysql.retry.MessageRetryQueueMapper; import com.njcn.msgpush.module.push.dal.redis.MessageConfirmRedisDAO; import com.njcn.msgpush.module.push.dal.redis.MessageRetryRedisDAO; import com.njcn.msgpush.module.push.enums.ChannelTypeEnum; +import com.njcn.msgpush.module.push.enums.RetrySourceEnum; import com.njcn.msgpush.module.push.service.message.MessageRecordService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -50,7 +50,7 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl CHANNELS = Arrays.asList( ChannelTypeEnum.SMS.getCode(), ChannelTypeEnum.EMAIL.getCode(), - ChannelTypeEnum.APP_PUSH.getCode()); + ChannelTypeEnum.APP.getCode()); private final AtomicBoolean startupRebuildCompleted = new AtomicBoolean(false); @@ -136,7 +136,7 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl { + case APP -> { if (retryCount == 1) { plusSeconds = 60; } else { @@ -176,7 +176,7 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl messageRecordDOList = messageRecordService.listByIds(claimedMessageIds); for (MessageRecordDO messageRecordDO : messageRecordDOList) { - processSingleRetry(messageRecordDO); + processSingleRetry(messageRecordDO, RetrySourceEnum.AUTO_RETRY); } } @@ -188,18 +188,16 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl messageRecordDOList = messageRecordService.listByIds(messageIds); for (MessageRecordDO messageRecordDO : messageRecordDOList) { - processSingleRetry(messageRecordDO); + processSingleRetry(messageRecordDO, RetrySourceEnum.MANUAL_RETRY); } } } diff --git a/msgpush-module-push/msgpush-module-push-server/src/test/java/com/njcn/msgpush/module/push/sms/MsgPushClientTest.java b/msgpush-module-push/msgpush-module-push-server/src/test/java/com/njcn/msgpush/module/push/sms/MsgPushClientTest.java index 34deb0d..02b4afd 100644 --- a/msgpush-module-push/msgpush-module-push-server/src/test/java/com/njcn/msgpush/module/push/sms/MsgPushClientTest.java +++ b/msgpush-module-push/msgpush-module-push-server/src/test/java/com/njcn/msgpush/module/push/sms/MsgPushClientTest.java @@ -21,7 +21,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.UUID; /** * @author caozehui @@ -60,14 +59,12 @@ public class MsgPushClientTest { for (int i = 0; i < 1; i++) { MessageRecordReqVO message = new MessageRecordReqVO(); message.setId(1234567890L); - message.setAppName("NPQS-9000"); message.setChannel(ChannelTypeEnum.SMS.getMsg()); message.setReceiver("18839431215"); message.setContent("【南京灿能电力】测试短信" + i + ",请忽略。" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - message.setProviderType(ProviderTypeEnum.TELECOM.getCode()); messageIdList.add(message); } - List sendResult = messageRecordService.send(messageIdList); + List sendResult = messageRecordService.send(messageIdList, ChannelTypeEnum.SMS); Thread.sleep(9000); System.out.println(JSON.toJSONString(sendResult)); diff --git a/msgpush-module-system/msgpush-module-system-api/pom.xml b/msgpush-module-system/msgpush-module-system-api/pom.xml index 9c97863..a04287d 100644 --- a/msgpush-module-system/msgpush-module-system-api/pom.xml +++ b/msgpush-module-system/msgpush-module-system-api/pom.xml @@ -24,7 +24,7 @@ io.swagger.core.v3 - swagger-annotations + swagger-annotations-jakarta @@ -43,4 +43,4 @@ - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index ab8f9ef..37460a7 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,7 @@ 1.18.42 3.5.9 + 2.2.38 1.6.3 UTF-8 @@ -119,6 +120,12 @@ msgpush-spring-boot-starter-websocket ${revision} + + + io.swagger.core.v3 + swagger-annotations-jakarta + ${swagger.jakarta.version} +