Commit c91833a504242b511c623ab220c1aa105ac17e59

Authored by YunaiV
1 parent 0d0110ec

定义短信回调的结果

Showing 17 changed files with 222 additions and 184 deletions
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
@@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
128 // 设置每个请求的权限 128 // 设置每个请求的权限
129 .authorizeRequests() 129 .authorizeRequests()
130 // 登陆的接口,可匿名访问 130 // 登陆的接口,可匿名访问
131 - .antMatchers(webProperties.getApiPrefix() + "/login").anonymous() 131 + .antMatchers(api("/login")).anonymous()
132 // 通用的接口,可匿名访问 132 // 通用的接口,可匿名访问
133 - .antMatchers( webProperties.getApiPrefix() + "/system/captcha/**").anonymous() 133 + .antMatchers(api("/system/captcha/**")).anonymous()
134 // 静态资源,可匿名访问 134 // 静态资源,可匿名访问
135 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() 135 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
136 // 文件的获取接口,可匿名访问 136 // 文件的获取接口,可匿名访问
137 - .antMatchers(webProperties.getApiPrefix() + "/infra/file/get/**").anonymous() 137 + .antMatchers(api("/infra/file/get/**")).anonymous()
138 // Swagger 接口文档 138 // Swagger 接口文档
139 .antMatchers("/swagger-ui.html").anonymous() 139 .antMatchers("/swagger-ui.html").anonymous()
140 .antMatchers("/swagger-resources/**").anonymous() 140 .antMatchers("/swagger-resources/**").anonymous()
@@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
148 .antMatchers("/actuator/**").anonymous() 148 .antMatchers("/actuator/**").anonymous()
149 // Druid 监控 149 // Druid 监控
150 .antMatchers("/druid/**").anonymous() 150 .antMatchers("/druid/**").anonymous()
  151 + // 短信回调 API
  152 + .antMatchers(api("/system/sms/callback/**")).anonymous()
151 // 除上面外的所有请求全部需要鉴权认证 153 // 除上面外的所有请求全部需要鉴权认证
152 .anyRequest().authenticated() 154 .anyRequest().authenticated()
153 .and() 155 .and()
154 .headers().frameOptions().disable(); 156 .headers().frameOptions().disable();
155 - httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); 157 + httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler);
156 // 添加 JWT Filter 158 // 添加 JWT Filter
157 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 159 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
158 } 160 }
159 161
  162 + private String api(String url) {
  163 + return webProperties.getApiPrefix() + url;
  164 + }
  165 +
160 } 166 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
1 package cn.iocoder.dashboard.framework.sms.core.client; 1 package cn.iocoder.dashboard.framework.sms.core.client;
2 2
3 import cn.iocoder.dashboard.common.core.KeyValue; 3 import cn.iocoder.dashboard.common.core.KeyValue;
4 -import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail; 4 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
5 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; 5 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
6 6
7 -import javax.servlet.ServletRequest;  
8 import java.util.List; 7 import java.util.List;
9 8
10 /** 9 /**
@@ -31,15 +30,15 @@ public interface SmsClient { @@ -31,15 +30,15 @@ public interface SmsClient {
31 * @param templateParams 短信模板参数 30 * @param templateParams 短信模板参数
32 * @return 短信发送结果 31 * @return 短信发送结果
33 */ 32 */
34 - SmsCommonResult<SmsSendRespDTO> send(Long logId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams); 33 + SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams);
35 34
36 - // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX  
37 /** 35 /**
38 - * 短信发送回调请求处理 36 + * 解析接收短信的接收结果
39 * 37 *
40 - * @param request 请求  
41 - * @return 短信发送结果 38 + * @param text 结果
  39 + * @return 结果内容
  40 + * @throws Throwable 当解析 text 发生异常时,则会抛出异常
42 */ 41 */
43 - SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception; 42 + SmsCommonResult<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable;
44 43
45 } 44 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java
@@ -19,6 +19,14 @@ public interface SmsClientFactory { @@ -19,6 +19,14 @@ public interface SmsClientFactory {
19 SmsClient getSmsClient(Long channelId); 19 SmsClient getSmsClient(Long channelId);
20 20
21 /** 21 /**
  22 + * 获得短信 Client
  23 + *
  24 + * @param channelCode 渠道编码
  25 + * @return 短信 Client
  26 + */
  27 + SmsClient getSmsClient(String channelCode);
  28 +
  29 + /**
22 * 创建短信 Client 30 * 创建短信 Client
23 * 31 *
24 * @param properties 配置对象 32 * @param properties 配置对象
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsResultDetail.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java
@@ -7,10 +7,12 @@ import java.io.Serializable; @@ -7,10 +7,12 @@ import java.io.Serializable;
7 import java.util.Date; 7 import java.util.Date;
8 8
9 /** 9 /**
10 - * 消息内容实体类 10 + * 消息接收 Response DTO
  11 + *
  12 + * @author 芋道源码
11 */ 13 */
12 @Data 14 @Data
13 -public class SmsResultDetail implements Serializable { 15 +public class SmsReceiveRespDTO implements Serializable {
14 16
15 /** 17 /**
16 * 唯一标识 18 * 唯一标识
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
@@ -4,6 +4,7 @@ import cn.iocoder.dashboard.common.core.KeyValue; @@ -4,6 +4,7 @@ import cn.iocoder.dashboard.common.core.KeyValue;
4 import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; 4 import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
5 import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping; 5 import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
6 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; 6 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
  7 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
7 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; 8 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
8 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; 9 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
9 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
@@ -68,12 +69,12 @@ public abstract class AbstractSmsClient implements SmsClient { @@ -68,12 +69,12 @@ public abstract class AbstractSmsClient implements SmsClient {
68 } 69 }
69 70
70 @Override 71 @Override
71 - public final SmsCommonResult<SmsSendRespDTO> send(Long logId, String mobile,  
72 - String apiTemplateId, List<KeyValue<String, Object>> templateParams) { 72 + public final SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile,
  73 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
73 // 执行短信发送 74 // 执行短信发送
74 SmsCommonResult<SmsSendRespDTO> result; 75 SmsCommonResult<SmsSendRespDTO> result;
75 try { 76 try {
76 - result = doSend(logId, mobile, apiTemplateId, templateParams); 77 + result = doSendSms(logId, mobile, apiTemplateId, templateParams);
77 } catch (Throwable ex) { 78 } catch (Throwable ex) {
78 // 打印异常日志 79 // 打印异常日志
79 log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]", 80 log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
@@ -84,17 +85,20 @@ public abstract class AbstractSmsClient implements SmsClient { @@ -84,17 +85,20 @@ public abstract class AbstractSmsClient implements SmsClient {
84 return result; 85 return result;
85 } 86 }
86 87
87 - /**  
88 - * 发送消息  
89 - *  
90 - * @param sendLogId 发送日志编号  
91 - * @param mobile 手机号  
92 - * @param apiTemplateId 短信 API 的模板编号  
93 - * @param templateParams 短信模板参数  
94 - * @return 短信发送结果  
95 - */  
96 - protected abstract SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile,  
97 - String apiTemplateId, List<KeyValue<String, Object>> templateParams) 88 + protected abstract SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  89 + String apiTemplateId, List<KeyValue<String, Object>> templateParams)
98 throws Throwable; 90 throws Throwable;
99 91
  92 + @Override
  93 + public SmsCommonResult<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
  94 + try {
  95 + return doParseSmsReceiveStatus(text);
  96 + } catch (Throwable ex) {
  97 + log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex);
  98 + throw ex;
  99 + }
  100 + }
  101 +
  102 + protected abstract SmsCommonResult<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable;
  103 +
100 } 104 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java
@@ -10,8 +10,9 @@ import lombok.extern.slf4j.Slf4j; @@ -10,8 +10,9 @@ import lombok.extern.slf4j.Slf4j;
10 import org.springframework.util.Assert; 10 import org.springframework.util.Assert;
11 import org.springframework.validation.annotation.Validated; 11 import org.springframework.validation.annotation.Validated;
12 12
13 -import java.util.Map; 13 +import java.util.Arrays;
14 import java.util.concurrent.ConcurrentHashMap; 14 import java.util.concurrent.ConcurrentHashMap;
  15 +import java.util.concurrent.ConcurrentMap;
15 16
16 /** 17 /**
17 * 短信客户端工厂接口 18 * 短信客户端工厂接口
@@ -26,20 +27,45 @@ public class SmsClientFactoryImpl implements SmsClientFactory { @@ -26,20 +27,45 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
26 * 短信客户端 Map 27 * 短信客户端 Map
27 * key:渠道编号,使用 {@link SmsChannelProperties#getId()} 28 * key:渠道编号,使用 {@link SmsChannelProperties#getId()}
28 */ 29 */
29 - private final Map<Long, AbstractSmsClient> clients = new ConcurrentHashMap<>(); 30 + private final ConcurrentMap<Long, AbstractSmsClient> channelIdClients = new ConcurrentHashMap<>();
  31 +
  32 + /**
  33 + * 短信客户端 Map
  34 + * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()}
  35 + *
  36 + * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。
  37 + * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients}
  38 + */
  39 + private final ConcurrentMap<String, AbstractSmsClient> channelCodeClients = new ConcurrentHashMap<>();
  40 +
  41 + public SmsClientFactoryImpl() {
  42 + // 初始化 channelCodeClients 集合
  43 + Arrays.stream(SmsChannelEnum.values()).forEach(channel -> {
  44 + // 创建一个空的 SmsChannelProperties 对象
  45 + SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode());
  46 + // 创建 Sms 客户端
  47 + AbstractSmsClient smsClient = createSmsClient(properties);
  48 + channelCodeClients.put(channel.getCode(), smsClient);
  49 + });
  50 + }
30 51
31 @Override 52 @Override
32 public SmsClient getSmsClient(Long channelId) { 53 public SmsClient getSmsClient(Long channelId) {
33 - return clients.get(channelId); 54 + return channelIdClients.get(channelId);
  55 + }
  56 +
  57 + @Override
  58 + public SmsClient getSmsClient(String channelCode) {
  59 + return channelCodeClients.get(channelCode);
34 } 60 }
35 61
36 @Override 62 @Override
37 public void createOrUpdateSmsClient(SmsChannelProperties properties) { 63 public void createOrUpdateSmsClient(SmsChannelProperties properties) {
38 - AbstractSmsClient client = clients.get(properties.getId()); 64 + AbstractSmsClient client = channelIdClients.get(properties.getId());
39 if (client == null) { 65 if (client == null) {
40 client = this.createSmsClient(properties); 66 client = this.createSmsClient(properties);
41 client.init(); 67 client.init();
42 - clients.put(client.getId(), client); 68 + channelIdClients.put(client.getId(), client);
43 } else { 69 } else {
44 client.refresh(properties); 70 client.refresh(properties);
45 } 71 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
@@ -5,7 +5,7 @@ import cn.hutool.core.date.DateUtil; @@ -5,7 +5,7 @@ import cn.hutool.core.date.DateUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.dashboard.common.core.KeyValue; 6 import cn.iocoder.dashboard.common.core.KeyValue;
7 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; 7 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
8 -import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail; 8 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
9 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; 9 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
10 import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; 10 import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
11 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; 11 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
@@ -61,8 +61,8 @@ public class AliyunSmsClient extends AbstractSmsClient { @@ -61,8 +61,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
61 } 61 }
62 62
63 @Override 63 @Override
64 - protected SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile,  
65 - String apiTemplateId, List<KeyValue<String, Object>> templateParams) { 64 + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  65 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
66 // 构建参数 66 // 构建参数
67 SendSmsRequest request = new SendSmsRequest(); 67 SendSmsRequest request = new SendSmsRequest();
68 request.setSysMethod(MethodType.POST); 68 request.setSysMethod(MethodType.POST);
@@ -110,11 +110,10 @@ public class AliyunSmsClient extends AbstractSmsClient { @@ -110,11 +110,10 @@ public class AliyunSmsClient extends AbstractSmsClient {
110 * @return 110 * @return
111 * @throws Exception 111 * @throws Exception
112 */ 112 */
113 - @Override  
114 - public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception { 113 + public SmsReceiveRespDTO smsSendCallbackHandle(ServletRequest request) throws Exception {
115 BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); 114 BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
116 String paramStr = reader.readLine(); 115 String paramStr = reader.readLine();
117 - List<Map<String, Object>> params = JsonUtils.parseByType(paramStr, new TypeReference<List<Map<String, Object>>>() { 116 + List<Map<String, Object>> params = JsonUtils.parseObject(paramStr, new TypeReference<List<Map<String, Object>>>() {
118 }); 117 });
119 if (CollectionUtil.isNotEmpty(params)) { 118 if (CollectionUtil.isNotEmpty(params)) {
120 Map<String, Object> sendResultParamMap = params.get(0); 119 Map<String, Object> sendResultParamMap = params.get(0);
@@ -123,6 +122,11 @@ public class AliyunSmsClient extends AbstractSmsClient { @@ -123,6 +122,11 @@ public class AliyunSmsClient extends AbstractSmsClient {
123 return null; 122 return null;
124 } 123 }
125 124
  125 + @Override
  126 + protected SmsCommonResult<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  127 + return null;
  128 + }
  129 +
126 /** 130 /**
127 * 短信发送回调辅助类 131 * 短信发送回调辅助类
128 */ 132 */
@@ -168,8 +172,8 @@ public class AliyunSmsClient extends AbstractSmsClient { @@ -168,8 +172,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
168 return sendResultParamMap.get(CallbackField.OUT_ID).toString(); 172 return sendResultParamMap.get(CallbackField.OUT_ID).toString();
169 } 173 }
170 174
171 - public SmsResultDetail toResultDetail() {  
172 - SmsResultDetail resultDetail = new SmsResultDetail(); 175 + public SmsReceiveRespDTO toResultDetail() {
  176 + SmsReceiveRespDTO resultDetail = new SmsReceiveRespDTO();
173 resultDetail.setSendStatus(getSendStatus()); 177 resultDetail.setSendStatus(getSendStatus());
174 resultDetail.setApiId(getBizId()); 178 resultDetail.setApiId(getBizId());
175 resultDetail.setSendTime(getSendTime()); 179 resultDetail.setSendTime(getSendTime());
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
@@ -2,33 +2,31 @@ package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; @@ -2,33 +2,31 @@ 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; 4 import cn.hutool.core.collection.CollectionUtil;
5 -import cn.hutool.core.date.DateUtil;  
6 import cn.hutool.core.util.CharsetUtil; 5 import cn.hutool.core.util.CharsetUtil;
7 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
8 import cn.hutool.core.util.URLUtil; 7 import cn.hutool.core.util.URLUtil;
9 import cn.iocoder.dashboard.common.core.KeyValue; 8 import cn.iocoder.dashboard.common.core.KeyValue;
10 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; 9 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
11 -import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail; 10 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
12 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; 11 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
13 import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; 12 import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
14 -import cn.iocoder.dashboard.framework.sms.core.enums.SmsConstants;  
15 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; 13 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
16 -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; 14 +import cn.iocoder.dashboard.util.date.DateUtils;
17 import cn.iocoder.dashboard.util.json.JsonUtils; 15 import cn.iocoder.dashboard.util.json.JsonUtils;
  16 +import com.fasterxml.jackson.annotation.JsonFormat;
  17 +import com.fasterxml.jackson.annotation.JsonProperty;
18 import com.fasterxml.jackson.core.type.TypeReference; 18 import com.fasterxml.jackson.core.type.TypeReference;
19 import com.yunpian.sdk.YunpianClient; 19 import com.yunpian.sdk.YunpianClient;
20 import com.yunpian.sdk.constant.YunpianConstant; 20 import com.yunpian.sdk.constant.YunpianConstant;
21 import com.yunpian.sdk.model.Result; 21 import com.yunpian.sdk.model.Result;
22 import com.yunpian.sdk.model.SmsSingleSend; 22 import com.yunpian.sdk.model.SmsSingleSend;
  23 +import lombok.Data;
23 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
24 25
25 import javax.servlet.ServletRequest; 26 import javax.servlet.ServletRequest;
26 import java.io.UnsupportedEncodingException; 27 import java.io.UnsupportedEncodingException;
27 import java.net.URLEncoder; 28 import java.net.URLEncoder;
28 -import java.util.HashMap;  
29 -import java.util.List;  
30 -import java.util.Map;  
31 -import java.util.StringJoiner; 29 +import java.util.*;
32 30
33 /** 31 /**
34 * 云片短信客户端的实现类 32 * 云片短信客户端的实现类
@@ -65,8 +63,8 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -65,8 +63,8 @@ public class YunpianSmsClient extends AbstractSmsClient {
65 } 63 }
66 64
67 @Override 65 @Override
68 - protected SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile,  
69 - String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable { 66 + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  67 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
70 // 构建参数 68 // 构建参数
71 Map<String, String> request = new HashMap<>(); 69 Map<String, String> request = new HashMap<>();
72 request.put(YunpianConstant.APIKEY, properties.getApiKey()); 70 request.put(YunpianConstant.APIKEY, properties.getApiKey());
@@ -74,7 +72,7 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -74,7 +72,7 @@ public class YunpianSmsClient extends AbstractSmsClient {
74 request.put(YunpianConstant.TPL_ID, apiTemplateId); 72 request.put(YunpianConstant.TPL_ID, apiTemplateId);
75 request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams)); 73 request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams));
76 request.put(YunpianConstant.UID, String.valueOf(sendLogId)); 74 request.put(YunpianConstant.UID, String.valueOf(sendLogId));
77 - request.put(Helper.CALLBACK, properties.getCallbackUrl()); 75 + request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl());
78 76
79 // 执行发送 77 // 执行发送
80 Result<SmsSingleSend> sendResult = client.sms().tpl_single_send(request); 78 Result<SmsSingleSend> sendResult = client.sms().tpl_single_send(request);
@@ -108,15 +106,6 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -108,15 +106,6 @@ public class YunpianSmsClient extends AbstractSmsClient {
108 } 106 }
109 107
110 /** 108 /**
111 - * 云片的比较复杂,又是加密又是套娃的  
112 - */  
113 - @Override  
114 - public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws UnsupportedEncodingException {  
115 - Map<String, String> map = getRequestParams(request);  
116 - return Helper.getSmsResultDetailByParam(map);  
117 - }  
118 -  
119 - /**  
120 * 从 request 中获取请求中传入的短信发送结果信息 109 * 从 request 中获取请求中传入的短信发送结果信息
121 * 110 *
122 * @param request 回调请求 111 * @param request 回调请求
@@ -127,7 +116,7 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -127,7 +116,7 @@ public class YunpianSmsClient extends AbstractSmsClient {
127 Map<String, String[]> parameterMap = request.getParameterMap(); 116 Map<String, String[]> parameterMap = request.getParameterMap();
128 String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS); 117 String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS);
129 String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8); 118 String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8);
130 - List<Map<String, String>> paramList = JsonUtils.parseByType(encode, callbackType); 119 + List<Map<String, String>> paramList = JsonUtils.parseObject(encode, callbackType);
131 if (CollectionUtil.isNotEmpty(paramList)) { 120 if (CollectionUtil.isNotEmpty(paramList)) {
132 return paramList.get(0); 121 return paramList.get(0);
133 } 122 }
@@ -135,46 +124,63 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -135,46 +124,63 @@ public class YunpianSmsClient extends AbstractSmsClient {
135 + JsonUtils.toJsonString(request.getParameterMap())); 124 + JsonUtils.toJsonString(request.getParameterMap()));
136 } 125 }
137 126
  127 + @Override
  128 + protected SmsCommonResult<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  129 + return null;
  130 + }
  131 +
138 /** 132 /**
139 - * 云片的回调函数的一些辅助方法 133 + * 短信接收状态
  134 + *
  135 + * 参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档
  136 + *
  137 + * @author 芋道源码
140 */ 138 */
141 - private static class Helper {  
142 -  
143 - //短信唯一标识  
144 - private final static String API_ID = "sid"; 139 + @Data
  140 + public static class SmsReceiveStatus {
  141 +
  142 + /**
  143 + * 运营商反馈代码的中文解释
  144 + *
  145 + * 默认不推送此字段,如需推送,请联系客服
  146 + */
  147 + @JsonProperty("error_detail")
  148 + private String errorDetail;
  149 + /**
  150 + * 短信编号
  151 + */
  152 + private Long sid;
  153 + /**
  154 + * 用户自定义 id
  155 + *
  156 + * 这里我们传递的是 SysSmsLogDO 的日志编号
  157 + */
  158 + private Long uid;
  159 + /**
  160 + * 用户接收时间
  161 + */
  162 + @JsonProperty("user_receive_time")
  163 + @JsonFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  164 + 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;
145 183
146 - //回调地址·  
147 - private final static String CALLBACK = "callback";  
148 -  
149 - //手机号  
150 - private final static String MOBILE = "mobile";  
151 -  
152 - //错误信息  
153 - private final static String ERROR_MSG = "error_msg";  
154 -  
155 - //用户接收时间 字符串 标准格式  
156 - private final static String USER_RECEIVE_TIME = "user_receive_time";  
157 -  
158 - //发送状态  
159 - private final static String REPORT_STATUS = "report_status";  
160 -  
161 - private static int getSendStatus(Map<String, String> map) {  
162 - String reportStatus = map.get(REPORT_STATUS);  
163 - return SmsConstants.SUCCESS.equals(reportStatus)  
164 - ? SysSmsSendStatusEnum.SUCCESS.getStatus()  
165 - : SysSmsSendStatusEnum.FAILURE.getStatus();  
166 - }  
167 -  
168 - public static SmsResultDetail getSmsResultDetailByParam(Map<String, String> map) {  
169 - SmsResultDetail detail = new SmsResultDetail();  
170 - detail.setPhone(map.get(MOBILE));  
171 - detail.setMessage(map.get(ERROR_MSG));  
172 - detail.setSendTime(DateUtil.parseTime(map.get(USER_RECEIVE_TIME)));  
173 - detail.setSendStatus(getSendStatus(map));  
174 - detail.setApiId(API_ID);  
175 -  
176 - detail.setCallbackResponseBody(SmsConstants.SUCCESS);  
177 - return detail;  
178 - }  
179 } 184 }
  185 +
180 } 186 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java
@@ -16,8 +16,9 @@ public enum SmsChannelEnum { @@ -16,8 +16,9 @@ public enum SmsChannelEnum {
16 16
17 YUN_PIAN("YUN_PIAN", "云片"), 17 YUN_PIAN("YUN_PIAN", "云片"),
18 ALIYUN("ALIYUN", "阿里云"), 18 ALIYUN("ALIYUN", "阿里云"),
19 - TENCENT("TENCENT", "腾讯云"),  
20 - HUA_WEI("HUA_WEI", "华为云"),; 19 +// TENCENT("TENCENT", "腾讯云"),
  20 +// HUA_WEI("HUA_WEI", "华为云"),
  21 + ;
21 22
22 /** 23 /**
23 * 编码 24 * 编码
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsConstants.java deleted
1 -package cn.iocoder.dashboard.framework.sms.core.enums;  
2 -  
3 -/**  
4 - * 短信相关常量类  
5 - *  
6 - * @author zzf  
7 - * @date 2021/3/5 10:42  
8 - */  
9 -public interface SmsConstants {  
10 -  
11 -  
12 - String COMMA = ",";  
13 -  
14 - String SUCCESS = "SUCCESS";  
15 -  
16 -}  
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsCallbackController.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms;
  2 +
  3 +import cn.hutool.core.util.URLUtil;
  4 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  5 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
  6 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
  7 +import io.swagger.annotations.Api;
  8 +import io.swagger.annotations.ApiImplicitParam;
  9 +import io.swagger.annotations.ApiOperation;
  10 +import org.springframework.web.bind.annotation.PostMapping;
  11 +import org.springframework.web.bind.annotation.RequestMapping;
  12 +import org.springframework.web.bind.annotation.RequestParam;
  13 +import org.springframework.web.bind.annotation.RestController;
  14 +
  15 +import javax.annotation.Resource;
  16 +
  17 +@Api(tags = "短信回调")
  18 +@RestController
  19 +@RequestMapping("/system/sms/callback")
  20 +public class SmsCallbackController {
  21 +
  22 + @Resource
  23 + private SysSmsService smsService;
  24 +
  25 + @PostMapping("/sms/yunpian")
  26 + @ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档")
  27 + @ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = Long.class)
  28 + public CommonResult<Boolean> receiveYunpianSmsStatus(@RequestParam("sms_status") String smsStatus) throws Throwable {
  29 + String text = URLUtil.decode(smsStatus); // decode 解码参数,因为它被 encode
  30 + smsService.receiveSmsStatus(SmsChannelEnum.YUN_PIAN.getCode(), text);
  31 + return CommonResult.success(true);
  32 + }
  33 +
  34 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java deleted
1 -package cn.iocoder.dashboard.modules.system.controller.sms;  
2 -  
3 -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;  
4 -import io.swagger.annotations.Api;  
5 -import io.swagger.annotations.ApiOperation;  
6 -import org.springframework.web.bind.annotation.PostMapping;  
7 -import org.springframework.web.bind.annotation.RequestMapping;  
8 -import org.springframework.web.bind.annotation.RestController;  
9 -  
10 -import javax.annotation.Resource;  
11 -import javax.servlet.ServletRequest;  
12 -  
13 -/**  
14 - * 短信默认回调接口  
15 - *  
16 - * @author zzf  
17 - * @date 2021/3/5 8:59  
18 - */  
19 -@Api(tags = "短信回调api")  
20 -@RestController  
21 -@RequestMapping("/sms/callback")  
22 -public class SmsDefaultCallbackController {  
23 -  
24 - @Resource  
25 - private SysSmsService smsService;  
26 -  
27 -  
28 - @ApiOperation(value = "短信发送回调接口")  
29 - @PostMapping("/sms-send")  
30 - public Object sendSmsCallback(ServletRequest request) {  
31 - return smsService.smsSendCallbackHandle(request);  
32 - }  
33 -  
34 -/*  
35 - @Resource  
36 - private SmsSendStreamProducer smsSendStreamProducer;  
37 -  
38 - @ApiOperation("redis stream测试")  
39 - @GetMapping("/test/redis/stream")  
40 - public void test() {  
41 - SmsBody smsBody = new SmsBody();  
42 - smsBody.setSmsLogId(1L);  
43 - smsBody.setTemplateCode("sdf");  
44 - smsBody.setTemplateContent("sdf");  
45 - smsSendStreamProducer.sendSmsSendMessage(smsBody, "18216466755");  
46 - }*/  
47 -  
48 -}  
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java
@@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.sms; @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.sms;
2 2
3 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; 3 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
4 4
5 -import javax.servlet.ServletRequest;  
6 import java.util.List; 5 import java.util.List;
7 import java.util.Map; 6 import java.util.Map;
8 7
@@ -24,11 +23,12 @@ public interface SysSmsService { @@ -24,11 +23,12 @@ public interface SysSmsService {
24 void doSendSms(SysSmsSendMessage message); 23 void doSendSms(SysSmsSendMessage message);
25 24
26 /** 25 /**
27 - * 处理短信发送回调函数 26 + * 接收短信的接收结果
28 * 27 *
29 - * @param request 请求  
30 - * @return 响应数据 28 + * @param channelCode 渠道编码
  29 + * @param text 结果内容
  30 + * @throws Throwable 处理失败时,抛出异常
31 */ 31 */
32 - Object smsSendCallbackHandle(ServletRequest request); 32 + void receiveSmsStatus(String channelCode, String text) throws Throwable;
33 33
34 } 34 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
@@ -7,6 +7,7 @@ import cn.iocoder.dashboard.common.enums.UserTypeEnum; @@ -7,6 +7,7 @@ import cn.iocoder.dashboard.common.enums.UserTypeEnum;
7 import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; 7 import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
8 import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; 8 import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
9 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; 9 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
  10 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
10 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; 11 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
11 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; 12 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
12 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; 13 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
@@ -21,7 +22,6 @@ import org.springframework.stereotype.Service; @@ -21,7 +22,6 @@ import org.springframework.stereotype.Service;
21 import org.springframework.util.Assert; 22 import org.springframework.util.Assert;
22 23
23 import javax.annotation.Resource; 24 import javax.annotation.Resource;
24 -import javax.servlet.ServletRequest;  
25 import java.util.List; 25 import java.util.List;
26 import java.util.Map; 26 import java.util.Map;
27 import java.util.Objects; 27 import java.util.Objects;
@@ -139,7 +139,7 @@ public class SysSmsServiceImpl implements SysSmsService { @@ -139,7 +139,7 @@ public class SysSmsServiceImpl implements SysSmsService {
139 SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId()); 139 SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
140 Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", message.getChannelId())); 140 Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", message.getChannelId()));
141 // 发送短信 141 // 发送短信
142 - SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.send(message.getLogId(), message.getMobile(), 142 + SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
143 message.getApiTemplateId(), message.getTemplateParams()); 143 message.getApiTemplateId(), message.getTemplateParams());
144 smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(), 144 smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(),
145 sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(), 145 sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(),
@@ -147,11 +147,12 @@ public class SysSmsServiceImpl implements SysSmsService { @@ -147,11 +147,12 @@ public class SysSmsServiceImpl implements SysSmsService {
147 } 147 }
148 148
149 @Override 149 @Override
150 - public Object smsSendCallbackHandle(ServletRequest request) {  
151 -// SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request);  
152 -// logService.updateSendLogByResultDetail(smsResultDetail);  
153 -// return smsResultDetail.getCallbackResponseBody();  
154 - return null; 150 + public void receiveSmsStatus(String channelCode, String text) throws Throwable {
  151 + // 获得渠道对应的 SmsClient 客户端
  152 + SmsClient smsClient = smsClientFactory.getSmsClient(channelCode);
  153 + Assert.notNull(smsClient, String.format("短信客户端(%s) 不存在", channelCode));
  154 + // 解析内容
  155 + SmsCommonResult<SmsReceiveRespDTO> receiveResult = smsClient.parseSmsReceiveStatus(text);
155 } 156 }
156 157
157 } 158 }
src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java
@@ -2,12 +2,14 @@ package cn.iocoder.dashboard.util.json; @@ -2,12 +2,14 @@ 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;
5 import com.fasterxml.jackson.core.JsonProcessingException; 6 import com.fasterxml.jackson.core.JsonProcessingException;
6 import com.fasterxml.jackson.core.type.TypeReference; 7 import com.fasterxml.jackson.core.type.TypeReference;
7 import com.fasterxml.jackson.databind.ObjectMapper; 8 import com.fasterxml.jackson.databind.ObjectMapper;
8 9
9 import java.io.IOException; 10 import java.io.IOException;
10 -import java.util.Set; 11 +import java.util.ArrayList;
  12 +import java.util.List;
11 13
12 /** 14 /**
13 * JSON 工具类 15 * JSON 工具类
@@ -59,7 +61,7 @@ public class JsonUtils { @@ -59,7 +61,7 @@ public class JsonUtils {
59 } 61 }
60 } 62 }
61 63
62 - public static Object parseObject(String text, TypeReference<Set<Long>> typeReference) { 64 + public static <T> T parseObject(String text, TypeReference<T> typeReference) {
63 try { 65 try {
64 return objectMapper.readValue(text, typeReference); 66 return objectMapper.readValue(text, typeReference);
65 } catch (IOException e) { 67 } catch (IOException e) {
@@ -67,12 +69,21 @@ public class JsonUtils { @@ -67,12 +69,21 @@ public class JsonUtils {
67 } 69 }
68 } 70 }
69 71
70 - public static <T> T parseByType(String text, TypeReference<T> typeReference) { 72 + public static <T> List<T> parseArray(String text, Class<T> clazz) {
  73 + if (StrUtil.isEmpty(text)) {
  74 + return new ArrayList<>();
  75 + }
71 try { 76 try {
72 - return objectMapper.readValue(text, typeReference); 77 + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
73 } catch (IOException e) { 78 } catch (IOException e) {
74 throw new RuntimeException(e); 79 throw new RuntimeException(e);
75 } 80 }
76 } 81 }
77 82
  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 +
78 } 89 }
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
@@ -32,7 +32,7 @@ public class AliyunSmsClientTest { @@ -32,7 +32,7 @@ public class AliyunSmsClientTest {
32 templateParams.add(new KeyValue<>("code", "1024")); 32 templateParams.add(new KeyValue<>("code", "1024"));
33 // templateParams.put("operation", "嘿嘿"); 33 // templateParams.put("operation", "嘿嘿");
34 // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); 34 // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams);
35 - SmsCommonResult<SmsSendRespDTO> result = smsClient.send(1L, "15601691399", "SMS_207945135", templateParams); 35 + SmsCommonResult<SmsSendRespDTO> result = smsClient.sendSms(1L, "15601691399", "SMS_207945135", templateParams);
36 System.out.println(result); 36 System.out.println(result);
37 } 37 }
38 38
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java
@@ -31,7 +31,7 @@ public class YunpianSmsClientIntegrationTest { @@ -31,7 +31,7 @@ public class YunpianSmsClientIntegrationTest {
31 templateParams.add(new KeyValue<>("code", "1024")); 31 templateParams.add(new KeyValue<>("code", "1024"));
32 templateParams.add(new KeyValue<>("operation", "嘿嘿")); 32 templateParams.add(new KeyValue<>("operation", "嘿嘿"));
33 // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); 33 // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams);
34 - SmsCommonResult<SmsSendRespDTO> result = smsClient.send(1L, "15601691399", "4383920", templateParams); 34 + SmsCommonResult<SmsSendRespDTO> result = smsClient.sendSms(1L, "15601691399", "4383920", templateParams);
35 System.out.println(result); 35 System.out.println(result);
36 } 36 }
37 37