短信推送demo
This commit is contained in:
@@ -35,19 +35,19 @@ public class ClientConfiguration {
|
||||
|
||||
for (ChannelProviderConfigDO config : activeProviders) {
|
||||
switch (config.getProviderType()) {
|
||||
case MsgPushConstant.ALI_YUN: {
|
||||
MessageProviderFactory orDefault = messageProviderFactoryMap.getOrDefault(MsgPushConstant.ALI_YUN, new AliyunProviderFactory());
|
||||
messageProviderFactoryMap.put(MsgPushConstant.ALI_YUN, orDefault);
|
||||
case MsgPushConstant.PROVIDER_TYPE_ALI_YUN: {
|
||||
MessageProviderFactory orDefault = messageProviderFactoryMap.getOrDefault(MsgPushConstant.PROVIDER_TYPE_ALI_YUN, new AliyunProviderFactory());
|
||||
messageProviderFactoryMap.put(MsgPushConstant.PROVIDER_TYPE_ALI_YUN, orDefault);
|
||||
}
|
||||
break;
|
||||
case MsgPushConstant.TELECOM: {
|
||||
MessageProviderFactory orDefault = messageProviderFactoryMap.getOrDefault(MsgPushConstant.TELECOM, new AliyunProviderFactory());
|
||||
messageProviderFactoryMap.put(MsgPushConstant.TELECOM, orDefault);
|
||||
case MsgPushConstant.PROVIDER_TYPE_TELECOM: {
|
||||
MessageProviderFactory orDefault = messageProviderFactoryMap.getOrDefault(MsgPushConstant.PROVIDER_TYPE_TELECOM, new AliyunProviderFactory());
|
||||
messageProviderFactoryMap.put(MsgPushConstant.PROVIDER_TYPE_TELECOM, orDefault);
|
||||
}
|
||||
break;
|
||||
case MsgPushConstant.UNI_PUSH: {
|
||||
MessageProviderFactory orDefault = messageProviderFactoryMap.getOrDefault(MsgPushConstant.UNI_PUSH, new AliyunProviderFactory());
|
||||
messageProviderFactoryMap.put(MsgPushConstant.UNI_PUSH, orDefault);
|
||||
case MsgPushConstant.PROVIDER_TYPE_UNI_PUSH: {
|
||||
MessageProviderFactory orDefault = messageProviderFactoryMap.getOrDefault(MsgPushConstant.PROVIDER_TYPE_UNI_PUSH, new AliyunProviderFactory());
|
||||
messageProviderFactoryMap.put(MsgPushConstant.PROVIDER_TYPE_UNI_PUSH, orDefault);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface MessageProviderFactory {
|
||||
/**
|
||||
* 创建短信发送器
|
||||
*/
|
||||
SmsSender createSmsSender(ChannelProviderConfigDO config) throws Exception;
|
||||
SmsSender createSmsSender(ChannelProviderConfigDO config);
|
||||
|
||||
/**
|
||||
* 创建邮件发送器
|
||||
@@ -23,5 +23,5 @@ public interface MessageProviderFactory {
|
||||
/**
|
||||
* 创建APP推送发送器
|
||||
*/
|
||||
AppPushSender createAppPushSender();
|
||||
AppPushSender createAppPushSender(ChannelProviderConfigDO config);
|
||||
}
|
||||
|
||||
@@ -8,14 +8,16 @@ import com.njcn.msgpush.module.push.client.sender.impl.AliyunEmailSender;
|
||||
import com.njcn.msgpush.module.push.client.sender.impl.AliyunSmsSender;
|
||||
import com.njcn.msgpush.module.push.client.setting.impl.AliYunMailSetting;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-02-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class AliyunProviderFactory implements MessageProviderFactory {
|
||||
@Override
|
||||
public SmsSender createSmsSender(ChannelProviderConfigDO config) throws Exception {
|
||||
public SmsSender createSmsSender(ChannelProviderConfigDO config) {
|
||||
AliYunMailSetting aliYunSmsSetting = AliYunMailSetting.builder()
|
||||
.accessKeyId(config.getAppKey())
|
||||
.accessKeySecret(config.getAppSecret())
|
||||
@@ -38,7 +40,8 @@ public class AliyunProviderFactory implements MessageProviderFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppPushSender createAppPushSender() {
|
||||
public AppPushSender createAppPushSender(ChannelProviderConfigDO config) {
|
||||
log.error("阿里云暂不支持app推送");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ import com.njcn.msgpush.module.push.client.sender.impl.TelecomSmsSender;
|
||||
import com.njcn.msgpush.module.push.client.setting.impl.TelecomSmsSetting;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import com.njcn.msgpush.module.push.util.RestTemplateUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-02-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class TelecomProviderFactory implements MessageProviderFactory {
|
||||
private RestTemplateUtil restTemplateUtil;
|
||||
|
||||
@@ -21,7 +23,7 @@ public class TelecomProviderFactory implements MessageProviderFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsSender createSmsSender(ChannelProviderConfigDO config) throws Exception {
|
||||
public SmsSender createSmsSender(ChannelProviderConfigDO config) {
|
||||
TelecomSmsSetting telecomSmsSetting = TelecomSmsSetting.builder()
|
||||
.account(config.getAppKey())
|
||||
.password(config.getAppSecret())
|
||||
@@ -35,7 +37,8 @@ public class TelecomProviderFactory implements MessageProviderFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppPushSender createAppPushSender() {
|
||||
public AppPushSender createAppPushSender(ChannelProviderConfigDO config) {
|
||||
log.error("电信暂不支持app推送");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,29 @@ import com.njcn.msgpush.module.push.client.sender.AppPushSender;
|
||||
import com.njcn.msgpush.module.push.client.sender.EmailSender;
|
||||
import com.njcn.msgpush.module.push.client.sender.SmsSender;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-02-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class UniPushProviderFactory implements MessageProviderFactory {
|
||||
@Override
|
||||
public SmsSender createSmsSender(ChannelProviderConfigDO config) throws Exception {
|
||||
public SmsSender createSmsSender(ChannelProviderConfigDO config) {
|
||||
log.error("uniPush暂不支持短信推送");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailSender createEmailSender(ChannelProviderConfigDO config) throws Exception {
|
||||
log.error("uniPush暂不支持email推送");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppPushSender createAppPushSender() {
|
||||
public AppPushSender createAppPushSender(ChannelProviderConfigDO config) {
|
||||
log.error("阿里云暂不支持app推送");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.njcn.msgpush.module.push.client.sender;
|
||||
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-02-11
|
||||
*/
|
||||
public interface AppPushSender {
|
||||
|
||||
boolean appPush();
|
||||
boolean appPush(MessageRecordDO message);
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ public interface EmailSender {
|
||||
* @param toAddressList 接收地址集合
|
||||
* @return 发送结果
|
||||
*/
|
||||
boolean sendEmail(Map<String, Object> params, List<String> toAddressList) throws Exception;
|
||||
boolean sendEmail(Map<String, Object> params, List<String> toAddressList);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.njcn.msgpush.module.push.client.sender;
|
||||
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -11,18 +13,16 @@ public interface SmsSender {
|
||||
/**
|
||||
* 向单个手机号发送短信
|
||||
*
|
||||
* @param phoneNumber 手机号
|
||||
* @param params 参数
|
||||
* @param message 消息
|
||||
* @return 发送结果
|
||||
*/
|
||||
boolean sendSms(Map<String, Object> params, String phoneNumber) throws Exception;
|
||||
boolean sendSms(MessageRecordDO message);
|
||||
|
||||
/**
|
||||
* 向多个手机号发送短信
|
||||
*
|
||||
* @param phoneNumbers 手机号集合
|
||||
* @param params 参数
|
||||
* @param messageList 消息集合
|
||||
* @return 发送结果
|
||||
*/
|
||||
boolean sendBatchSms(Map<String, Object> params, List<String> phoneNumbers);
|
||||
boolean sendBatchSms(List<MessageRecordDO> messageList);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class AliyunEmailSender implements EmailSender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendEmail(Map<String, Object> params, List<String> toAddressList) throws Exception {
|
||||
public boolean sendEmail(Map<String, Object> params, List<String> toAddressList) {
|
||||
RuntimeOptions runtimeOptions = new RuntimeOptions();
|
||||
runtimeOptions.autoretry = true;
|
||||
SingleSendMailRequest request = new SingleSendMailRequest()
|
||||
@@ -75,7 +75,11 @@ public class AliyunEmailSender implements EmailSender {
|
||||
.setHtmlBody(params.get(HTML_BODY).toString())
|
||||
.setTextBody(params.get(TEXT_BODY).toString());
|
||||
|
||||
SingleSendMailResponse response = this.emailClient.singleSendMailWithOptions(request, runtimeOptions);
|
||||
try {
|
||||
SingleSendMailResponse response = this.emailClient.singleSendMailWithOptions(request, runtimeOptions);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// if(OK.equals(response.)){
|
||||
// return true;
|
||||
// }else {
|
||||
|
||||
@@ -2,18 +2,19 @@ package com.njcn.msgpush.module.push.client.sender.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import com.aliyun.dysmsapi20170525.models.SendBatchSmsRequest;
|
||||
import com.aliyun.dysmsapi20170525.models.SendBatchSmsResponse;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
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.constant.MessageStatusConstant;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -41,14 +42,14 @@ public class AliyunSmsSender implements SmsSender {
|
||||
new java.util.concurrent.ArrayBlockingQueue<>(1000),
|
||||
r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName("AliYunSmsClient-Pool-" + thread.getId());
|
||||
thread.setName("AliyunSmsSender-Pool-" + thread.getId());
|
||||
thread.setDaemon(false);
|
||||
return thread;
|
||||
},
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
);
|
||||
|
||||
public AliyunSmsSender(AliYunMailSetting aliYunSmsSetting) throws Exception {
|
||||
public AliyunSmsSender(AliYunMailSetting aliYunSmsSetting) {
|
||||
if (ObjectUtil.isNotNull(aliYunSmsSetting)) {
|
||||
Config config = new Config()
|
||||
.setAccessKeyId(aliYunSmsSetting.getAccessKeyId())
|
||||
@@ -60,27 +61,31 @@ public class AliyunSmsSender implements SmsSender {
|
||||
this.smsClient = new Client(config);
|
||||
} catch (Exception e) {
|
||||
log.error("阿里云-短信服务初始化失败,请检查配置信息");
|
||||
throw e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean sendSms(Map<String, Object> params, String phoneNumber) throws Exception {
|
||||
public boolean sendSms(MessageRecordDO message) {
|
||||
Future<Boolean> future = THREAD_POOL_EXECUTOR.submit(() -> {
|
||||
// todo 修改消息的状态为 sending
|
||||
|
||||
message.setStatus(MessageStatusConstant.SENDING);
|
||||
RuntimeOptions runtimeOptions = new RuntimeOptions();
|
||||
// 设置自动重试,默认是不开启的。重试次数默认是3次
|
||||
runtimeOptions.autoretry = true;
|
||||
SendSmsRequest request = new SendSmsRequest()
|
||||
.setPhoneNumbers(phoneNumber)
|
||||
.setSignName(params.get(SIGN_NAME).toString())
|
||||
.setTemplateCode(params.get(TEMPLATE_CODE).toString())
|
||||
.setTemplateParam(params.get(TEMPLATE_PARAM).toString());
|
||||
.setPhoneNumbers(message.getReceiver())
|
||||
.setSignName(message.getTitle())
|
||||
.setTemplateCode(message.getTemplateCode())
|
||||
.setTemplateParam(message.getTemplateParams());
|
||||
try {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long start = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
message.setSendTime(now);
|
||||
SendSmsResponse response = this.smsClient.sendSmsWithOptions(request, runtimeOptions);
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
message.setCostTime((int) (end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - start));
|
||||
System.out.println(toJSONString(response));
|
||||
if (OK.equals(response.body.code)) {
|
||||
return true;
|
||||
@@ -92,36 +97,27 @@ public class AliyunSmsSender implements SmsSender {
|
||||
throw new Exception(e);
|
||||
}
|
||||
});
|
||||
Boolean b = future.get(3, TimeUnit.SECONDS);
|
||||
Boolean b = null;
|
||||
try {
|
||||
b = future.get(3, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (b) {
|
||||
// todo 修改消息的状态为 success
|
||||
message.setStatus(MessageStatusConstant.SUCCESS);
|
||||
} else {
|
||||
// todo 修改消息的状态为 failed
|
||||
message.setStatus(MessageStatusConstant.FAILED);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendBatchSms(Map<String, Object> params, List<String> phoneNumbers) {
|
||||
RuntimeOptions runtimeOptions = new RuntimeOptions();
|
||||
// 设置自动重试,默认是不开启的。重试次数默认是3次
|
||||
runtimeOptions.autoretry = true;
|
||||
SendBatchSmsRequest request = new SendBatchSmsRequest()
|
||||
.setPhoneNumberJson(toJSONString(phoneNumbers))
|
||||
.setSignNameJson(toJSONString(params.get(SIGN_NAME)))
|
||||
.setTemplateCode(params.get(TEMPLATE_CODE).toString())
|
||||
.setTemplateParamJson(toJSONString(params.get(TEMPLATE_PARAM)));
|
||||
try {
|
||||
SendBatchSmsResponse response = this.smsClient.sendBatchSmsWithOptions(request, runtimeOptions);
|
||||
System.out.println(toJSONString(response));
|
||||
if (OK.equals(response.body.code)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("阿里云-短信服务发送失败");
|
||||
throw new RuntimeException(e);
|
||||
public boolean sendBatchSms(List<MessageRecordDO> messageIdList) {
|
||||
boolean res = true;
|
||||
for (MessageRecordDO message : messageIdList) {
|
||||
res &= this.sendSms(message);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package com.njcn.msgpush.module.push.client.sender.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.msgpush.module.push.client.sender.SmsSender;
|
||||
import com.njcn.msgpush.module.push.client.setting.impl.TelecomSmsSetting;
|
||||
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
import com.njcn.msgpush.module.push.util.RestTemplateUtil;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
@@ -29,12 +36,40 @@ public class TelecomSmsSender implements SmsSender {
|
||||
*/
|
||||
private static final String CONTENT_TYPE = "application/json;charset=utf-8";
|
||||
|
||||
public static final String CONTENT = "content";
|
||||
|
||||
private TelecomSmsSetting telecomSmsSetting;
|
||||
|
||||
private RestTemplateUtil restTemplateUtil;
|
||||
|
||||
private final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
|
||||
5,
|
||||
5,
|
||||
1000,
|
||||
TimeUnit.MILLISECONDS,
|
||||
new java.util.concurrent.ArrayBlockingQueue<>(1000),
|
||||
r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName("TelecomSmsSender-Pool-" + thread.getId());
|
||||
thread.setDaemon(false);
|
||||
return thread;
|
||||
},
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
);
|
||||
|
||||
private class TelecomSmsResponse {
|
||||
private String status;
|
||||
private Double balance;
|
||||
private List<TelecomSmsMessageRes> list;
|
||||
}
|
||||
|
||||
private class TelecomSmsMessageRes {
|
||||
//消息ID(用于状态报告匹配)
|
||||
private String mid;
|
||||
//手机号码
|
||||
private String mobile;
|
||||
//短信提交错误代码
|
||||
private Integer result;
|
||||
}
|
||||
|
||||
|
||||
public TelecomSmsSender(TelecomSmsSetting telecomSmsSetting, RestTemplateUtil restTemplateUtil) {
|
||||
this.telecomSmsSetting = telecomSmsSetting;
|
||||
@@ -42,25 +77,79 @@ public class TelecomSmsSender implements SmsSender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendSms(Map<String, Object> params, String phoneNumber) throws Exception {
|
||||
return this.sendBatchSms(params, List.of(phoneNumber));
|
||||
public boolean sendSms(MessageRecordDO message) {
|
||||
Future<Boolean> future = THREAD_POOL_EXECUTOR.submit(() -> {
|
||||
message.setStatus(MessageStatusConstant.SENDING);
|
||||
// 构建请求参数
|
||||
Map<String, Object> request = new HashMap<>();
|
||||
request.put("action", "send");
|
||||
request.put("account", telecomSmsSetting.getAccount());
|
||||
request.put("password", telecomSmsSetting.getPassword());
|
||||
request.put("mobile", message.getReceiver());
|
||||
request.put("content", message.getContent());
|
||||
request.put("extno", ACCESS_CODE);
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Content-Type", CONTENT_TYPE);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long start = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
message.setSendTime(now);
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplateUtil.post(
|
||||
API_URL,
|
||||
request,
|
||||
headers,
|
||||
String.class
|
||||
);
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
message.setCostTime((int) (end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - start));
|
||||
TelecomSmsResponse telecomSmsResponse = JSON.parseObject(response.getBody(), TelecomSmsResponse.class);
|
||||
|
||||
if (telecomSmsResponse.list.get(0).result == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Boolean b = null;
|
||||
try {
|
||||
b = future.get(3, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (b) {
|
||||
message.setStatus(MessageStatusConstant.SUCCESS);
|
||||
} else {
|
||||
message.setStatus(MessageStatusConstant.FAILED);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendBatchSms(Map<String, Object> params, List<String> phoneNumbers) {
|
||||
// 构建请求参数
|
||||
public boolean sendBatchSms(List<MessageRecordDO> messageList) {
|
||||
Map<String, Object> request = new HashMap<>();
|
||||
request.put("action", "send");
|
||||
request.put("action", "p2p");
|
||||
request.put("account", telecomSmsSetting.getAccount());
|
||||
request.put("password", telecomSmsSetting.getPassword());
|
||||
request.put("mobile", StrUtil.join(StrUtil.COMMA, phoneNumbers));
|
||||
request.put("content", params.get(CONTENT).toString());
|
||||
Map<String, String> mobileContentKvp = new HashMap<>();
|
||||
for (MessageRecordDO message : messageList) {
|
||||
mobileContentKvp.put(message.getReceiver(), "【" + message.getTitle() + "】" + message.getContent());
|
||||
message.setStatus(MessageStatusConstant.SENDING);
|
||||
}
|
||||
request.put("mobileContentKvp", JSON.toJSONString(mobileContentKvp));
|
||||
request.put("extno", ACCESS_CODE);
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Content-Type", CONTENT_TYPE);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long start = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplateUtil.post(
|
||||
API_URL,
|
||||
@@ -68,12 +157,16 @@ public class TelecomSmsSender implements SmsSender {
|
||||
headers,
|
||||
String.class
|
||||
);
|
||||
String body = response.getBody();
|
||||
|
||||
if (body.contains("\"status\": 0")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
for (MessageRecordDO message : messageList) {
|
||||
message.setSendTime(now);
|
||||
message.setCostTime((int) (end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - start));
|
||||
}
|
||||
TelecomSmsResponse telecomSmsResponse = JSON.parseObject(response.getBody(), TelecomSmsResponse.class);
|
||||
boolean res = true;
|
||||
for (TelecomSmsMessageRes telecomSmsMessageRes : telecomSmsResponse.list) {
|
||||
res &= telecomSmsMessageRes.result == 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.msgpush.module.push.client.sender.impl;
|
||||
|
||||
import com.njcn.msgpush.module.push.client.sender.AppPushSender;
|
||||
import com.njcn.msgpush.module.push.client.setting.impl.UniPushAppPushSetting;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
import com.njcn.msgpush.module.push.util.RestTemplateUtil;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-02-26
|
||||
*/
|
||||
public class UniPushEmailSender implements AppPushSender {
|
||||
private UniPushAppPushSetting uniPushAppPushSetting;
|
||||
|
||||
private RestTemplateUtil restTemplateUtil;
|
||||
|
||||
public UniPushEmailSender(UniPushAppPushSetting uniPushAppPushSetting, RestTemplateUtil restTemplateUtil) {
|
||||
this.uniPushAppPushSetting = uniPushAppPushSetting;
|
||||
this.restTemplateUtil = restTemplateUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appPush(MessageRecordDO message) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import lombok.*;
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UniPushAppPushSetting extends AppPushSetting {
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String appId;
|
||||
private String appKey;
|
||||
private String appSecret;
|
||||
private String masterSecret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.msgpush.module.push.constant;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-02-26
|
||||
*/
|
||||
public class MessageStatusConstant {
|
||||
//待发送
|
||||
public static final String PENDING = "pending";
|
||||
//发送中
|
||||
public static final String SENDING = "sending";
|
||||
//发送成功
|
||||
public static final String SUCCESS = "success";
|
||||
//发送失败(可重试)
|
||||
public static final String FAILED = "failed";
|
||||
//最终失败(超过最大重试次数,系统自动判定)
|
||||
public static final String FINALFAILED = "final_failed";
|
||||
//黑名单拦截
|
||||
public static final String BLACKLISTED = "blacklisted";
|
||||
//配额超限
|
||||
public static final String QUOTAEXCEEDED = "quota_exceeded";
|
||||
//频率限制
|
||||
public static final String RATE_LIMITED = "rate_limited";
|
||||
//手动终止重试(运维在重试队列管理中主动终止,区别于系统自动判定的final_failed)
|
||||
public static final String ABANDONED = "abandoned";
|
||||
|
||||
}
|
||||
@@ -5,7 +5,11 @@ package com.njcn.msgpush.module.push.constant;
|
||||
* @data 2026-02-11
|
||||
*/
|
||||
public class MsgPushConstant {
|
||||
public static final String ALI_YUN = "aliyun";
|
||||
public static final String TELECOM = "telecom";
|
||||
public static final String UNI_PUSH = "UniPush";
|
||||
public static final String PROVIDER_TYPE_ALI_YUN = "aliyun";
|
||||
public static final String PROVIDER_TYPE_TELECOM = "telecom";
|
||||
public static final String PROVIDER_TYPE_UNI_PUSH = "UniPush";
|
||||
|
||||
public static final String CHANNEL_SMS = "sms";
|
||||
public static final String CHANNEL_EMAIL = "email";
|
||||
public static final String CHANNEL_APP_PUSH = "app_push";
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.njcn.msgpush.module.push.controller.admin.channel;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.msgpush.framework.common.pojo.CommonResult;
|
||||
import com.njcn.msgpush.module.push.client.factory.MessageProviderFactory;
|
||||
import com.njcn.msgpush.module.push.controller.admin.channel.vo.ChannelProviderConfigReqVO;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import com.njcn.msgpush.module.push.service.channel.ChannelProviderConfigService;
|
||||
@@ -10,11 +11,13 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "管理后台 - 渠道服务商")
|
||||
@Slf4j
|
||||
@@ -26,6 +29,10 @@ public class ChannelProviderConfigController {
|
||||
@Autowired
|
||||
private ChannelProviderConfigService channelProviderConfigService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("messageProviderFactoryMap")
|
||||
private Map<String, MessageProviderFactory> messageProviderFactoryMap;
|
||||
|
||||
|
||||
@PostMapping("/list")
|
||||
@Operation(summary = "分页查询渠道服务商列表")
|
||||
@@ -35,7 +42,7 @@ public class ChannelProviderConfigController {
|
||||
return CommonResult.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("add")
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增渠道服务商")
|
||||
@PreAuthorize("@ss.hasPermission('push:channel:add')")
|
||||
public CommonResult<Boolean> addChannelProvider(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
||||
@@ -43,7 +50,7 @@ public class ChannelProviderConfigController {
|
||||
return CommonResult.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("update")
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "更新渠道服务商")
|
||||
@PreAuthorize("@ss.hasPermission('push:channel:update')")
|
||||
public CommonResult<Boolean> updateChannelProvider(@Validated @RequestBody ChannelProviderConfigReqVO reqVO) {
|
||||
@@ -51,7 +58,7 @@ public class ChannelProviderConfigController {
|
||||
return CommonResult.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("delete")
|
||||
@PostMapping("/delete")
|
||||
@Operation(summary = "删除渠道服务商")
|
||||
@PreAuthorize("@ss.hasPermission('push:channel:delete')")
|
||||
public CommonResult<Boolean> deleteChannelProvider(@RequestBody List<String> ids) {
|
||||
@@ -59,11 +66,39 @@ public class ChannelProviderConfigController {
|
||||
return CommonResult.success(res);
|
||||
}
|
||||
|
||||
@GetMapping("toggle")
|
||||
@GetMapping("/toggle")
|
||||
@Operation(summary = "启用/禁用渠道服务商")
|
||||
@PreAuthorize("@ss.hasPermission('push:channel:toggle')")
|
||||
public CommonResult<Boolean> toggleEnableChannelProvider(@RequestParam("id") String id) {
|
||||
boolean res = channelProviderConfigService.toggleEnableField(id);
|
||||
return CommonResult.success(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指定providerType服务商对应的bean
|
||||
*
|
||||
* @param providerType 服务商类型,例如:aliyun\telecom\UniPush
|
||||
*/
|
||||
public void registerProviderBean(String providerType) {
|
||||
// DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
|
||||
// BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
|
||||
// for (Object arg : args) {
|
||||
// builder.addConstructorArgValue(arg);
|
||||
// }
|
||||
// beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定providerType服务商对应的bean
|
||||
*
|
||||
* @param providerType 服务商类型,例如:aliyun\telecom\UniPush
|
||||
*/
|
||||
public void removeProviderBean(String providerType) {
|
||||
// DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
|
||||
// if (beanFactory.containsBeanDefinition(beanName)) {
|
||||
// beanFactory.removeBeanDefinition(beanName);
|
||||
// }
|
||||
messageProviderFactoryMap.remove(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,13 @@ public interface ChannelProviderConfigService extends IService<ChannelProviderCo
|
||||
* @return
|
||||
*/
|
||||
boolean toggleEnableField(String id);
|
||||
|
||||
/**
|
||||
* 根据类型和渠道获取服务提供商
|
||||
*
|
||||
* @param providerType 服务提供商类型
|
||||
* @param channel 渠道
|
||||
* @return
|
||||
*/
|
||||
ChannelProviderConfigDO getByTypeAndChannel(String providerType, String channel);
|
||||
}
|
||||
|
||||
@@ -7,18 +7,12 @@ import com.njcn.msgpush.framework.common.util.object.PageUtils;
|
||||
import com.njcn.msgpush.module.push.controller.admin.channel.vo.ChannelProviderConfigReqVO;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import com.njcn.msgpush.module.push.dal.mysql.channel.ChannelProviderConfigMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ChannelProviderConfigServiceImpl extends ServiceImpl<ChannelProviderConfigMapper, ChannelProviderConfigDO> implements ChannelProviderConfigService {
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public Page<ChannelProviderConfigDO> listChannelProviderCfg(ChannelProviderConfigReqVO pageReqVO) {
|
||||
@@ -44,19 +38,10 @@ public class ChannelProviderConfigServiceImpl extends ServiceImpl<ChannelProvide
|
||||
return false;
|
||||
}
|
||||
|
||||
public void registerBean(String beanName, Class<?> beanClass, Object... args) {
|
||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
|
||||
for (Object arg : args) {
|
||||
builder.addConstructorArgValue(arg);
|
||||
}
|
||||
beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
public void removeBean(String beanName) {
|
||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
|
||||
if (beanFactory.containsBeanDefinition(beanName)) {
|
||||
beanFactory.removeBeanDefinition(beanName);
|
||||
}
|
||||
@Override
|
||||
public ChannelProviderConfigDO getByTypeAndChannel(String providerType, String channel) {
|
||||
return this.lambdaQuery().eq(ChannelProviderConfigDO::getProviderType, providerType)
|
||||
.eq(ChannelProviderConfigDO::getChannel, channel)
|
||||
.one();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,12 @@ public interface MessageRecordService {
|
||||
* @return 发送是否成功的结果
|
||||
*/
|
||||
Boolean send(MessageRecordSendReqVO messageRecordSendReqVO);
|
||||
|
||||
/**
|
||||
* 添加消息记录
|
||||
*
|
||||
* @param messageRecordSendReqVO
|
||||
* @return
|
||||
*/
|
||||
Boolean add(MessageRecordSendReqVO messageRecordSendReqVO);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,55 @@
|
||||
package com.njcn.msgpush.module.push.service.message;
|
||||
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.msgpush.module.push.client.factory.MessageProviderFactory;
|
||||
import com.njcn.msgpush.module.push.constant.MessageStatusConstant;
|
||||
import com.njcn.msgpush.module.push.constant.MsgPushConstant;
|
||||
import com.njcn.msgpush.module.push.controller.admin.message.vo.MessageRecordSendReqVO;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
import com.njcn.msgpush.module.push.dal.mysql.message.MessageRecordMapper;
|
||||
import com.njcn.msgpush.module.push.service.channel.ChannelProviderConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class MessageRecordServiceImpl implements MessageRecordService{
|
||||
public class MessageRecordServiceImpl extends ServiceImpl<MessageRecordMapper, MessageRecordDO> implements MessageRecordService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("messageProviderFactoryMap")
|
||||
private Map<String, MessageProviderFactory> messageProviderFactoryMap;
|
||||
|
||||
@Autowired
|
||||
private ChannelProviderConfigService channelProviderConfigService;
|
||||
|
||||
@Override
|
||||
public Boolean send(MessageRecordSendReqVO messageRecordSendReqVO) {
|
||||
MessageRecordDO messageRecordDO = BeanUtil.copyProperties(messageRecordSendReqVO, MessageRecordDO.class);
|
||||
messageRecordDO.setStatus(MessageStatusConstant.PENDING);
|
||||
this.save(messageRecordDO);
|
||||
|
||||
ChannelProviderConfigDO channelProviderConfigDO = channelProviderConfigService.getByTypeAndChannel(messageRecordDO.getProviderType(), messageRecordDO.getChannel());
|
||||
//channelProviderConfigDO.setAppKey("LTAI4FxsR76x2dq3w9c5puUe");
|
||||
//channelProviderConfigDO.setAppSecret("GxkTR8fsrvHtixTlD9UPmOGli35tZs");
|
||||
MessageProviderFactory messageProviderFactory = messageProviderFactoryMap.get(messageRecordDO.getProviderType());
|
||||
boolean sendResult = switch (messageRecordDO.getChannel()) {
|
||||
case MsgPushConstant.CHANNEL_SMS -> messageProviderFactory.createSmsSender(channelProviderConfigDO).sendSms(messageRecordDO);
|
||||
//case MsgPushConstant.CHANNEL_EMAIL ->
|
||||
//messageProviderFactory.createEmailSender(channelProviderConfigDO).sendEmail(messageRecordDO.getTemplateParams(), messageRecordDO.getReceiver());
|
||||
case MsgPushConstant.CHANNEL_APP_PUSH -> messageProviderFactory.createAppPushSender(channelProviderConfigDO).appPush(messageRecordDO);
|
||||
default -> throw new RuntimeException("暂不支持该渠道:" + messageRecordDO.getChannel());
|
||||
};
|
||||
|
||||
return sendResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean add(MessageRecordSendReqVO messageRecordSendReqVO) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import com.njcn.msgpush.module.push.client.sender.SmsSender;
|
||||
import com.njcn.msgpush.module.push.client.sender.impl.AliyunSmsSender;
|
||||
import com.njcn.msgpush.module.push.constant.MsgPushConstant;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.channel.ChannelProviderConfigDO;
|
||||
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -27,17 +29,21 @@ public class MsgPushClientTest {
|
||||
|
||||
@Test
|
||||
public void testSendSms() throws Exception {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(AliyunSmsSender.SIGN_NAME, "灿能云");
|
||||
params.put(AliyunSmsSender.TEMPLATE_CODE, "SMS_481710295");
|
||||
params.put(AliyunSmsSender.TEMPLATE_PARAM, "{\"code\":\"123456\"}");
|
||||
|
||||
ChannelProviderConfigDO channelProviderConfigDO = new ChannelProviderConfigDO();
|
||||
channelProviderConfigDO.setAppKey("LTAI4FxsR76x2dq3w9c5puUe");
|
||||
channelProviderConfigDO.setAppSecret("GxkTR8fsrvHtixTlD9UPmOGli35tZs");
|
||||
SmsSender smsSender = messageProviderFactoryMap.get(MsgPushConstant.ALI_YUN).createSmsSender(channelProviderConfigDO);
|
||||
SmsSender smsSender = messageProviderFactoryMap.get(MsgPushConstant.PROVIDER_TYPE_ALI_YUN).createSmsSender(channelProviderConfigDO);
|
||||
|
||||
boolean b = smsSender.sendSms(params, "18839431215");
|
||||
MessageRecordDO message = new MessageRecordDO();
|
||||
message.setMessageId("1c2w1e2a3c456");
|
||||
message.setChannel(MsgPushConstant.CHANNEL_SMS);
|
||||
message.setTitle("灿能云");
|
||||
message.setReceiver("18839431215");
|
||||
message.setTemplateCode("SMS_481710295");
|
||||
message.setTemplateParams("{\"code\":\"123456\"}");
|
||||
message.setProviderType(MsgPushConstant.PROVIDER_TYPE_ALI_YUN);
|
||||
boolean b = smsSender.sendSms(message);
|
||||
System.out.println(System.currentTimeMillis() + " " + b);
|
||||
}
|
||||
|
||||
@@ -48,12 +54,25 @@ public class MsgPushClientTest {
|
||||
params.put(AliyunSmsSender.TEMPLATE_CODE, "SMS_481710295");
|
||||
params.put(AliyunSmsSender.TEMPLATE_PARAM, List.of("{\"code\":\"123456\"}"));
|
||||
|
||||
List<MessageRecordDO> messageIdList = new ArrayList<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
MessageRecordDO message = new MessageRecordDO();
|
||||
message.setMessageId(i + "c518e2a3c");
|
||||
message.setChannel(MsgPushConstant.CHANNEL_SMS);
|
||||
message.setTitle("灿能云");
|
||||
message.setReceiver("18839431215");
|
||||
message.setTemplateCode("SMS_481710295");
|
||||
message.setTemplateParams("{\"code\":\"65432" + i + "\"}");
|
||||
message.setProviderType(MsgPushConstant.PROVIDER_TYPE_ALI_YUN);
|
||||
messageIdList.add(message);
|
||||
}
|
||||
|
||||
ChannelProviderConfigDO channelProviderConfigDO = new ChannelProviderConfigDO();
|
||||
channelProviderConfigDO.setAppKey("LTAI4FxsR76x2dq3w9c5puUe");
|
||||
channelProviderConfigDO.setAppSecret("GxkTR8fsrvHtixTlD9UPmOGli35tZs");
|
||||
SmsSender smsSender = messageProviderFactoryMap.get(MsgPushConstant.ALI_YUN).createSmsSender(channelProviderConfigDO);
|
||||
SmsSender smsSender = messageProviderFactoryMap.get(MsgPushConstant.PROVIDER_TYPE_ALI_YUN).createSmsSender(channelProviderConfigDO);
|
||||
|
||||
boolean b = smsSender.sendBatchSms(params, List.of("18839431215"));
|
||||
boolean b = smsSender.sendBatchSms(messageIdList);
|
||||
System.out.println(System.currentTimeMillis() + " " + b);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user