获取下行状态
This commit is contained in:
@@ -66,6 +66,12 @@ spring:
|
|||||||
uri: grayLb://infra-server
|
uri: grayLb://infra-server
|
||||||
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
|
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
|
||||||
- Path=/infra/ws/**
|
- Path=/infra/ws/**
|
||||||
|
- id: push-admin-api # 路由的编号
|
||||||
|
uri: grayLb://push-server
|
||||||
|
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
|
||||||
|
- Path=/admin-api/push/**
|
||||||
|
filters:
|
||||||
|
- RewritePath=/admin-api/push/v3/api-docs, /v3/api-docs
|
||||||
x-forwarded:
|
x-forwarded:
|
||||||
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
|
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
|
||||||
|
|
||||||
@@ -87,6 +93,9 @@ knife4j:
|
|||||||
- name: infra-server
|
- name: infra-server
|
||||||
service-name: infra-server
|
service-name: infra-server
|
||||||
url: /admin-api/infra/v3/api-docs
|
url: /admin-api/infra/v3/api-docs
|
||||||
|
- name: push-server
|
||||||
|
service-name: push-server
|
||||||
|
url: /admin-api/push/v3/api-docs
|
||||||
|
|
||||||
--- #################### 灿能相关配置 ####################
|
--- #################### 灿能相关配置 ####################
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.njcn.msgpush.module.push.client.sender.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.aliyun.dysmsapi20170525.Client;
|
import com.aliyun.dysmsapi20170525.Client;
|
||||||
|
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsRequest;
|
||||||
|
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsResponse;
|
||||||
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||||
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
||||||
import com.aliyun.teaopenapi.models.Config;
|
import com.aliyun.teaopenapi.models.Config;
|
||||||
@@ -10,14 +12,17 @@ import com.njcn.msgpush.module.push.client.sender.Sender;
|
|||||||
import com.njcn.msgpush.module.push.client.sender.SmsSender;
|
import com.njcn.msgpush.module.push.client.sender.SmsSender;
|
||||||
import com.njcn.msgpush.module.push.client.setting.impl.AliYunMailSetting;
|
import com.njcn.msgpush.module.push.client.setting.impl.AliYunMailSetting;
|
||||||
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
||||||
|
import com.njcn.msgpush.module.push.dal.dataobject.channel.ProviderErrorCodeMappingDO;
|
||||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||||
import com.njcn.msgpush.module.push.enums.RetryStrategyEnum;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.aliyun.teautil.Common.toJSONString;
|
import static com.aliyun.teautil.Common.toJSONString;
|
||||||
@@ -35,6 +40,13 @@ public class AliyunSmsSender implements SmsSender {
|
|||||||
|
|
||||||
private Client smsClient;
|
private Client smsClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存放发送完成的消息。key为其返回的mid
|
||||||
|
*/
|
||||||
|
// private Map<String, MessageRecordDO> completeSendMessageMap = new HashMap<>();
|
||||||
|
|
||||||
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
|
||||||
public AliyunSmsSender(AliYunMailSetting aliYunSmsSetting, Sender sender) {
|
public AliyunSmsSender(AliYunMailSetting aliYunSmsSetting, Sender sender) {
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
if (ObjectUtil.isNotNull(aliYunSmsSetting)) {
|
if (ObjectUtil.isNotNull(aliYunSmsSetting)) {
|
||||||
@@ -43,7 +55,6 @@ public class AliyunSmsSender implements SmsSender {
|
|||||||
.setAccessKeySecret(aliYunSmsSetting.getAccessKeySecret())
|
.setAccessKeySecret(aliYunSmsSetting.getAccessKeySecret())
|
||||||
.setRegionId(aliYunSmsSetting.getRegionId())
|
.setRegionId(aliYunSmsSetting.getRegionId())
|
||||||
.setEndpoint(aliYunSmsSetting.getEndpoint());
|
.setEndpoint(aliYunSmsSetting.getEndpoint());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.smsClient = new Client(config);
|
this.smsClient = new Client(config);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -74,11 +85,12 @@ public class AliyunSmsSender implements SmsSender {
|
|||||||
message.setCostTime((int) (end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - start));
|
message.setCostTime((int) (end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - start));
|
||||||
System.out.println(toJSONString(response));
|
System.out.println(toJSONString(response));
|
||||||
if (OK.equals(response.body.code)) {
|
if (OK.equals(response.body.code)) {
|
||||||
|
this.getDownInfo(response.body.bizId, message);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
message.setErrorCode(response.body.code);
|
message.setErrorCode(response.body.code);
|
||||||
message.setErrorMsg(response.body.message);
|
message.setErrorMsg(response.body.message);
|
||||||
this.sender.messageRetryQueueService.addRetryMessage(message, RetryStrategyEnum.EXPONENTIAL_BACKOFF);
|
this.sender.messageRetryQueueService.addRetryMessage(message);
|
||||||
this.sender.channelProviderConfigService.failureUpdate(message.getProviderType(), message.getChannel());
|
this.sender.channelProviderConfigService.failureUpdate(message.getProviderType(), message.getChannel());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -105,4 +117,42 @@ public class AliyunSmsSender implements SmsSender {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下行信息
|
||||||
|
*
|
||||||
|
* @param bizId
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
private void getDownInfo(String bizId, MessageRecordDO message) {
|
||||||
|
if (ObjectUtil.isNull(this.scheduledExecutorService)) {
|
||||||
|
this.scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
||||||
|
}
|
||||||
|
this.scheduledExecutorService.schedule(() -> {
|
||||||
|
QuerySendDetailsRequest request = new QuerySendDetailsRequest()
|
||||||
|
.setPhoneNumber(message.getReceiver())
|
||||||
|
.setBizId(bizId)
|
||||||
|
.setSendDate(message.getSendTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
|
||||||
|
.setCurrentPage(1L)
|
||||||
|
.setPageSize(10L);
|
||||||
|
try {
|
||||||
|
QuerySendDetailsResponse response = this.smsClient.querySendDetails(request);
|
||||||
|
System.out.println(toJSONString(response));
|
||||||
|
// if (response.statusCode != HttpStatus.OK.value()) {
|
||||||
|
response.body.smsSendDetailDTOs.smsSendDetailDTO.forEach(detail -> {
|
||||||
|
if (!"DELIVERED".equals(detail.errCode)) {
|
||||||
|
ProviderErrorCodeMappingDO providerErrorCodeMappingDO = this.sender.providerErrorCodeMappingService.getByProviderErrorCode(message.getProviderType(), message.getChannel(), detail.errCode);
|
||||||
|
message.setErrorCode(detail.errCode);
|
||||||
|
message.setErrorMsg(providerErrorCodeMappingDO.getOriginalMessage());
|
||||||
|
this.sender.messageRetryQueueService.addRetryMessage(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// }
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
this.scheduledExecutorService.shutdown();
|
||||||
|
this.scheduledExecutorService = null;
|
||||||
|
}, 10, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import com.njcn.msgpush.module.push.client.setting.impl.TelecomSmsSetting;
|
|||||||
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
||||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ProviderErrorCodeMappingDO;
|
import com.njcn.msgpush.module.push.dal.dataobject.channel.ProviderErrorCodeMappingDO;
|
||||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||||
import com.njcn.msgpush.module.push.enums.RetryStrategyEnum;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -39,11 +38,6 @@ public class TelecomSmsSender implements SmsSender {
|
|||||||
private TelecomSmsSetting telecomSmsSetting;
|
private TelecomSmsSetting telecomSmsSetting;
|
||||||
private Sender sender;
|
private Sender sender;
|
||||||
|
|
||||||
/**
|
|
||||||
* 存放发送完成的消息。key为其返回的mid
|
|
||||||
*/
|
|
||||||
private Map<String, MessageRecordDO> completeSendMessageMap = new HashMap<>();
|
|
||||||
|
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -118,22 +112,14 @@ public class TelecomSmsSender implements SmsSender {
|
|||||||
|
|
||||||
if (response1.getStatusCode() == HttpStatus.OK) {
|
if (response1.getStatusCode() == HttpStatus.OK) {
|
||||||
String mid = telecomSmsSendResponse.list.get(0).mid;
|
String mid = telecomSmsSendResponse.list.get(0).mid;
|
||||||
completeSendMessageMap.put(mid, message);
|
|
||||||
// 定时任务,指定时间间隔后获取下行信息
|
// 定时任务,指定时间间隔后获取下行信息
|
||||||
if (ObjectUtil.isNull(this.scheduledExecutorService)) {
|
this.getDownInfo(mid, message);
|
||||||
this.scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
|
||||||
}
|
|
||||||
this.scheduledExecutorService.schedule(() -> {
|
|
||||||
this.getDownInfo(mid);
|
|
||||||
this.scheduledExecutorService.shutdown();
|
|
||||||
this.scheduledExecutorService = null;
|
|
||||||
}, 10, TimeUnit.SECONDS);
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
ProviderErrorCodeMappingDO providerErrorCodeMappingDO = this.sender.providerErrorCodeMappingService.getByProviderErrorCode(message.getProviderType(), message.getChannel(), telecomSmsSendResponse.list.get(0).result + "");
|
ProviderErrorCodeMappingDO providerErrorCodeMappingDO = this.sender.providerErrorCodeMappingService.getByProviderErrorCode(message.getProviderType(), message.getChannel(), telecomSmsSendResponse.list.get(0).result + "");
|
||||||
message.setErrorCode(telecomSmsSendResponse.list.get(0).result + "");
|
message.setErrorCode(telecomSmsSendResponse.list.get(0).result + "");
|
||||||
message.setErrorMsg(providerErrorCodeMappingDO.getOriginalMessage());
|
message.setErrorMsg(providerErrorCodeMappingDO.getOriginalMessage());
|
||||||
this.sender.messageRetryQueueService.addRetryMessage(message, RetryStrategyEnum.EXPONENTIAL_BACKOFF);
|
this.sender.messageRetryQueueService.addRetryMessage(message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -193,15 +179,19 @@ public class TelecomSmsSender implements SmsSender {
|
|||||||
* 获取下行信息
|
* 获取下行信息
|
||||||
*
|
*
|
||||||
* @param mid
|
* @param mid
|
||||||
|
* @param message
|
||||||
*/
|
*/
|
||||||
private void getDownInfo(String mid) {
|
private void getDownInfo(String mid, MessageRecordDO message) {
|
||||||
System.out.println("getDownInfo" + LocalDateTime.now());
|
if (ObjectUtil.isNull(this.scheduledExecutorService)) {
|
||||||
|
this.scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
||||||
|
}
|
||||||
|
this.scheduledExecutorService.schedule(() -> {
|
||||||
// 构建请求参数
|
// 构建请求参数
|
||||||
Map<String, Object> request = new HashMap<>();
|
Map<String, Object> request = new HashMap<>();
|
||||||
request.put("action", "select");
|
request.put("action", "select");
|
||||||
request.put("account", telecomSmsSetting.getAccount());
|
request.put("account", telecomSmsSetting.getAccount());
|
||||||
request.put("password", telecomSmsSetting.getPassword());
|
request.put("password", telecomSmsSetting.getPassword());
|
||||||
request.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
request.put("date", message.getSendTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
||||||
request.put("condition", "APMID");
|
request.put("condition", "APMID");
|
||||||
request.put("valueList", mid);
|
request.put("valueList", mid);
|
||||||
// request.put("condition", "MOBILE");
|
// request.put("condition", "MOBILE");
|
||||||
@@ -219,19 +209,18 @@ public class TelecomSmsSender implements SmsSender {
|
|||||||
);
|
);
|
||||||
System.out.println(JSON.toJSONString(JSON.toJSONString(response)));
|
System.out.println(JSON.toJSONString(JSON.toJSONString(response)));
|
||||||
|
|
||||||
if (response.getStatusCode() == HttpStatus.OK) {
|
// if (response.getStatusCode() == HttpStatus.OK) {
|
||||||
TelecomSmsSelectResponse telecomSmsSelectResponse = JSON.parseObject(response.getBody(), TelecomSmsSelectResponse.class);
|
TelecomSmsSelectResponse telecomSmsSelectResponse = JSON.parseObject(response.getBody(), TelecomSmsSelectResponse.class);
|
||||||
TelecomSmsSelectDetailRes telecomSmsSelectDetailRes = telecomSmsSelectResponse.list.get(0);
|
TelecomSmsSelectDetailRes telecomSmsSelectDetailRes = telecomSmsSelectResponse.list.get(0);
|
||||||
if (telecomSmsSelectDetailRes.getStatus() == 4) {
|
if (telecomSmsSelectDetailRes.getStatus() == 5) {
|
||||||
return;
|
|
||||||
} else if (telecomSmsSelectDetailRes.getStatus() == 5) {
|
|
||||||
MessageRecordDO message = completeSendMessageMap.get(mid);
|
|
||||||
ProviderErrorCodeMappingDO providerErrorCodeMappingDO = this.sender.providerErrorCodeMappingService.getByProviderErrorCode(message.getProviderType(), message.getChannel(), telecomSmsSelectDetailRes.getStat());
|
ProviderErrorCodeMappingDO providerErrorCodeMappingDO = this.sender.providerErrorCodeMappingService.getByProviderErrorCode(message.getProviderType(), message.getChannel(), telecomSmsSelectDetailRes.getStat());
|
||||||
message.setErrorCode(telecomSmsSelectDetailRes.getStat());
|
message.setErrorCode(telecomSmsSelectDetailRes.getStat());
|
||||||
message.setErrorMsg(providerErrorCodeMappingDO.getOriginalMessage());
|
message.setErrorMsg(providerErrorCodeMappingDO.getOriginalMessage());
|
||||||
this.sender.messageRetryQueueService.addRetryMessage(message, RetryStrategyEnum.EXPONENTIAL_BACKOFF);
|
this.sender.messageRetryQueueService.addRetryMessage(message);
|
||||||
completeSendMessageMap.remove(mid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
|
this.scheduledExecutorService.shutdown();
|
||||||
|
this.scheduledExecutorService = null;
|
||||||
|
}, 10, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ public class ChannelProviderConfigController {
|
|||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "分页查询渠道服务商列表")
|
@Operation(summary = "分页查询渠道服务商列表")
|
||||||
@PreAuthorize("@ss.hasPermission('push:channel:page')")
|
@PreAuthorize("@ss.hasPermission('push:channel:page')")
|
||||||
@Parameter(name = "reqVO", description = "分页查询参数", required = true)
|
|
||||||
public CommonResult<Page<ChannelProviderConfigDO>> pageChannelProviderConfig(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
public CommonResult<Page<ChannelProviderConfigDO>> pageChannelProviderConfig(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
||||||
Page<ChannelProviderConfigDO> res = channelProviderConfigService.getPage(reqVO);
|
Page<ChannelProviderConfigDO> res = channelProviderConfigService.getPage(reqVO);
|
||||||
return success(res);
|
return success(res);
|
||||||
@@ -58,7 +57,6 @@ public class ChannelProviderConfigController {
|
|||||||
@PostMapping("/add")
|
@PostMapping("/add")
|
||||||
@Operation(summary = "新增渠道服务商")
|
@Operation(summary = "新增渠道服务商")
|
||||||
@PreAuthorize("@ss.hasPermission('push:channel:add')")
|
@PreAuthorize("@ss.hasPermission('push:channel:add')")
|
||||||
@Parameter(name = "reqVO", description = "新增参数", required = true)
|
|
||||||
public CommonResult<Boolean> addChannelProvider(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
public CommonResult<Boolean> addChannelProvider(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
||||||
boolean res = channelProviderConfigService.save(BeanUtil.copyProperties(reqVO, ChannelProviderConfigDO.class));
|
boolean res = channelProviderConfigService.save(BeanUtil.copyProperties(reqVO, ChannelProviderConfigDO.class));
|
||||||
return success(res);
|
return success(res);
|
||||||
@@ -67,7 +65,6 @@ public class ChannelProviderConfigController {
|
|||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
@Operation(summary = "更新渠道服务商")
|
@Operation(summary = "更新渠道服务商")
|
||||||
@PreAuthorize("@ss.hasPermission('push:channel:update')")
|
@PreAuthorize("@ss.hasPermission('push:channel:update')")
|
||||||
@Parameter(name = "reqVO", description = "更新参数", required = true)
|
|
||||||
public CommonResult<Boolean> updateChannelProvider(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
public CommonResult<Boolean> updateChannelProvider(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
||||||
boolean res = channelProviderConfigService.updateById(BeanUtil.copyProperties(reqVO, ChannelProviderConfigDO.class));
|
boolean res = channelProviderConfigService.updateById(BeanUtil.copyProperties(reqVO, ChannelProviderConfigDO.class));
|
||||||
return success(res);
|
return success(res);
|
||||||
@@ -77,7 +74,7 @@ public class ChannelProviderConfigController {
|
|||||||
@Operation(summary = "删除渠道服务商")
|
@Operation(summary = "删除渠道服务商")
|
||||||
@PreAuthorize("@ss.hasPermission('push:channel:delete')")
|
@PreAuthorize("@ss.hasPermission('push:channel:delete')")
|
||||||
@Parameter(name = "ids", description = "id列表", required = true)
|
@Parameter(name = "ids", description = "id列表", required = true)
|
||||||
public CommonResult<Boolean> deleteChannelProvider(@RequestBody List<String> ids) {
|
public CommonResult<Boolean> deleteChannelProvider(@RequestParam("ids") List<String> ids) {
|
||||||
boolean res = channelProviderConfigService.removeBatchByIds(ids);
|
boolean res = channelProviderConfigService.removeBatchByIds(ids);
|
||||||
return success(res);
|
return success(res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -36,7 +33,6 @@ public class MessageRecordController {
|
|||||||
@PermitAll
|
@PermitAll
|
||||||
@PostMapping("/send")
|
@PostMapping("/send")
|
||||||
@Operation(summary = "消息推送")
|
@Operation(summary = "消息推送")
|
||||||
@Parameter(name = "reqVOList", description = "消息列表")
|
|
||||||
public CommonResult<Boolean> send(@Valid @RequestBody List<MessageRecordReqVO> reqVOList) {
|
public CommonResult<Boolean> send(@Valid @RequestBody List<MessageRecordReqVO> reqVOList) {
|
||||||
Boolean result = messageRecordService.send(reqVOList);
|
Boolean result = messageRecordService.send(reqVOList);
|
||||||
return CommonResult.success(result);
|
return CommonResult.success(result);
|
||||||
@@ -45,7 +41,6 @@ public class MessageRecordController {
|
|||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "分页查询渠道服务商列表")
|
@Operation(summary = "分页查询渠道服务商列表")
|
||||||
@PreAuthorize("@ss.hasPermission('push:message:page')")
|
@PreAuthorize("@ss.hasPermission('push:message:page')")
|
||||||
@Parameter(name = "reqVO", description = "分页查询参数", required = true)
|
|
||||||
public CommonResult<Page<MessageRecordDO>> pageChannelProviderConfig(@Validated @RequestBody MessageRecordReqVO reqVO) {
|
public CommonResult<Page<MessageRecordDO>> pageChannelProviderConfig(@Validated @RequestBody MessageRecordReqVO reqVO) {
|
||||||
Page<MessageRecordDO> res = messageRecordService.getPage(reqVO);
|
Page<MessageRecordDO> res = messageRecordService.getPage(reqVO);
|
||||||
return success(res);
|
return success(res);
|
||||||
@@ -54,7 +49,6 @@ public class MessageRecordController {
|
|||||||
@PostMapping("/add")
|
@PostMapping("/add")
|
||||||
@Operation(summary = "添加消息记录")
|
@Operation(summary = "添加消息记录")
|
||||||
@PreAuthorize("@ss.hasPermission('push:message:add')")
|
@PreAuthorize("@ss.hasPermission('push:message:add')")
|
||||||
@Parameter(name = "reqVO", description = "添加参数", required = true)
|
|
||||||
public CommonResult<Boolean> add(@Validated @RequestBody MessageRecordReqVO reqVO) {
|
public CommonResult<Boolean> add(@Validated @RequestBody MessageRecordReqVO reqVO) {
|
||||||
return messageRecordService.add(reqVO) ? success(true) : success(false);
|
return messageRecordService.add(reqVO) ? success(true) : success(false);
|
||||||
}
|
}
|
||||||
@@ -62,7 +56,6 @@ public class MessageRecordController {
|
|||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
@Operation(summary = "更新消息记录")
|
@Operation(summary = "更新消息记录")
|
||||||
@PreAuthorize("@ss.hasPermission('push:message:update')")
|
@PreAuthorize("@ss.hasPermission('push:message:update')")
|
||||||
@Parameter(name = "reqVO", description = "更新参数", required = true)
|
|
||||||
public CommonResult<Boolean> update(@Validated @RequestBody MessageRecordReqVO reqVO) {
|
public CommonResult<Boolean> update(@Validated @RequestBody MessageRecordReqVO reqVO) {
|
||||||
return messageRecordService.update(reqVO) ? success(true) : success(false);
|
return messageRecordService.update(reqVO) ? success(true) : success(false);
|
||||||
}
|
}
|
||||||
@@ -71,7 +64,7 @@ public class MessageRecordController {
|
|||||||
@Operation(summary = "删除消息记录")
|
@Operation(summary = "删除消息记录")
|
||||||
@PreAuthorize("@ss.hasPermission('push:message:delete')")
|
@PreAuthorize("@ss.hasPermission('push:message:delete')")
|
||||||
@Parameter(name = "ids", description = "编号", required = true)
|
@Parameter(name = "ids", description = "编号", required = true)
|
||||||
public CommonResult<Boolean> delete(@RequestBody List<String> ids) {
|
public CommonResult<Boolean> delete(@RequestParam("ids") List<String> ids) {
|
||||||
return messageRecordService.delete(ids) ? success(true) : success(false);
|
return messageRecordService.delete(ids) ? success(true) : success(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package com.njcn.msgpush.module.push.controller.admin.retry;
|
|||||||
import com.njcn.msgpush.framework.common.pojo.CommonResult;
|
import com.njcn.msgpush.framework.common.pojo.CommonResult;
|
||||||
import com.njcn.msgpush.framework.common.pojo.PageResult;
|
import com.njcn.msgpush.framework.common.pojo.PageResult;
|
||||||
import com.njcn.msgpush.module.push.controller.admin.retry.vo.MessageRetryQueueReqVO;
|
import com.njcn.msgpush.module.push.controller.admin.retry.vo.MessageRetryQueueReqVO;
|
||||||
|
import com.njcn.msgpush.module.push.controller.admin.retry.vo.RetryStrategyConfigVO;
|
||||||
import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO;
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO;
|
||||||
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.RetryStrategyConfigDO;
|
||||||
import com.njcn.msgpush.module.push.service.retry.MessageRetryQueueService;
|
import com.njcn.msgpush.module.push.service.retry.MessageRetryQueueService;
|
||||||
|
import com.njcn.msgpush.module.push.service.retry.RetryStrategyConfigService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -12,10 +15,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -34,11 +34,12 @@ public class MessageRetryQueueController {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MessageRetryQueueService messageRetryQueueService;
|
private MessageRetryQueueService messageRetryQueueService;
|
||||||
|
@Autowired
|
||||||
|
private RetryStrategyConfigService retryStrategyConfigService;
|
||||||
|
|
||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "分页获得消息重试队列")
|
@Operation(summary = "分页获得消息重试队列")
|
||||||
@PreAuthorize("@ss.hasPermission('push:retry:page')")
|
@PreAuthorize("@ss.hasPermission('push:retry:page')")
|
||||||
@Parameter(name = "reqVO", description = "分页查询参数", required = true)
|
|
||||||
public CommonResult<PageResult<MessageRetryQueueDO>> getRetryPage(@Validated @RequestBody MessageRetryQueueReqVO reqVO) {
|
public CommonResult<PageResult<MessageRetryQueueDO>> getRetryPage(@Validated @RequestBody MessageRetryQueueReqVO reqVO) {
|
||||||
PageResult<MessageRetryQueueDO> result = messageRetryQueueService.getPage(reqVO);
|
PageResult<MessageRetryQueueDO> result = messageRetryQueueService.getPage(reqVO);
|
||||||
return success(result);
|
return success(result);
|
||||||
@@ -47,8 +48,31 @@ public class MessageRetryQueueController {
|
|||||||
@PostMapping("/manual")
|
@PostMapping("/manual")
|
||||||
@Operation(summary = "批量手动重试消息")
|
@Operation(summary = "批量手动重试消息")
|
||||||
@PreAuthorize("@ss.hasPermission('push:retry:manual')")
|
@PreAuthorize("@ss.hasPermission('push:retry:manual')")
|
||||||
|
@Parameter(name = "messageIds", description = "消息ID列表", required = true)
|
||||||
public CommonResult<Void> manualRetry(@RequestBody List<String> messageIds) {
|
public CommonResult<Void> manualRetry(@RequestBody List<String> messageIds) {
|
||||||
messageRetryQueueService.manualRetry(messageIds);
|
messageRetryQueueService.manualRetry(messageIds);
|
||||||
return success(null);
|
return success(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/config/list")
|
||||||
|
@Operation(summary = "获得消息重试配置列表")
|
||||||
|
@PreAuthorize("@ss.hasPermission('push:retry:config:list')")
|
||||||
|
public CommonResult<List<RetryStrategyConfigDO>> listRetryConfig() {
|
||||||
|
return success(retryStrategyConfigService.listAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/config/update")
|
||||||
|
@Operation(summary = "更新消息重试配置")
|
||||||
|
@PreAuthorize("@ss.hasPermission('push:retry:config:update')")
|
||||||
|
public CommonResult<Boolean> updateRetryConfig(@Validated @RequestBody RetryStrategyConfigVO retryStrategyConfigVO) {
|
||||||
|
return success(retryStrategyConfigService.updateStrategyConfig(retryStrategyConfigVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/config/toggle")
|
||||||
|
@Operation(summary = "启用/禁用重试策略配置")
|
||||||
|
@PreAuthorize("@ss.hasPermission('push:retry:config:toggle')")
|
||||||
|
@Parameter(name = "id", description = "id", required = true)
|
||||||
|
public CommonResult<Boolean> toggleRetryConfig(@RequestParam("id") String id) {
|
||||||
|
return success(retryStrategyConfigService.toggleEnableField(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.njcn.msgpush.module.push.controller.admin.retry.vo;
|
|||||||
import com.njcn.msgpush.framework.common.pojo.PageParam;
|
import com.njcn.msgpush.framework.common.pojo.PageParam;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,9 +17,11 @@ public class MessageRetryQueueReqVO extends PageParam {
|
|||||||
private String messageId;
|
private String messageId;
|
||||||
|
|
||||||
@Schema(description = "渠道类型", example = "sms/email/app_push")
|
@Schema(description = "渠道类型", example = "sms/email/app_push")
|
||||||
|
@NotBlank(message = "渠道类型不能为空")
|
||||||
private String channel;
|
private String channel;
|
||||||
|
|
||||||
@Schema(description = "接收者", example = "10086")
|
@Schema(description = "接收者", example = "10086")
|
||||||
|
@NotBlank(message = "接收者不能为空")
|
||||||
private String receiver;
|
private String receiver;
|
||||||
|
|
||||||
@Schema(description = "最小重试次数")
|
@Schema(description = "最小重试次数")
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.njcn.msgpush.module.push.controller.admin.retry.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author caozehui
|
||||||
|
* @data 2026-03-05
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "管理后台 - 重试策略配置 Request VO")
|
||||||
|
public class RetryStrategyConfigVO {
|
||||||
|
/**
|
||||||
|
* 主键 ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "主键 ID")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 渠道类型:sms/email/app_push
|
||||||
|
*/
|
||||||
|
@Schema(description = "渠道类型:sms/email/app_push")
|
||||||
|
@NotBlank(message = "渠道类型不能为空")
|
||||||
|
private String channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大重试次数
|
||||||
|
*/
|
||||||
|
@Schema(description = "最大重试次数")
|
||||||
|
@NotNull(message = "最大重试次数不能为空")
|
||||||
|
private Integer maxRetryCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试间隔(秒),逗号分隔,如:300,600,1800
|
||||||
|
*/
|
||||||
|
@Schema(description = "重试间隔(秒),逗号分隔,如:300,600,1800")
|
||||||
|
@NotBlank(message = "重试间隔不能为空")
|
||||||
|
private String retryIntervals;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.njcn.msgpush.module.push.dal.dataobject.retry;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author caozehui
|
||||||
|
* @data 2026-03-05
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("push_retry_strategy_config")
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class RetryStrategyConfigDO extends BaseDO {
|
||||||
|
/**
|
||||||
|
* 主键 ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道类型:sms/email/app_push
|
||||||
|
*/
|
||||||
|
private String channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大重试次数
|
||||||
|
*/
|
||||||
|
private Integer maxRetryCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试间隔(秒),逗号分隔,如:300,600,1800
|
||||||
|
*/
|
||||||
|
private String retryIntervals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-否 1-是
|
||||||
|
*/
|
||||||
|
private Integer enabled;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.njcn.msgpush.module.push.dal.mysql.retry;
|
||||||
|
|
||||||
|
import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.RetryStrategyConfigDO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author caozehui
|
||||||
|
* @data 2026-03-05
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface RetryStrategyConfigMapper extends BaseMapperX<RetryStrategyConfigDO> {
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package com.njcn.msgpush.module.push.enums;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author caozehui
|
|
||||||
* @date 2026-02-27
|
|
||||||
* @description 消息重试策略枚举
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum RetryStrategyEnum {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 固定间隔重试
|
|
||||||
*/
|
|
||||||
FIXED_INTERVAL(1, "固定间隔重试") {
|
|
||||||
@Override
|
|
||||||
public LocalDateTime calculateNextRetryTime(int retryCount, int intervalSeconds) {
|
|
||||||
return LocalDateTime.now().plusSeconds(intervalSeconds);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指数退避重试
|
|
||||||
*/
|
|
||||||
EXPONENTIAL_BACKOFF(2, "指数退避重试") {
|
|
||||||
@Override
|
|
||||||
public LocalDateTime calculateNextRetryTime(int retryCount, int baseIntervalSeconds) {
|
|
||||||
// 基础间隔 * 2^重试次数,最大不超过1小时
|
|
||||||
long delay = Math.min(baseIntervalSeconds * (1L << retryCount), 3600);
|
|
||||||
return LocalDateTime.now().plusSeconds(delay);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义时间重试
|
|
||||||
*/
|
|
||||||
// CUSTOM(3, "自定义时间重试") {
|
|
||||||
// @Override
|
|
||||||
// public LocalDateTime calculateNextRetryTime(int retryCount, int unused) {
|
|
||||||
//
|
|
||||||
// return LocalDateTime.now().plusMinutes(5 * retryCount);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
private Integer code;
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算下次重试时间
|
|
||||||
*
|
|
||||||
* @param retryCount 当前重试次数
|
|
||||||
* @param param 参数(根据策略不同含义不同)
|
|
||||||
* @return 下次重试时间
|
|
||||||
*/
|
|
||||||
public abstract LocalDateTime calculateNextRetryTime(int retryCount, int param);
|
|
||||||
|
|
||||||
public static RetryStrategyEnum fromCode(Integer code) {
|
|
||||||
for (RetryStrategyEnum strategy : values()) {
|
|
||||||
if (strategy.getCode().equals(code)) {
|
|
||||||
return strategy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FIXED_INTERVAL; // 默认返回固定间隔
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import com.njcn.msgpush.framework.common.pojo.PageResult;
|
|||||||
import com.njcn.msgpush.module.push.controller.admin.retry.vo.MessageRetryQueueReqVO;
|
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.message.MessageRecordDO;
|
||||||
import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO;
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO;
|
||||||
import com.njcn.msgpush.module.push.enums.RetryStrategyEnum;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -13,9 +12,8 @@ public interface MessageRetryQueueService {
|
|||||||
* 添加消息到重试队列
|
* 添加消息到重试队列
|
||||||
*
|
*
|
||||||
* @param message 消息
|
* @param message 消息
|
||||||
* @param strategy 重试策略
|
|
||||||
*/
|
*/
|
||||||
void addRetryMessage(MessageRecordDO message, RetryStrategyEnum strategy);
|
void addRetryMessage(MessageRecordDO message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量处理重试消息
|
* 批量处理重试消息
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ package com.njcn.msgpush.module.push.service.retry;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.njcn.msgpush.framework.common.pojo.PageResult;
|
import com.njcn.msgpush.framework.common.pojo.PageResult;
|
||||||
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
||||||
|
import com.njcn.msgpush.module.push.constant.MsgPushConstant;
|
||||||
import com.njcn.msgpush.module.push.controller.admin.retry.vo.MessageRetryQueueReqVO;
|
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.message.MessageRecordDO;
|
||||||
import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO;
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.MessageRetryQueueDO;
|
||||||
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.RetryStrategyConfigDO;
|
||||||
import com.njcn.msgpush.module.push.dal.mysql.retry.MessageRetryQueueMapper;
|
import com.njcn.msgpush.module.push.dal.mysql.retry.MessageRetryQueueMapper;
|
||||||
import com.njcn.msgpush.module.push.dal.redis.MessageRetryRedisDAO;
|
import com.njcn.msgpush.module.push.dal.redis.MessageRetryRedisDAO;
|
||||||
import com.njcn.msgpush.module.push.enums.RetryStrategyEnum;
|
|
||||||
import com.njcn.msgpush.module.push.service.channel.ChannelProviderConfigService;
|
import com.njcn.msgpush.module.push.service.channel.ChannelProviderConfigService;
|
||||||
import com.njcn.msgpush.module.push.service.message.MessageRecordService;
|
import com.njcn.msgpush.module.push.service.message.MessageRecordService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -35,6 +37,8 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl<MessageRetryQueueM
|
|||||||
private MessageRecordService messageRecordService;
|
private MessageRecordService messageRecordService;
|
||||||
@Autowired
|
@Autowired
|
||||||
public ChannelProviderConfigService channelProviderConfigService;
|
public ChannelProviderConfigService channelProviderConfigService;
|
||||||
|
@Autowired
|
||||||
|
public RetryStrategyConfigService retryStrategyConfigService;
|
||||||
|
|
||||||
public final ThreadPoolExecutor MSG_RETRY_THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
|
public final ThreadPoolExecutor MSG_RETRY_THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
|
||||||
5,
|
5,
|
||||||
@@ -68,7 +72,7 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl<MessageRetryQueueM
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void addRetryMessage(MessageRecordDO message, RetryStrategyEnum strategy) {
|
public void addRetryMessage(MessageRecordDO message) {
|
||||||
// 检查是否已存在重试记录
|
// 检查是否已存在重试记录
|
||||||
MessageRetryQueueDO existing = super.baseMapper.selectOne(
|
MessageRetryQueueDO existing = super.baseMapper.selectOne(
|
||||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MessageRetryQueueDO>()
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MessageRetryQueueDO>()
|
||||||
@@ -88,7 +92,7 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl<MessageRetryQueueM
|
|||||||
retryRecord.setLastErrorMsg(message.getErrorMsg());
|
retryRecord.setLastErrorMsg(message.getErrorMsg());
|
||||||
|
|
||||||
// 计算下次重试时间
|
// 计算下次重试时间
|
||||||
LocalDateTime nextRetryTime = strategy.calculateNextRetryTime(0, DEFAULT_RETRY_INTERVAL);
|
LocalDateTime nextRetryTime = this.calculateNextRetryTime(message.getChannel(), 1);
|
||||||
retryRecord.setNextRetryTime(nextRetryTime);
|
retryRecord.setNextRetryTime(nextRetryTime);
|
||||||
|
|
||||||
super.baseMapper.insert(retryRecord);
|
super.baseMapper.insert(retryRecord);
|
||||||
@@ -98,6 +102,66 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl<MessageRetryQueueM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算下次重试时间
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* @param retryCount
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private LocalDateTime calculateNextRetryTime(String channel, int retryCount) {
|
||||||
|
RetryStrategyConfigDO strategyConfig = retryStrategyConfigService.getStrategyConfig(channel);
|
||||||
|
long plusSeconds = 0;
|
||||||
|
if (ObjectUtil.isNull(strategyConfig)) {
|
||||||
|
// 默认策略
|
||||||
|
switch (channel) {
|
||||||
|
case MsgPushConstant.CHANNEL_SMS: {
|
||||||
|
if (retryCount == 1) {
|
||||||
|
plusSeconds = 60 * 5;
|
||||||
|
} else if (retryCount == 2) {
|
||||||
|
plusSeconds = 60 * 10;
|
||||||
|
} else {
|
||||||
|
plusSeconds = 60 * 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MsgPushConstant.CHANNEL_EMAIL: {
|
||||||
|
if (retryCount == 1) {
|
||||||
|
plusSeconds = 60 * 10;
|
||||||
|
} else if (retryCount == 2) {
|
||||||
|
plusSeconds = 60 * 30;
|
||||||
|
} else if (retryCount == 3) {
|
||||||
|
plusSeconds = 60 * 60;
|
||||||
|
} else if (retryCount == 4) {
|
||||||
|
plusSeconds = 60 * 60 * 2;
|
||||||
|
} else {
|
||||||
|
plusSeconds = 60 * 60 * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MsgPushConstant.CHANNEL_APP_PUSH: {
|
||||||
|
if (retryCount == 1) {
|
||||||
|
plusSeconds = 60 * 1;
|
||||||
|
} else {
|
||||||
|
plusSeconds = 60 * 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
plusSeconds = 60 * 5;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String retryIntervals = strategyConfig.getRetryIntervals();
|
||||||
|
String[] split = retryIntervals.split(String.valueOf(StrUtil.C_COMMA));
|
||||||
|
if (retryCount >= split.length) {
|
||||||
|
plusSeconds = Long.parseLong(split[split.length - 1]);
|
||||||
|
} else {
|
||||||
|
plusSeconds = Long.parseLong(split[retryCount - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LocalDateTime.now().plusSeconds(plusSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processRetryBatch(String channel) {
|
public void processRetryBatch(String channel) {
|
||||||
// 从数据库查询需要重试的消息
|
// 从数据库查询需要重试的消息
|
||||||
@@ -224,8 +288,7 @@ public class MessageRetryQueueServiceImpl extends ServiceImpl<MessageRetryQueueM
|
|||||||
messageRetryRedisDAO.removeFromRetryQueue(messageRecordDO.getChannel(), messageRecordDO.getMessageId());
|
messageRetryRedisDAO.removeFromRetryQueue(messageRecordDO.getChannel(), messageRecordDO.getMessageId());
|
||||||
} else {
|
} else {
|
||||||
// 还可以继续重试,更新重试信息
|
// 还可以继续重试,更新重试信息
|
||||||
RetryStrategyEnum strategy = RetryStrategyEnum.EXPONENTIAL_BACKOFF;
|
LocalDateTime nextRetryTime = this.calculateNextRetryTime(messageRecordDO.getChannel(), newRetryCount);
|
||||||
LocalDateTime nextRetryTime = strategy.calculateNextRetryTime(newRetryCount, DEFAULT_RETRY_INTERVAL);
|
|
||||||
|
|
||||||
super.baseMapper.updateRetryInfo(
|
super.baseMapper.updateRetryInfo(
|
||||||
messageRecordDO.getMessageId(),
|
messageRecordDO.getMessageId(),
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.njcn.msgpush.module.push.service.retry;
|
||||||
|
|
||||||
|
|
||||||
|
import com.njcn.msgpush.module.push.controller.admin.retry.vo.RetryStrategyConfigVO;
|
||||||
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.RetryStrategyConfigDO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author caozehui
|
||||||
|
* @data 2026-03-05
|
||||||
|
*/
|
||||||
|
public interface RetryStrategyConfigService {
|
||||||
|
/**
|
||||||
|
* 获得指定渠道的重试策略
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
RetryStrategyConfigDO getStrategyConfig(String channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得所有重试策略
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<RetryStrategyConfigDO> listAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改重试策略配置
|
||||||
|
*
|
||||||
|
* @param strategyConfigVO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean updateStrategyConfig(RetryStrategyConfigVO strategyConfigVO);
|
||||||
|
|
||||||
|
boolean toggleEnableField(String id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.njcn.msgpush.module.push.service.retry;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.njcn.msgpush.module.push.controller.admin.retry.vo.RetryStrategyConfigVO;
|
||||||
|
import com.njcn.msgpush.module.push.dal.dataobject.retry.RetryStrategyConfigDO;
|
||||||
|
import com.njcn.msgpush.module.push.dal.mysql.retry.RetryStrategyConfigMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author caozehui
|
||||||
|
* @data 2026-03-05
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class RetryStrategyConfigServiceImpl extends ServiceImpl<RetryStrategyConfigMapper, RetryStrategyConfigDO> implements RetryStrategyConfigService {
|
||||||
|
@Override
|
||||||
|
public RetryStrategyConfigDO getStrategyConfig(String channel) {
|
||||||
|
return this.lambdaQuery().eq(RetryStrategyConfigDO::getChannel, channel)
|
||||||
|
.eq(RetryStrategyConfigDO::getDeleted, false)
|
||||||
|
.eq(RetryStrategyConfigDO::getEnabled, true)
|
||||||
|
.one();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RetryStrategyConfigDO> listAll() {
|
||||||
|
return this.lambdaQuery()
|
||||||
|
.eq(RetryStrategyConfigDO::getDeleted, false)
|
||||||
|
.eq(RetryStrategyConfigDO::getEnabled, true)
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateStrategyConfig(RetryStrategyConfigVO strategyConfigVO) {
|
||||||
|
RetryStrategyConfigDO retryStrategyConfigDO = BeanUtil.copyProperties(strategyConfigVO, RetryStrategyConfigDO.class);
|
||||||
|
return this.updateById(retryStrategyConfigDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean toggleEnableField(String id) {
|
||||||
|
RetryStrategyConfigDO retryStrategyConfigDO = this.getById(id);
|
||||||
|
retryStrategyConfigDO.setEnabled(retryStrategyConfigDO.getEnabled() ^ 0X0001);
|
||||||
|
return this.updateById(retryStrategyConfigDO);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user