Commit 8ab29d2a257e7fbbdbae4230137a83ea64a7f5a6
1 parent
c91833a5
短信回调的解析重构
Showing
13 changed files
with
251 additions
and
212 deletions
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
| @@ -39,6 +39,6 @@ public interface SmsClient { | @@ -39,6 +39,6 @@ public interface SmsClient { | ||
| 39 | * @return 结果内容 | 39 | * @return 结果内容 |
| 40 | * @throws Throwable 当解析 text 发生异常时,则会抛出异常 | 40 | * @throws Throwable 当解析 text 发生异常时,则会抛出异常 |
| 41 | */ | 41 | */ |
| 42 | - SmsCommonResult<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable; | 42 | + List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable; |
| 43 | 43 | ||
| 44 | } | 44 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java
| 1 | package cn.iocoder.dashboard.framework.sms.core.client.dto; | 1 | package cn.iocoder.dashboard.framework.sms.core.client.dto; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; | ||
| 4 | import lombok.Data; | 3 | import lombok.Data; |
| 5 | 4 | ||
| 6 | -import java.io.Serializable; | ||
| 7 | import java.util.Date; | 5 | import java.util.Date; |
| 8 | 6 | ||
| 9 | /** | 7 | /** |
| @@ -12,35 +10,39 @@ import java.util.Date; | @@ -12,35 +10,39 @@ import java.util.Date; | ||
| 12 | * @author 芋道源码 | 10 | * @author 芋道源码 |
| 13 | */ | 11 | */ |
| 14 | @Data | 12 | @Data |
| 15 | -public class SmsReceiveRespDTO implements Serializable { | 13 | +public class SmsReceiveRespDTO { |
| 16 | 14 | ||
| 17 | /** | 15 | /** |
| 18 | - * 唯一标识 | 16 | + * 是否接收成功 |
| 19 | */ | 17 | */ |
| 20 | - private String apiId; | ||
| 21 | - | 18 | + private Boolean success; |
| 22 | /** | 19 | /** |
| 23 | - * 短信发送状态 {@link SysSmsSendStatusEnum} | 20 | + * API 接收结果编码 |
| 24 | */ | 21 | */ |
| 25 | - private Integer sendStatus; | ||
| 26 | - | 22 | + private String errorCode; |
| 27 | /** | 23 | /** |
| 28 | - * 接收手机号 | 24 | + * API 接收结果说明 |
| 29 | */ | 25 | */ |
| 30 | - private String phone; | 26 | + private String errorMsg; |
| 31 | 27 | ||
| 32 | /** | 28 | /** |
| 33 | - * 提示 | 29 | + * 手机号 |
| 34 | */ | 30 | */ |
| 35 | - private String message; | ||
| 36 | - | 31 | + private String mobile; |
| 37 | /** | 32 | /** |
| 38 | - * 时间 | 33 | + * 用户接收时间 |
| 39 | */ | 34 | */ |
| 40 | - private Date sendTime; | 35 | + private Date receiveTime; |
| 41 | 36 | ||
| 42 | /** | 37 | /** |
| 43 | - * 接口返回值 | 38 | + * 短信 API 发送返回的序号 |
| 44 | */ | 39 | */ |
| 45 | - private Object callbackResponseBody; | 40 | + private String serialNo; |
| 41 | + /** | ||
| 42 | + * 短信日志编号 | ||
| 43 | + * | ||
| 44 | + * 对应 SysSmsLogDO 的编号 | ||
| 45 | + */ | ||
| 46 | + private Long logId; | ||
| 47 | + | ||
| 46 | } | 48 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
| @@ -90,7 +90,7 @@ public abstract class AbstractSmsClient implements SmsClient { | @@ -90,7 +90,7 @@ public abstract class AbstractSmsClient implements SmsClient { | ||
| 90 | throws Throwable; | 90 | throws Throwable; |
| 91 | 91 | ||
| 92 | @Override | 92 | @Override |
| 93 | - public SmsCommonResult<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable { | 93 | + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable { |
| 94 | try { | 94 | try { |
| 95 | return doParseSmsReceiveStatus(text); | 95 | return doParseSmsReceiveStatus(text); |
| 96 | } catch (Throwable ex) { | 96 | } catch (Throwable ex) { |
| @@ -99,6 +99,6 @@ public abstract class AbstractSmsClient implements SmsClient { | @@ -99,6 +99,6 @@ public abstract class AbstractSmsClient implements SmsClient { | ||
| 99 | } | 99 | } |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | - protected abstract SmsCommonResult<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable; | 102 | + protected abstract List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable; |
| 103 | 103 | ||
| 104 | } | 104 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
| 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollectionUtil; | ||
| 4 | -import cn.hutool.core.date.DateUtil; | ||
| 5 | import cn.hutool.core.util.StrUtil; | 3 | import cn.hutool.core.util.StrUtil; |
| 6 | import cn.iocoder.dashboard.common.core.KeyValue; | 4 | import cn.iocoder.dashboard.common.core.KeyValue; |
| 7 | import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | 5 | import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; |
| @@ -9,7 +7,6 @@ import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | @@ -9,7 +7,6 @@ import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||
| 9 | import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | 7 | import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; |
| 10 | import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; | 8 | import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; |
| 11 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | 9 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; |
| 12 | -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; | ||
| 13 | import cn.iocoder.dashboard.util.collection.MapUtils; | 10 | import cn.iocoder.dashboard.util.collection.MapUtils; |
| 14 | import cn.iocoder.dashboard.util.json.JsonUtils; | 11 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 15 | import com.aliyuncs.DefaultAcsClient; | 12 | import com.aliyuncs.DefaultAcsClient; |
| @@ -20,16 +17,17 @@ import com.aliyuncs.exceptions.ClientException; | @@ -20,16 +17,17 @@ import com.aliyuncs.exceptions.ClientException; | ||
| 20 | import com.aliyuncs.http.MethodType; | 17 | import com.aliyuncs.http.MethodType; |
| 21 | import com.aliyuncs.profile.DefaultProfile; | 18 | import com.aliyuncs.profile.DefaultProfile; |
| 22 | import com.aliyuncs.profile.IClientProfile; | 19 | import com.aliyuncs.profile.IClientProfile; |
| 23 | -import com.fasterxml.jackson.core.type.TypeReference; | 20 | +import com.fasterxml.jackson.annotation.JsonFormat; |
| 21 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 22 | +import lombok.Data; | ||
| 24 | import lombok.extern.slf4j.Slf4j; | 23 | import lombok.extern.slf4j.Slf4j; |
| 25 | 24 | ||
| 26 | -import javax.servlet.ServletRequest; | ||
| 27 | -import java.io.BufferedReader; | ||
| 28 | -import java.io.InputStreamReader; | ||
| 29 | import java.util.Date; | 25 | import java.util.Date; |
| 30 | -import java.util.HashMap; | ||
| 31 | import java.util.List; | 26 | import java.util.List; |
| 32 | -import java.util.Map; | 27 | +import java.util.stream.Collectors; |
| 28 | + | ||
| 29 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 30 | +import static cn.iocoder.dashboard.util.date.DateUtils.TIME_ZONE_DEFAULT; | ||
| 33 | 31 | ||
| 34 | /** | 32 | /** |
| 35 | * 阿里短信客户端的实现类 | 33 | * 阿里短信客户端的实现类 |
| @@ -40,8 +38,9 @@ import java.util.Map; | @@ -40,8 +38,9 @@ import java.util.Map; | ||
| 40 | @Slf4j | 38 | @Slf4j |
| 41 | public class AliyunSmsClient extends AbstractSmsClient { | 39 | public class AliyunSmsClient extends AbstractSmsClient { |
| 42 | 40 | ||
| 43 | - private static final String PRODUCT = "Dystopi"; | ||
| 44 | - private static final String DOMAIN = "dysmsapi.aliyuncs.com"; | 41 | + /** |
| 42 | + * REGION, 使用杭州 | ||
| 43 | + */ | ||
| 45 | private static final String ENDPOINT = "cn-hangzhou"; | 44 | private static final String ENDPOINT = "cn-hangzhou"; |
| 46 | 45 | ||
| 47 | /** | 46 | /** |
| @@ -56,7 +55,6 @@ public class AliyunSmsClient extends AbstractSmsClient { | @@ -56,7 +55,6 @@ public class AliyunSmsClient extends AbstractSmsClient { | ||
| 56 | @Override | 55 | @Override |
| 57 | protected void doInit() { | 56 | protected void doInit() { |
| 58 | IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); | 57 | IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); |
| 59 | - DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN); | ||
| 60 | acsClient = new DefaultAcsClient(profile); | 58 | acsClient = new DefaultAcsClient(profile); |
| 61 | } | 59 | } |
| 62 | 60 | ||
| @@ -93,133 +91,80 @@ public class AliyunSmsClient extends AbstractSmsClient { | @@ -93,133 +91,80 @@ public class AliyunSmsClient extends AbstractSmsClient { | ||
| 93 | return ex.getErrMsg() + " => " + ex.getErrorDescription(); | 91 | return ex.getErrMsg() + " => " + ex.getErrorDescription(); |
| 94 | } | 92 | } |
| 95 | 93 | ||
| 96 | - /** | ||
| 97 | - * [{ | ||
| 98 | - * "send_time" : "2017-08-30 00:00:00", | ||
| 99 | - * "report_time" : "2017-08-30 00:00:00", | ||
| 100 | - * "success" : true, | ||
| 101 | - * "err_msg" : "用户接收成功", | ||
| 102 | - * "err_code" : "DELIVERED", | ||
| 103 | - * "phone_number" : "18612345678", | ||
| 104 | - * "sms_size" : "1", | ||
| 105 | - * "biz_id" : "932702304080415357^0", | ||
| 106 | - * "out_id" : "1184585343" | ||
| 107 | - * }] | ||
| 108 | - * | ||
| 109 | - * @param request 请求 | ||
| 110 | - * @return | ||
| 111 | - * @throws Exception | ||
| 112 | - */ | ||
| 113 | - public SmsReceiveRespDTO smsSendCallbackHandle(ServletRequest request) throws Exception { | ||
| 114 | - BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); | ||
| 115 | - String paramStr = reader.readLine(); | ||
| 116 | - List<Map<String, Object>> params = JsonUtils.parseObject(paramStr, new TypeReference<List<Map<String, Object>>>() { | ||
| 117 | - }); | ||
| 118 | - if (CollectionUtil.isNotEmpty(params)) { | ||
| 119 | - Map<String, Object> sendResultParamMap = params.get(0); | ||
| 120 | - return CallbackHelper.of(sendResultParamMap).toResultDetail(); | ||
| 121 | - } | ||
| 122 | - return null; | ||
| 123 | - } | ||
| 124 | - | ||
| 125 | @Override | 94 | @Override |
| 126 | - protected SmsCommonResult<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { | ||
| 127 | - return null; | 95 | + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { |
| 96 | + List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); | ||
| 97 | + return statuses.stream().map(status -> { | ||
| 98 | + SmsReceiveRespDTO resp = new SmsReceiveRespDTO(); | ||
| 99 | + resp.setSuccess(status.getSuccess()); | ||
| 100 | + resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()); | ||
| 101 | + resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()); | ||
| 102 | + resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId())); | ||
| 103 | + return resp; | ||
| 104 | + }).collect(Collectors.toList()); | ||
| 128 | } | 105 | } |
| 129 | 106 | ||
| 130 | /** | 107 | /** |
| 131 | - * 短信发送回调辅助类 | 108 | + * 短信接收状态 |
| 109 | + * | ||
| 110 | + * 参见 https://help.aliyun.com/document_detail/101867.html 文档 | ||
| 111 | + * | ||
| 112 | + * @author 芋道源码 | ||
| 132 | */ | 113 | */ |
| 133 | - private static class CallbackHelper { | ||
| 134 | - | ||
| 135 | - private final Map<String, Object> sendResultParamMap; | ||
| 136 | - | ||
| 137 | - private CallbackHelper(Map<String, Object> sendResultParamMap) { | ||
| 138 | - this.sendResultParamMap = sendResultParamMap; | ||
| 139 | - } | ||
| 140 | - | ||
| 141 | - public static CallbackHelper of(Map<String, Object> sendResultParamMap) { | ||
| 142 | - return new CallbackHelper(sendResultParamMap); | ||
| 143 | - } | ||
| 144 | - | ||
| 145 | - public Integer getSendStatus() { | ||
| 146 | - return ((Boolean) sendResultParamMap.get(CallbackField.SUCCESS)) | ||
| 147 | - ? SysSmsSendStatusEnum.SUCCESS.getStatus() | ||
| 148 | - : SysSmsSendStatusEnum.FAILURE.getStatus(); | ||
| 149 | - } | ||
| 150 | - | ||
| 151 | - public String getBizId() { | ||
| 152 | - return sendResultParamMap.get(CallbackField.BIZ_ID).toString(); | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - public String getErrMsg() { | ||
| 156 | - return sendResultParamMap.get(CallbackField.ERR_MSG).toString(); | ||
| 157 | - } | ||
| 158 | - | ||
| 159 | - public String getErrCode() { | ||
| 160 | - return sendResultParamMap.get(CallbackField.ERR_CODE).toString(); | ||
| 161 | - } | ||
| 162 | - | ||
| 163 | - public Date getSendTime() { | ||
| 164 | - return DateUtil.parseTime(sendResultParamMap.get(CallbackField.SEND_TIME).toString()); | ||
| 165 | - } | ||
| 166 | - | ||
| 167 | - public String getPhoneNumber() { | ||
| 168 | - return sendResultParamMap.get(CallbackField.PHONE_NUMBER).toString(); | ||
| 169 | - } | ||
| 170 | - | ||
| 171 | - public String getOutId() { | ||
| 172 | - return sendResultParamMap.get(CallbackField.OUT_ID).toString(); | ||
| 173 | - } | ||
| 174 | - | ||
| 175 | - public SmsReceiveRespDTO toResultDetail() { | ||
| 176 | - SmsReceiveRespDTO resultDetail = new SmsReceiveRespDTO(); | ||
| 177 | - resultDetail.setSendStatus(getSendStatus()); | ||
| 178 | - resultDetail.setApiId(getBizId()); | ||
| 179 | - resultDetail.setSendTime(getSendTime()); | ||
| 180 | - resultDetail.setPhone(getPhoneNumber()); | ||
| 181 | - resultDetail.setMessage(getErrMsg()); | ||
| 182 | - | ||
| 183 | - resultDetail.setCallbackResponseBody(generateSuccessResponseBody()); | ||
| 184 | - return resultDetail; | ||
| 185 | - } | 114 | + @Data |
| 115 | + public static class SmsReceiveStatus { | ||
| 186 | 116 | ||
| 187 | /** | 117 | /** |
| 188 | - * 生成回调成功的返回对象 | 118 | + * 手机号 |
| 189 | */ | 119 | */ |
| 190 | - private Map<String, Object> generateSuccessResponseBody() { | ||
| 191 | - Map<String, Object> result = new HashMap<>(); | ||
| 192 | - result.put("code", 0); | ||
| 193 | - result.put("msg", "成功"); | ||
| 194 | - return result; | ||
| 195 | - } | ||
| 196 | - | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - /** | ||
| 200 | - * 回调接口字段定义 | ||
| 201 | - */ | ||
| 202 | - private interface CallbackField { | ||
| 203 | - //是否成功 boolean | ||
| 204 | - String SUCCESS = "success"; | ||
| 205 | - | ||
| 206 | - //发送时间 | ||
| 207 | - String SEND_TIME = "send_time"; | ||
| 208 | - | ||
| 209 | - //错误信息 | ||
| 210 | - String ERR_MSG = "err_msg"; | ||
| 211 | - | ||
| 212 | - //错误编码 | ||
| 213 | - String ERR_CODE = "err_code"; | ||
| 214 | - | ||
| 215 | - //手机号 | ||
| 216 | - String PHONE_NUMBER = "phone_number"; | ||
| 217 | - | ||
| 218 | - //用户序列号 out_id | ||
| 219 | - String OUT_ID = "out_id"; | 120 | + @JsonProperty("phone_number") |
| 121 | + private String phoneNumber; | ||
| 122 | + /** | ||
| 123 | + * 发送时间 | ||
| 124 | + */ | ||
| 125 | + @JsonProperty("send_time") | ||
| 126 | + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) | ||
| 127 | + private Date sendTime; | ||
| 128 | + /** | ||
| 129 | + * 状态报告时间 | ||
| 130 | + */ | ||
| 131 | + @JsonProperty("report_time") | ||
| 132 | + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) | ||
| 133 | + private Date reportTime; | ||
| 134 | + /** | ||
| 135 | + * 是否接收成功 | ||
| 136 | + */ | ||
| 137 | + private Boolean success; | ||
| 138 | + /** | ||
| 139 | + * 状态报告说明 | ||
| 140 | + */ | ||
| 141 | + @JsonProperty("err_msg") | ||
| 142 | + private String errMsg; | ||
| 143 | + /** | ||
| 144 | + * 状态报告编码 | ||
| 145 | + */ | ||
| 146 | + @JsonProperty("err_code") | ||
| 147 | + private String errCode; | ||
| 148 | + /** | ||
| 149 | + * 发送序列号 | ||
| 150 | + */ | ||
| 151 | + @JsonProperty("biz_id") | ||
| 152 | + private String bizId; | ||
| 153 | + /** | ||
| 154 | + * 用户序列号 | ||
| 155 | + * | ||
| 156 | + * 这里我们传递的是 SysSmsLogDO 的日志编号 | ||
| 157 | + */ | ||
| 158 | + @JsonProperty("out_id") | ||
| 159 | + private String outId; | ||
| 160 | + /** | ||
| 161 | + * 短信长度,例如说 1、2、3 | ||
| 162 | + * | ||
| 163 | + * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送 | ||
| 164 | + */ | ||
| 165 | + @JsonProperty("sms_size") | ||
| 166 | + private Integer smsSize; | ||
| 220 | 167 | ||
| 221 | - //biz_id 即 apiId 唯一标识 | ||
| 222 | - String BIZ_ID = "biz_id"; | ||
| 223 | } | 168 | } |
| 224 | 169 | ||
| 225 | } | 170 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
| 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.collection.CollectionUtil; | ||
| 5 | -import cn.hutool.core.util.CharsetUtil; | ||
| 6 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 7 | import cn.hutool.core.util.URLUtil; | 5 | import cn.hutool.core.util.URLUtil; |
| 8 | import cn.iocoder.dashboard.common.core.KeyValue; | 6 | import cn.iocoder.dashboard.common.core.KeyValue; |
| @@ -11,11 +9,9 @@ import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | @@ -11,11 +9,9 @@ import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||
| 11 | import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | 9 | import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; |
| 12 | import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; | 10 | import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; |
| 13 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | 11 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; |
| 14 | -import cn.iocoder.dashboard.util.date.DateUtils; | ||
| 15 | import cn.iocoder.dashboard.util.json.JsonUtils; | 12 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 16 | import com.fasterxml.jackson.annotation.JsonFormat; | 13 | import com.fasterxml.jackson.annotation.JsonFormat; |
| 17 | import com.fasterxml.jackson.annotation.JsonProperty; | 14 | import com.fasterxml.jackson.annotation.JsonProperty; |
| 18 | -import com.fasterxml.jackson.core.type.TypeReference; | ||
| 19 | import com.yunpian.sdk.YunpianClient; | 15 | import com.yunpian.sdk.YunpianClient; |
| 20 | import com.yunpian.sdk.constant.YunpianConstant; | 16 | import com.yunpian.sdk.constant.YunpianConstant; |
| 21 | import com.yunpian.sdk.model.Result; | 17 | import com.yunpian.sdk.model.Result; |
| @@ -23,10 +19,11 @@ import com.yunpian.sdk.model.SmsSingleSend; | @@ -23,10 +19,11 @@ import com.yunpian.sdk.model.SmsSingleSend; | ||
| 23 | import lombok.Data; | 19 | import lombok.Data; |
| 24 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
| 25 | 21 | ||
| 26 | -import javax.servlet.ServletRequest; | ||
| 27 | -import java.io.UnsupportedEncodingException; | ||
| 28 | -import java.net.URLEncoder; | ||
| 29 | import java.util.*; | 22 | import java.util.*; |
| 23 | +import java.util.stream.Collectors; | ||
| 24 | + | ||
| 25 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 26 | +import static cn.iocoder.dashboard.util.date.DateUtils.TIME_ZONE_DEFAULT; | ||
| 30 | 27 | ||
| 31 | /** | 28 | /** |
| 32 | * 云片短信客户端的实现类 | 29 | * 云片短信客户端的实现类 |
| @@ -42,9 +39,6 @@ public class YunpianSmsClient extends AbstractSmsClient { | @@ -42,9 +39,6 @@ public class YunpianSmsClient extends AbstractSmsClient { | ||
| 42 | */ | 39 | */ |
| 43 | private volatile YunpianClient client; | 40 | private volatile YunpianClient client; |
| 44 | 41 | ||
| 45 | - private final TypeReference<List<Map<String, String>>> callbackType = new TypeReference<List<Map<String, String>>>() { | ||
| 46 | - }; | ||
| 47 | - | ||
| 48 | public YunpianSmsClient(SmsChannelProperties properties) { | 42 | public YunpianSmsClient(SmsChannelProperties properties) { |
| 49 | super(properties, new YunpianSmsCodeMapping()); | 43 | super(properties, new YunpianSmsCodeMapping()); |
| 50 | } | 44 | } |
| @@ -105,28 +99,17 @@ public class YunpianSmsClient extends AbstractSmsClient { | @@ -105,28 +99,17 @@ public class YunpianSmsClient extends AbstractSmsClient { | ||
| 105 | return sendResult.getMsg() + " => " + sendResult.getDetail(); | 99 | return sendResult.getMsg() + " => " + sendResult.getDetail(); |
| 106 | } | 100 | } |
| 107 | 101 | ||
| 108 | - /** | ||
| 109 | - * 从 request 中获取请求中传入的短信发送结果信息 | ||
| 110 | - * | ||
| 111 | - * @param request 回调请求 | ||
| 112 | - * @return 短信发送结果信息 | ||
| 113 | - * @throws UnsupportedEncodingException 解码异常 | ||
| 114 | - */ | ||
| 115 | - private Map<String, String> getRequestParams(ServletRequest request) throws UnsupportedEncodingException { | ||
| 116 | - Map<String, String[]> parameterMap = request.getParameterMap(); | ||
| 117 | - String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS); | ||
| 118 | - String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8); | ||
| 119 | - List<Map<String, String>> paramList = JsonUtils.parseObject(encode, callbackType); | ||
| 120 | - if (CollectionUtil.isNotEmpty(paramList)) { | ||
| 121 | - return paramList.get(0); | ||
| 122 | - } | ||
| 123 | - throw new IllegalArgumentException("YunpianSmsClient getRequestParams fail! can't format RequestParam: " | ||
| 124 | - + JsonUtils.toJsonString(request.getParameterMap())); | ||
| 125 | - } | ||
| 126 | - | ||
| 127 | @Override | 102 | @Override |
| 128 | - protected SmsCommonResult<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { | ||
| 129 | - return null; | 103 | + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { |
| 104 | + List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); | ||
| 105 | + return statuses.stream().map(status -> { | ||
| 106 | + SmsReceiveRespDTO resp = new SmsReceiveRespDTO(); | ||
| 107 | + resp.setSuccess(Objects.equals(status.getReportStatus(), "SUCCESS")); | ||
| 108 | + resp.setErrorCode(status.getErrorMsg()).setErrorMsg(status.getErrorDetail()); | ||
| 109 | + resp.setMobile(status.getMobile()).setReceiveTime(status.getUserReceiveTime()); | ||
| 110 | + resp.setSerialNo(String.valueOf(status.getSid())).setLogId(status.getUid()); | ||
| 111 | + return resp; | ||
| 112 | + }).collect(Collectors.toList()); | ||
| 130 | } | 113 | } |
| 131 | 114 | ||
| 132 | /** | 115 | /** |
| @@ -140,6 +123,24 @@ public class YunpianSmsClient extends AbstractSmsClient { | @@ -140,6 +123,24 @@ public class YunpianSmsClient extends AbstractSmsClient { | ||
| 140 | public static class SmsReceiveStatus { | 123 | public static class SmsReceiveStatus { |
| 141 | 124 | ||
| 142 | /** | 125 | /** |
| 126 | + * 接收状态 | ||
| 127 | + * | ||
| 128 | + * 目前仅有 SUCCESS / FAIL,所以使用 Boolean 接收 | ||
| 129 | + */ | ||
| 130 | + @JsonProperty("report_status") | ||
| 131 | + private String reportStatus; | ||
| 132 | + /** | ||
| 133 | + * 接收手机号 | ||
| 134 | + */ | ||
| 135 | + private String mobile; | ||
| 136 | + /** | ||
| 137 | + * 运营商返回的代码,如:"DB:0103" | ||
| 138 | + * | ||
| 139 | + * 由于不同运营商信息不同,此字段仅供参考; | ||
| 140 | + */ | ||
| 141 | + @JsonProperty("error_msg") | ||
| 142 | + private String errorMsg; | ||
| 143 | + /** | ||
| 143 | * 运营商反馈代码的中文解释 | 144 | * 运营商反馈代码的中文解释 |
| 144 | * | 145 | * |
| 145 | * 默认不推送此字段,如需推送,请联系客服 | 146 | * 默认不推送此字段,如需推送,请联系客服 |
| @@ -160,26 +161,8 @@ public class YunpianSmsClient extends AbstractSmsClient { | @@ -160,26 +161,8 @@ public class YunpianSmsClient extends AbstractSmsClient { | ||
| 160 | * 用户接收时间 | 161 | * 用户接收时间 |
| 161 | */ | 162 | */ |
| 162 | @JsonProperty("user_receive_time") | 163 | @JsonProperty("user_receive_time") |
| 163 | - @JsonFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | 164 | + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) |
| 164 | private Date userReceiveTime; | 165 | private Date userReceiveTime; |
| 165 | - /** | ||
| 166 | - * 运营商返回的代码,如:"DB:0103" | ||
| 167 | - * | ||
| 168 | - * 由于不同运营商信息不同,此字段仅供参考; | ||
| 169 | - */ | ||
| 170 | - @JsonProperty("error_msg") | ||
| 171 | - private String errorMsg; | ||
| 172 | - /** | ||
| 173 | - * 接收手机号 | ||
| 174 | - */ | ||
| 175 | - private String mobile; | ||
| 176 | - /** | ||
| 177 | - * 接收状态 | ||
| 178 | - * | ||
| 179 | - * 目前仅有 SUCCESS / FAIL,所以使用 Boolean 接收 | ||
| 180 | - */ | ||
| 181 | - @JsonProperty("report_status") | ||
| 182 | - private String reportStatus; | ||
| 183 | 166 | ||
| 184 | } | 167 | } |
| 185 | 168 |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
| @@ -152,7 +152,7 @@ public class SysSmsServiceImpl implements SysSmsService { | @@ -152,7 +152,7 @@ public class SysSmsServiceImpl implements SysSmsService { | ||
| 152 | SmsClient smsClient = smsClientFactory.getSmsClient(channelCode); | 152 | SmsClient smsClient = smsClientFactory.getSmsClient(channelCode); |
| 153 | Assert.notNull(smsClient, String.format("短信客户端(%s) 不存在", channelCode)); | 153 | Assert.notNull(smsClient, String.format("短信客户端(%s) 不存在", channelCode)); |
| 154 | // 解析内容 | 154 | // 解析内容 |
| 155 | - SmsCommonResult<SmsReceiveRespDTO> receiveResult = smsClient.parseSmsReceiveStatus(text); | 155 | + List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text); |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | } | 158 | } |
src/main/java/cn/iocoder/dashboard/util/date/DateUtils.java
| @@ -9,6 +9,11 @@ import java.util.Date; | @@ -9,6 +9,11 @@ import java.util.Date; | ||
| 9 | */ | 9 | */ |
| 10 | public class DateUtils { | 10 | public class DateUtils { |
| 11 | 11 | ||
| 12 | + /** | ||
| 13 | + * 时区 - 默认 | ||
| 14 | + */ | ||
| 15 | + public static final String TIME_ZONE_DEFAULT = "GMT+8"; | ||
| 16 | + | ||
| 12 | public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; | 17 | public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; |
| 13 | 18 | ||
| 14 | public static Date addTime(Duration duration) { | 19 | public static Date addTime(Duration duration) { |
src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java
| @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.util.json; | @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.util.json; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.ArrayUtil; | 3 | import cn.hutool.core.util.ArrayUtil; |
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | -import cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian.YunpianSmsClient; | ||
| 6 | import com.fasterxml.jackson.core.JsonProcessingException; | 5 | import com.fasterxml.jackson.core.JsonProcessingException; |
| 7 | import com.fasterxml.jackson.core.type.TypeReference; | 6 | import com.fasterxml.jackson.core.type.TypeReference; |
| 8 | import com.fasterxml.jackson.databind.ObjectMapper; | 7 | import com.fasterxml.jackson.databind.ObjectMapper; |
| @@ -80,10 +79,4 @@ public class JsonUtils { | @@ -80,10 +79,4 @@ public class JsonUtils { | ||
| 80 | } | 79 | } |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | - public static void main(String[] args) { | ||
| 84 | - String text = "[{\"sid\":9527,\"uid\":null,\"user_receive_time\":\"2014-03-17 22:55:21\",\"error_msg\":\"\",\"mobile\":\"15205201314\",\"report_status\":\"SUCCESS\"},{\"sid\":9528,\"uid\":null,\"user_receive_time\":\"2014-03-17 22:55:23\",\"error_msg\":\"\",\"mobile\":\"15212341234\",\"report_status\":\"SUCCESS\"}]"; | ||
| 85 | - List<YunpianSmsClient.SmsReceiveStatus> result = parseArray(text, YunpianSmsClient.SmsReceiveStatus.class); | ||
| 86 | - System.out.println(result); | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | } | 82 | } |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java renamed to src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientIntegrationTest.java
| @@ -13,7 +13,7 @@ import java.util.List; | @@ -13,7 +13,7 @@ import java.util.List; | ||
| 13 | /** | 13 | /** |
| 14 | * {@link AliyunSmsClient} 的集成测试 | 14 | * {@link AliyunSmsClient} 的集成测试 |
| 15 | */ | 15 | */ |
| 16 | -public class AliyunSmsClientTest { | 16 | +public class AliyunSmsClientIntegrationTest { |
| 17 | 17 | ||
| 18 | @Test | 18 | @Test |
| 19 | public void testSend() { | 19 | public void testSend() { |
src/test-integration/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsServiceIntegrationTest.java
| @@ -34,7 +34,7 @@ public class SysSmsServiceIntegrationTest extends BaseDbAndRedisIntegrationTest | @@ -34,7 +34,7 @@ public class SysSmsServiceIntegrationTest extends BaseDbAndRedisIntegrationTest | ||
| 34 | private SysUserService userService; | 34 | private SysUserService userService; |
| 35 | 35 | ||
| 36 | @Test | 36 | @Test |
| 37 | - public void testSendSingleSms_云片发送成功() { | 37 | + public void testSendSingleSms_yunpianSuccess() { |
| 38 | // 参数准备 | 38 | // 参数准备 |
| 39 | String mobile = "15601691399"; | 39 | String mobile = "15601691399"; |
| 40 | Long userId = 1L; | 40 | Long userId = 1L; |
| @@ -50,7 +50,7 @@ public class SysSmsServiceIntegrationTest extends BaseDbAndRedisIntegrationTest | @@ -50,7 +50,7 @@ public class SysSmsServiceIntegrationTest extends BaseDbAndRedisIntegrationTest | ||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | @Test | 52 | @Test |
| 53 | - public void testSendSingleSms_阿里云发送成功() { | 53 | + public void testSendSingleSms_aliyunSuccess() { |
| 54 | // 参数准备 | 54 | // 参数准备 |
| 55 | String mobile = "15601691399"; | 55 | String mobile = "15601691399"; |
| 56 | Long userId = 1L; | 56 | Long userId = 1L; |
src/test/java/cn/iocoder/dashboard/framework/package-info.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework; |
src/test/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||
| 4 | +import cn.iocoder.dashboard.util.date.DateUtils; | ||
| 5 | +import org.junit.jupiter.api.Test; | ||
| 6 | +import org.mockito.InjectMocks; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| 11 | +import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * {@link AliyunSmsClient} 的单元测试 | ||
| 15 | + */ | ||
| 16 | +public class AliyunSmsClientTest { | ||
| 17 | + | ||
| 18 | + @InjectMocks | ||
| 19 | + private final AliyunSmsClient smsClient = new AliyunSmsClient(null); | ||
| 20 | + | ||
| 21 | + @Test | ||
| 22 | + void doInit() { | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + @Test | ||
| 26 | + void doSendSms() { | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Test | ||
| 30 | + public void testDoTParseSmsReceiveStatus() throws Throwable { | ||
| 31 | + // 准备参数 | ||
| 32 | + String text = "[\n" + | ||
| 33 | + " {\n" + | ||
| 34 | + " \"phone_number\" : \"13900000001\",\n" + | ||
| 35 | + " \"send_time\" : \"2017-01-01 11:12:13\",\n" + | ||
| 36 | + " \"report_time\" : \"2017-02-02 22:23:24\",\n" + | ||
| 37 | + " \"success\" : true,\n" + | ||
| 38 | + " \"err_code\" : \"DELIVERED\",\n" + | ||
| 39 | + " \"err_msg\" : \"用户接收成功\",\n" + | ||
| 40 | + " \"sms_size\" : \"1\",\n" + | ||
| 41 | + " \"biz_id\" : \"12345\",\n" + | ||
| 42 | + " \"out_id\" : \"67890\"\n" + | ||
| 43 | + " }\n" + | ||
| 44 | + "]"; | ||
| 45 | + // mock 方法 | ||
| 46 | + | ||
| 47 | + // 调用 | ||
| 48 | + List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text); | ||
| 49 | + // 断言 | ||
| 50 | + assertEquals(1, statuses.size()); | ||
| 51 | + assertTrue(statuses.get(0).getSuccess()); | ||
| 52 | + assertEquals("DELIVERED", statuses.get(0).getErrorCode()); | ||
| 53 | + assertEquals("用户接收成功", statuses.get(0).getErrorMsg()); | ||
| 54 | + assertEquals("13900000001", statuses.get(0).getMobile()); | ||
| 55 | + assertEquals(DateUtils.buildTime(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime()); | ||
| 56 | + assertEquals("12345", statuses.get(0).getSerialNo()); | ||
| 57 | + assertEquals(67890L, statuses.get(0).getLogId()); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | +} |
src/test/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||
| 4 | +import cn.iocoder.dashboard.util.date.DateUtils; | ||
| 5 | +import org.junit.jupiter.api.Test; | ||
| 6 | +import org.mockito.InjectMocks; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 对 {@link YunpianSmsClient} 的单元测试 | ||
| 14 | + */ | ||
| 15 | +public class YunpianSmsClientTest { | ||
| 16 | + | ||
| 17 | + @InjectMocks | ||
| 18 | + private final YunpianSmsClient smsClient = new YunpianSmsClient(null); | ||
| 19 | + | ||
| 20 | + @Test | ||
| 21 | + void doInit() { | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + @Test | ||
| 25 | + void doSendSms() { | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Test | ||
| 29 | + void testDoParseSmsReceiveStatus() throws Throwable { | ||
| 30 | + // 准备参数 | ||
| 31 | + String text = "[{\"sid\":9527,\"uid\":1024,\"user_receive_time\":\"2014-03-17 22:55:21\",\"error_msg\":\"\",\"mobile\":\"15205201314\",\"report_status\":\"SUCCESS\"}]"; | ||
| 32 | + // mock 方法 | ||
| 33 | + | ||
| 34 | + // 调用 | ||
| 35 | + | ||
| 36 | + // 断言 | ||
| 37 | + // 调用 | ||
| 38 | + List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text); | ||
| 39 | + // 断言 | ||
| 40 | + assertEquals(1, statuses.size()); | ||
| 41 | + assertTrue(statuses.get(0).getSuccess()); | ||
| 42 | + assertEquals("", statuses.get(0).getErrorCode()); | ||
| 43 | + assertNull(statuses.get(0).getErrorMsg()); | ||
| 44 | + assertEquals("15205201314", statuses.get(0).getMobile()); | ||
| 45 | + assertEquals(DateUtils.buildTime(2014, 3, 17, 22, 55, 21), statuses.get(0).getReceiveTime()); | ||
| 46 | + assertEquals("9527", statuses.get(0).getSerialNo()); | ||
| 47 | + assertEquals(1024L, statuses.get(0).getLogId()); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | +} |