批量发送半成品
This commit is contained in:
@@ -58,7 +58,7 @@ public abstract class AbstractSmsClient implements SmsClient {
|
||||
* @return 短信发送结果
|
||||
* @throws Exception 调用发送失败,抛出异常
|
||||
*/
|
||||
public abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception;
|
||||
protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception;
|
||||
|
||||
protected void beforeSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception {
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package cn.iocoder.dashboard.framework.sms.client;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
|
||||
@@ -14,11 +13,11 @@ 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 org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -31,7 +30,7 @@ import java.util.List;
|
||||
* @date 2021/1/25 14:17
|
||||
*/
|
||||
@Slf4j
|
||||
public class AliyunSmsClient extends AbstractSmsClient {
|
||||
public class AliyunSmsClient extends AbstractSmsClient implements NeedQuerySendResultSmsClient {
|
||||
|
||||
private static final String OK = "OK";
|
||||
|
||||
@@ -70,35 +69,43 @@ public class AliyunSmsClient extends AbstractSmsClient {
|
||||
request.setTemplateParam(smsBody.getParamsStr());
|
||||
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
|
||||
|
||||
boolean result = OK.equals(sendSmsResponse.getCode());
|
||||
if (!result) {
|
||||
boolean success = OK.equals(sendSmsResponse.getCode());
|
||||
if (!success) {
|
||||
log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
|
||||
}
|
||||
SmsResult resultBody = new SmsResult();
|
||||
resultBody.setSuccess(result);
|
||||
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(sendSmsResponse.getBizId());
|
||||
// TODO FROM 芋艿 to zzf:发送完之后,基于短信平台回调,去更新回执状态。短信发送是否成功,和最终用户收到,是两个维度。这块有困惑,可以微信,我给个截图哈。
|
||||
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.setCreateTime(DateUtil.parseDateTime(s.getSendDate()));
|
||||
resultDetail.setSendTime(DateUtil.parseDateTime(s.getSendDate()));
|
||||
resultDetail.setMessage(s.getContent());
|
||||
resultDetail.setPhone(s.getPhoneNum());
|
||||
resultDetail.setStatus(statusConvert(s.getSendStatus()));
|
||||
resultDetail.setSendStatus(statusConvert(s.getSendStatus()));
|
||||
resultDetailList.add(resultDetail);
|
||||
});
|
||||
resultBody.setResult(resultDetailList);
|
||||
return resultBody;
|
||||
return resultDetailList;
|
||||
}
|
||||
|
||||
private int statusConvert(Long aliSendStatus) {
|
||||
if (aliSendStatus == 1L) {
|
||||
return SmsSendStatusEnum.SUCCESS.getStatus();
|
||||
return SmsSendStatusEnum.SEND_SUCCESS.getStatus();
|
||||
}
|
||||
if (aliSendStatus == 2L) {
|
||||
return SmsSendStatusEnum.FAIL.getStatus();
|
||||
return SmsSendStatusEnum.SEND_FAIL.getStatus();
|
||||
}
|
||||
return SmsSendStatusEnum.WAITING.getStatus();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package cn.iocoder.dashboard.framework.sms.client;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 短信父接口
|
||||
@@ -23,4 +25,7 @@ public interface SmsClient {
|
||||
*/
|
||||
SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets);
|
||||
|
||||
|
||||
//List<SmsResultDetail> getSmsSendResult(String jsonObjectParam);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package cn.iocoder.dashboard.framework.sms.client;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsConstants;
|
||||
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.fasterxml.jackson.core.type.TypeReference;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 云片短信实现类
|
||||
*
|
||||
* @author zzf
|
||||
* @date 9:48 2021/3/5
|
||||
*/
|
||||
@Slf4j
|
||||
public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSmsClient {
|
||||
|
||||
private final YunpianClient client;
|
||||
|
||||
private final TypeReference<List<Map<String, String>>> callbackType = new TypeReference<List<Map<String, String>>>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* 构造云片短信发送处理
|
||||
*
|
||||
* @param channelVO 阿里云短信配置
|
||||
*/
|
||||
public YunpianSmsClient(SmsChannelProperty channelVO) {
|
||||
super(channelVO);
|
||||
client = new YunpianClient(channelVO.getApiKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) {
|
||||
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());
|
||||
|
||||
Result<SmsBatchSend> sendResult = client.sms().batch_send(paramMap);
|
||||
boolean success = sendResult.getCode().equals(Code.OK);
|
||||
|
||||
if (!success) {
|
||||
log.debug("send fail[code={}, message={}]", sendResult.getCode(), sendResult.getDetail());
|
||||
}
|
||||
return new SmsResult()
|
||||
.setSuccess(success)
|
||||
.setMessage(sendResult.getDetail())
|
||||
.setCode(sendResult.getCode().toString())
|
||||
.setApiId(sendResult.getData().getData().get(0).getSid().toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化短信内容,将参数注入到模板中
|
||||
*
|
||||
* @param smsBody 短信信息
|
||||
* @return 格式化后的短信内容
|
||||
*/
|
||||
private String formatContent(SmsBody smsBody) {
|
||||
StringBuilder result = new StringBuilder(smsBody.getTemplateContent());
|
||||
smsBody.getParams().forEach((key, val) -> {
|
||||
String param = parseParamToPlaceholder(key);
|
||||
result.replace(result.indexOf(param), result.indexOf(param + param.length()), val);
|
||||
});
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定参数改成对应的占位字符
|
||||
* <p>
|
||||
* 云片的是 #param# 的形式作为占位符
|
||||
*
|
||||
* @param key 参数名
|
||||
* @return 对应的占位字符
|
||||
*/
|
||||
private String parseParamToPlaceholder(String key) {
|
||||
return SmsConstants.JING_HAO + key + SmsConstants.JING_HAO;
|
||||
}
|
||||
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 request 中获取请求中传入的短信发送结果信息
|
||||
*
|
||||
* @param request 回调请求
|
||||
* @return 短信发送结果信息
|
||||
* @throws UnsupportedEncodingException 解码异常
|
||||
*/
|
||||
private List<Map<String, String>> getSendResult(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);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import cn.iocoder.dashboard.util.json.JsonUtils;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 消息内容实体类
|
||||
@@ -22,6 +21,11 @@ public class SmsBody {
|
||||
*/
|
||||
private String templateCode;
|
||||
|
||||
/**
|
||||
* 模板编码
|
||||
*/
|
||||
private String templateContent;
|
||||
|
||||
/**
|
||||
* 参数列表
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.dashboard.framework.sms.core;
|
||||
|
||||
/**
|
||||
* 短信相关常量类
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/3/5 10:42
|
||||
*/
|
||||
public interface SmsConstants {
|
||||
|
||||
String OK = "OK";
|
||||
|
||||
String JING_HAO = "#";
|
||||
|
||||
String COMMA = ",";
|
||||
|
||||
String SUCCESS = "SUCCESS";
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
package cn.iocoder.dashboard.framework.sms.core;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息内容实体类
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class SmsResult implements Serializable {
|
||||
|
||||
/**
|
||||
@@ -16,6 +17,11 @@ public class SmsResult implements Serializable {
|
||||
*/
|
||||
private Boolean success;
|
||||
|
||||
/**
|
||||
* 第三方唯一标识
|
||||
*/
|
||||
private String apiId;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
@@ -27,10 +33,9 @@ public class SmsResult implements Serializable {
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 返回值
|
||||
* 用于查询发送结果的参数
|
||||
*/
|
||||
private List<SmsResultDetail> result;
|
||||
|
||||
private String sendResultParam;
|
||||
|
||||
public static SmsResult failResult(String message) {
|
||||
SmsResult resultBody = new SmsResult();
|
||||
|
||||
@@ -14,7 +14,7 @@ public class SmsResultDetail implements Serializable {
|
||||
/**
|
||||
* 短信发送状态 {@link cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
private Integer sendStatus;
|
||||
|
||||
/**
|
||||
* 接收手机号
|
||||
@@ -29,5 +29,5 @@ public class SmsResultDetail implements Serializable {
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private Date createTime;
|
||||
private Date sendTime;
|
||||
}
|
||||
|
||||
@@ -54,4 +54,15 @@ public class SmsChannelProperty implements Serializable {
|
||||
@NotEmpty(message = "签名值不能为空")
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 是否拥有回调函数(0否 1是)
|
||||
*/
|
||||
@NotNull(message = "是否拥有回调函数不能为空")
|
||||
private Integer hadCallback;
|
||||
|
||||
/**
|
||||
* 短信发送回调url
|
||||
*/
|
||||
private String callbackUrl;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user