实现短信通过redis stream异步发送,实现短信callback处理

This commit is contained in:
zengzefeng
2021-03-12 11:32:04 +08:00
parent 767cd90279
commit 225664550f
56 changed files with 877 additions and 492 deletions

View File

@@ -0,0 +1,29 @@
package cn.iocoder.dashboard.framework.redis.core.util;
import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendMessage;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Redis 消息工具类
*
* @author 芋道源码
*/
public class RedisStreamUtils {
public static final String KEY_SMS_SEND = "stream_sms_send";
public static final String GROUP_SMS_SEND = "group_sms_send";
/**
* 发送 Redis 消息,基于 Redis pub/sub 实现
*
* @param redisTemplate Redis 操作模板
* @param message 消息
*/
public static void sendChannelMessage(RedisTemplate<String, ?> redisTemplate, SmsSendMessage message) {
redisTemplate.opsForStream().add(StreamRecords.newRecord().ofObject(message).withStreamKey(KEY_SMS_SEND));
}
}

View File

@@ -134,6 +134,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.antMatchers(webProperties.getApiPrefix() + "/system/file/get/**").anonymous()
// TODO
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/**").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()

View File

@@ -5,8 +5,6 @@ import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;
/**
* 抽象短息客户端
*
@@ -35,12 +33,12 @@ public abstract class AbstractSmsClient implements SmsClient {
}
@Override
public final SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets) {
public final SmsResult send(String templateApiId, SmsBody smsBody, String target) {
SmsResult result;
try {
beforeSend(templateApiId, smsBody, targets);
result = doSend(templateApiId, smsBody, targets);
afterSend(templateApiId, smsBody, targets, result);
beforeSend(templateApiId, smsBody, target);
result = doSend(templateApiId, smsBody, target);
afterSend(templateApiId, smsBody, target, result);
} catch (Exception e) {
// exception handle
log.debug(e.getMessage(), e);
@@ -54,16 +52,16 @@ public abstract class AbstractSmsClient implements SmsClient {
*
* @param templateApiId 短信模板唯一标识
* @param smsBody 消息内容
* @param targets 发送对象列表
* @param targetPhone 发送对象手机号
* @return 短信发送结果
* @throws Exception 调用发送失败,抛出异常
*/
protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception;
protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception;
protected void beforeSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception {
protected void beforeSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception {
}
protected void afterSend(String templateApiId, SmsBody smsBody, Collection<String> targets, SmsResult result) throws Exception {
protected void afterSend(String templateApiId, SmsBody smsBody, String targetPhone, SmsResult result) throws Exception {
}
}

View File

@@ -1,113 +0,0 @@
package cn.iocoder.dashboard.framework.sms.client;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 阿里短信实现类
*
* @author zzf
* @date 2021/1/25 14:17
*/
@Slf4j
public class AliyunSmsClient extends AbstractSmsClient implements NeedQuerySendResultSmsClient {
private static final String OK = "OK";
private static final String PRODUCT = "Dysmsapi";
private static final String DOMAIN = "dysmsapi.aliyuncs.com";
private static final String ENDPOINT = "cn-hangzhou";
private final IAcsClient acsClient;
/**
* 构造阿里云短信发送处理
*
* @param channelVO 阿里云短信配置
*/
public AliyunSmsClient(SmsChannelProperty channelVO) {
super(channelVO);
String accessKeyId = channelVO.getApiKey();
String accessKeySecret = channelVO.getApiSecret();
IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN);
acsClient = new DefaultAcsClient(profile);
}
@Override
public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception {
SendSmsRequest request = new SendSmsRequest();
request.setSysMethod(MethodType.POST);
request.setPhoneNumbers(ArrayUtil.join(targets, ","));
request.setSignName(channelVO.getApiSignatureId());
request.setTemplateCode(templateApiId);
request.setTemplateParam(smsBody.getParamsStr());
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
boolean success = OK.equals(sendSmsResponse.getCode());
if (!success) {
log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
}
return new SmsResult()
.setSuccess(success)
.setMessage(sendSmsResponse.getMessage())
.setCode(sendSmsResponse.getCode())
.setApiId(sendSmsResponse.getBizId())
.setSendResultParam(sendSmsResponse.getBizId());
}
@Override
public List<SmsResultDetail> getSmsSendResult(String param) throws ClientException {
QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest();
querySendDetailsRequest.setBizId(param);
// TODO FROM 芋艿 to zzf发送完之后基于短信平台回调去更新回执状态。短信发送是否成功和最终用户收到是两个维度。这块有困惑可以微信我给个截图哈。 DONE
QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest);
List<SmsResultDetail> resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount()));
acsResponse.getSmsSendDetailDTOs().forEach(s -> {
SmsResultDetail resultDetail = new SmsResultDetail();
resultDetail.setSendTime(DateUtil.parseDateTime(s.getSendDate()));
resultDetail.setMessage(s.getContent());
resultDetail.setPhone(s.getPhoneNum());
resultDetail.setSendStatus(statusConvert(s.getSendStatus()));
resultDetailList.add(resultDetail);
});
return resultDetailList;
}
private int statusConvert(Long aliSendStatus) {
if (aliSendStatus == 1L) {
return SmsSendStatusEnum.SEND_SUCCESS.getStatus();
}
if (aliSendStatus == 2L) {
return SmsSendStatusEnum.SEND_FAIL.getStatus();
}
return SmsSendStatusEnum.WAITING.getStatus();
}
}

View File

@@ -1,25 +0,0 @@
package cn.iocoder.dashboard.framework.sms.client;
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
import javax.servlet.ServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.List;
/**
* 需要发送请求获取短信发送结果的短信客户端
*
* @author zzf
* @date 2021/3/4 17:20
*/
public interface HadCallbackSmsClient {
/**
* 获取短信发送结果
*
* @param request 请求
* @return 短信发送结果
*/
List<SmsResultDetail> getSmsSendResult(ServletRequest request) throws Exception;
}

View File

@@ -1,24 +0,0 @@
package cn.iocoder.dashboard.framework.sms.client;
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
import com.aliyuncs.exceptions.ClientException;
import java.util.List;
/**
* 需要发送请求获取短信发送结果的短信客户端
*
* @author zzf
* @date 2021/3/4 17:20
*/
public interface NeedQuerySendResultSmsClient {
/**
* 获取短信发送结果
*
* @param param 参数
* @return 短信发送结果
*/
List<SmsResultDetail> getSmsSendResult(String param) throws Exception;
}

View File

@@ -4,8 +4,7 @@ import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
import java.util.Collection;
import java.util.List;
import javax.servlet.ServletRequest;
/**
* 短信父接口
@@ -23,9 +22,15 @@ public interface SmsClient {
* @param targets 发送对象列表
* @return 短信发送结果
*/
SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets);
SmsResult send(String templateApiId, SmsBody smsBody, String targets);
//List<SmsResultDetail> getSmsSendResult(String jsonObjectParam);
/**
* 短信发送回调请求处理
*
* @param request 请求
* @return 短信发送结果
*/
SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception;
}

View File

@@ -0,0 +1,212 @@
package cn.iocoder.dashboard.framework.sms.client.impl.ali;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum;
import cn.iocoder.dashboard.util.json.JsonUtils;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 阿里短信实现类
*
* @author zzf
* @date 2021/1/25 14:17
*/
@Slf4j
public class AliyunSmsClient extends AbstractSmsClient {
private static final String OK = "OK";
private static final String PRODUCT = "Dystopi";
private static final String DOMAIN = "dysmsapi.aliyuncs.com";
private static final String ENDPOINT = "cn-hangzhou";
private final IAcsClient acsClient;
/**
* 构造阿里云短信发送处理
*
* @param channelVO 阿里云短信配置
*/
public AliyunSmsClient(SmsChannelProperty channelVO) {
super(channelVO);
String accessKeyId = channelVO.getApiKey();
String accessKeySecret = channelVO.getApiSecret();
IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN);
acsClient = new DefaultAcsClient(profile);
}
@Override
public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception {
SendSmsRequest request = new SendSmsRequest();
request.setSysMethod(MethodType.POST);
request.setPhoneNumbers(targetPhone);
request.setSignName(channelVO.getApiSignatureId());
request.setTemplateCode(templateApiId);
request.setTemplateParam(smsBody.getParamsStr());
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
boolean success = OK.equals(sendSmsResponse.getCode());
if (!success) {
log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
}
return new SmsResult()
.setSuccess(success)
.setMessage(sendSmsResponse.getMessage())
.setCode(sendSmsResponse.getCode())
.setApiId(sendSmsResponse.getBizId());
}
/**
* [{
* "send_time" : "2017-08-30 00:00:00",
* "report_time" : "2017-08-30 00:00:00",
* "success" : true,
* "err_msg" : "用户接收成功",
* "err_code" : "DELIVERED",
* "phone_number" : "18612345678",
* "sms_size" : "1",
* "biz_id" : "932702304080415357^0",
* "out_id" : "1184585343"
* }]
*
* @param request 请求
* @return
* @throws Exception
*/
@Override
public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String paramStr = reader.readLine();
List<Map<String, Object>> params = JsonUtils.parseByType(paramStr, new TypeReference<List<Map<String, Object>>>() {
});
if (CollectionUtil.isNotEmpty(params)) {
Map<String, Object> sendResultParamMap = params.get(0);
return CallbackHelper.of(sendResultParamMap).toResultDetail();
}
return null;
}
/**
* 短信发送回调辅助类
*/
private static class CallbackHelper {
private final Map<String, Object> sendResultParamMap;
private CallbackHelper(Map<String, Object> sendResultParamMap) {
this.sendResultParamMap = sendResultParamMap;
}
public static CallbackHelper of(Map<String, Object> sendResultParamMap) {
return new CallbackHelper(sendResultParamMap);
}
public Integer getSendStatus() {
return ((Boolean) sendResultParamMap.get(CallbackField.SUCCESS))
? SmsSendStatusEnum.SEND_SUCCESS.getStatus()
: SmsSendStatusEnum.SEND_FAIL.getStatus();
}
public String getBizId() {
return sendResultParamMap.get(CallbackField.BIZ_ID).toString();
}
public String getErrMsg() {
return sendResultParamMap.get(CallbackField.ERR_MSG).toString();
}
public String getErrCode() {
return sendResultParamMap.get(CallbackField.ERR_CODE).toString();
}
public Date getSendTime() {
return DateUtil.parseTime(sendResultParamMap.get(CallbackField.SEND_TIME).toString());
}
public String getPhoneNumber() {
return sendResultParamMap.get(CallbackField.PHONE_NUMBER).toString();
}
public String getOutId() {
return sendResultParamMap.get(CallbackField.OUT_ID).toString();
}
public SmsResultDetail toResultDetail() {
SmsResultDetail resultDetail = new SmsResultDetail();
resultDetail.setSendStatus(getSendStatus());
resultDetail.setApiId(getBizId());
resultDetail.setSendTime(getSendTime());
resultDetail.setPhone(getPhoneNumber());
resultDetail.setMessage(getErrMsg());
resultDetail.setCallbackResponseBody(generateSuccessResponseBody());
return resultDetail;
}
/**
* 生成回调成功的返回对象
*/
private Map<String, Object> generateSuccessResponseBody() {
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("msg", "成功");
return result;
}
}
/**
* 回调接口字段定义
*/
private interface CallbackField {
//是否成功 boolean
String SUCCESS = "success";
//发送时间
String SEND_TIME = "send_time";
//错误信息
String ERR_MSG = "err_msg";
//错误编码
String ERR_CODE = "err_code";
//手机号
String PHONE_NUMBER = "phone_number";
//用户序列号 out_id
String OUT_ID = "out_id";
//biz_id 即 apiId 唯一标识
String BIZ_ID = "biz_id";
}
}

View File

@@ -1,7 +1,9 @@
package cn.iocoder.dashboard.framework.sms.client;
package cn.iocoder.dashboard.framework.sms.client.impl.yunpian;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsConstants;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
@@ -14,13 +16,15 @@ import com.yunpian.sdk.YunpianClient;
import com.yunpian.sdk.constant.Code;
import com.yunpian.sdk.constant.YunpianConstant;
import com.yunpian.sdk.model.Result;
import com.yunpian.sdk.model.SmsBatchSend;
import com.yunpian.sdk.model.SmsSingleSend;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 云片短信实现类
@@ -29,7 +33,7 @@ import java.util.*;
* @date 9:48 2021/3/5
*/
@Slf4j
public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSmsClient {
public class YunpianSmsClient extends AbstractSmsClient {
private final YunpianClient client;
@@ -47,14 +51,14 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm
}
@Override
public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) {
public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) {
Map<String, String> paramMap = new HashMap<>();
paramMap.put("apikey", getProperty().getApiKey());
paramMap.put("mobile", String.join(SmsConstants.COMMA, targets));
paramMap.put("text", formatContent(smsBody));
paramMap.put("callback", getProperty().getCallbackUrl());
paramMap.put(YunpianConstant.APIKEY, getProperty().getApiKey());
paramMap.put(YunpianConstant.MOBILE, String.join(SmsConstants.COMMA, targetPhone));
paramMap.put(YunpianConstant.TEXT, formatContent(smsBody));
paramMap.put(Helper.CALLBACK, getProperty().getCallbackUrl());
Result<SmsBatchSend> sendResult = client.sms().batch_send(paramMap);
Result<SmsSingleSend> sendResult = client.sms().single_send(paramMap);
boolean success = sendResult.getCode().equals(Code.OK);
if (!success) {
@@ -64,7 +68,7 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm
.setSuccess(success)
.setMessage(sendResult.getDetail())
.setCode(sendResult.getCode().toString())
.setApiId(sendResult.getData().getData().get(0).getSid().toString());
.setApiId(sendResult.getData().getSid().toString());
}
@@ -96,26 +100,16 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm
}
/**
* 云片的比较复杂又是加密又是套娃的
*/
@Override
public List<SmsResultDetail> getSmsSendResult(ServletRequest request) throws UnsupportedEncodingException {
List<Map<String, String>> stringStringMap = getSendResult(request);
List<SmsResultDetail> resultDetailList = new ArrayList<>(stringStringMap.size());
stringStringMap.forEach(map -> {
SmsResultDetail detail = new SmsResultDetail();
detail.setPhone(map.get("mobile"));
detail.setMessage(map.get("error_msg"));
detail.setSendTime(DateUtil.parseTime(map.get("user_receive_time")));
String reportStatus = map.get("report_status");
detail.setSendStatus(reportStatus.equals(SmsConstants.SUCCESS)
? SmsSendStatusEnum.SEND_SUCCESS.getStatus()
: SmsSendStatusEnum.SEND_FAIL.getStatus()
);
resultDetailList.add(detail);
});
return resultDetailList;
public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws UnsupportedEncodingException {
Map<String, String> map = getRequestParams(request);
return Helper.getSmsResultDetailByParam(map);
}
/**
* request 中获取请求中传入的短信发送结果信息
*
@@ -123,10 +117,58 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm
* @return 短信发送结果信息
* @throws UnsupportedEncodingException 解码异常
*/
private List<Map<String, String>> getSendResult(ServletRequest request) throws UnsupportedEncodingException {
private Map<String, String> getRequestParams(ServletRequest request) throws UnsupportedEncodingException {
Map<String, String[]> parameterMap = request.getParameterMap();
String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS);
String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8);
return JsonUtils.parseByType(encode, callbackType);
List<Map<String, String>> paramList = JsonUtils.parseByType(encode, callbackType);
if (CollectionUtil.isNotEmpty(paramList)) {
return paramList.get(0);
}
throw new IllegalArgumentException("YunpianSmsClient getRequestParams fail! can't format RequestParam: "
+ JsonUtils.toJsonString(request.getParameterMap()));
}
/**
* 云片的回调函数的一些辅助方法
*/
private static class Helper {
//短信唯一标识
private final static String API_ID = "sid";
//回调地址·
private final static String CALLBACK = "callback";
//手机号
private final static String MOBILE = "mobile";
//错误信息
private final static String ERROR_MSG = "error_msg";
//用户接收时间 字符串 标准格式
private final static String USER_RECEIVE_TIME = "user_receive_time";
//发送状态
private final static String REPORT_STATUS = "report_status";
private static int getSendStatus(Map<String, String> map) {
String reportStatus = map.get(REPORT_STATUS);
return SmsConstants.SUCCESS.equals(reportStatus)
? SmsSendStatusEnum.SEND_SUCCESS.getStatus()
: SmsSendStatusEnum.SEND_FAIL.getStatus();
}
public static SmsResultDetail getSmsResultDetailByParam(Map<String, String> map) {
SmsResultDetail detail = new SmsResultDetail();
detail.setPhone(map.get(MOBILE));
detail.setMessage(map.get(ERROR_MSG));
detail.setSendTime(DateUtil.parseTime(map.get(USER_RECEIVE_TIME)));
detail.setSendStatus(getSendStatus(map));
detail.setApiId(API_ID);
detail.setCallbackResponseBody(SmsConstants.SUCCESS);
return detail;
}
}
}

View File

@@ -2,12 +2,15 @@ package cn.iocoder.dashboard.framework.sms.core;
import cn.iocoder.dashboard.common.exception.ServiceException;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient;
import cn.iocoder.dashboard.framework.sms.client.impl.ali.AliyunSmsClient;
import cn.iocoder.dashboard.framework.sms.client.impl.yunpian.YunpianSmsClient;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty;
import cn.iocoder.dashboard.util.json.JsonUtils;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -54,6 +57,8 @@ public class SmsClientFactory {
switch (channelEnum) {
case ALI:
return new AliyunSmsClient(channelVO);
case YUN_PIAN:
return new YunpianSmsClient(channelVO);
// TODO fill more channel
default:
break;
@@ -102,4 +107,28 @@ public class SmsClientFactory {
return smsTemplateProperty.getApiTemplateId();
}
/**
* 从短信发送回调函数请求中获取用于唯一确定一条send_lod的apiId
*
* @param callbackRequest 短信发送回调函数请求
* @return 第三方平台短信唯一标识
*/
public SmsResultDetail getSmsResultDetailFromCallbackQuery(ServletRequest callbackRequest) {
for (Long channelId : smsSenderMap.keySet()) {
AbstractSmsClient smsClient = smsSenderMap.get(channelId);
try {
SmsResultDetail smsSendResult = smsClient.smsSendCallbackHandle(callbackRequest);
if (smsSendResult != null) {
return smsSendResult;
}
} catch (Exception ignored) {
}
}
throw new IllegalArgumentException("getSmsResultDetailFromCallbackQuery fail! don't match SmsClient by RequestParam: "
+ JsonUtils.toJsonString(callbackRequest.getParameterMap()));
}
}

View File

@@ -32,11 +32,6 @@ public class SmsResult implements Serializable {
*/
private String message;
/**
* 用于查询发送结果的参数
*/
private String sendResultParam;
public static SmsResult failResult(String message) {
SmsResult resultBody = new SmsResult();
resultBody.setSuccess(false);

View File

@@ -11,6 +11,11 @@ import java.util.Date;
@Data
public class SmsResultDetail implements Serializable {
/**
* 唯一标识
*/
private String apiId;
/**
* 短信发送状态 {@link cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum}
*/
@@ -30,4 +35,9 @@ public class SmsResultDetail implements Serializable {
* 时间
*/
private Date sendTime;
/**
* 接口返回值
*/
private Object callbackResponseBody;
}

View File

@@ -14,8 +14,8 @@ import lombok.Getter;
public enum SmsChannelEnum {
ALI("ALI", "阿里"),
YUN_PIAN("YUN_PIAN", "云片"),
HUA_WEI("HUA_WEI", "华为"),
QI_NIU("QI_NIU", "七牛"),
TENCENT("TENCENT", "腾讯");
private final String code;