diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java index bb55e1e71..e20431153 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java @@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { // 设置每个请求的权限 .authorizeRequests() // 登陆的接口,可匿名访问 - .antMatchers(webProperties.getApiPrefix() + "/login").anonymous() + .antMatchers(api("/login")).anonymous() // 通用的接口,可匿名访问 - .antMatchers( webProperties.getApiPrefix() + "/system/captcha/**").anonymous() + .antMatchers(api("/system/captcha/**")).anonymous() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() // 文件的获取接口,可匿名访问 - .antMatchers(webProperties.getApiPrefix() + "/infra/file/get/**").anonymous() + .antMatchers(api("/infra/file/get/**")).anonymous() // Swagger 接口文档 .antMatchers("/swagger-ui.html").anonymous() .antMatchers("/swagger-resources/**").anonymous() @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .antMatchers("/actuator/**").anonymous() // Druid 监控 .antMatchers("/druid/**").anonymous() + // 短信回调 API + .antMatchers(api("/system/sms/callback/**")).anonymous() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); - httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); + httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 添加 JWT Filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } + private String api(String url) { + return webProperties.getApiPrefix() + url; + } + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java index f343b540f..fc84c5df2 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java @@ -1,10 +1,9 @@ package cn.iocoder.dashboard.framework.sms.core.client; import cn.iocoder.dashboard.common.core.KeyValue; -import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail; +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; -import javax.servlet.ServletRequest; import java.util.List; /** @@ -31,15 +30,15 @@ public interface SmsClient { * @param templateParams 短信模板参数 * @return 短信发送结果 */ - SmsCommonResult send(Long logId, String mobile, String apiTemplateId, List> templateParams); + SmsCommonResult sendSms(Long logId, String mobile, String apiTemplateId, List> templateParams); - // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX /** - * 短信发送回调请求处理 + * 解析接收短信的接收结果 * - * @param request 请求 - * @return 短信发送结果 + * @param text 结果 + * @return 结果内容 + * @throws Throwable 当解析 text 发生异常时,则会抛出异常 */ - SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception; + SmsCommonResult parseSmsReceiveStatus(String text) throws Throwable; } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java index f4bc87dd4..83fb88c24 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java @@ -18,6 +18,14 @@ public interface SmsClientFactory { */ SmsClient getSmsClient(Long channelId); + /** + * 获得短信 Client + * + * @param channelCode 渠道编码 + * @return 短信 Client + */ + SmsClient getSmsClient(String channelCode); + /** * 创建短信 Client * diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsResultDetail.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java similarity index 85% rename from src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsResultDetail.java rename to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java index f8e19db82..b120ca3dd 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsResultDetail.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java @@ -7,10 +7,12 @@ import java.io.Serializable; import java.util.Date; /** - * 消息内容实体类 + * 消息接收 Response DTO + * + * @author 芋道源码 */ @Data -public class SmsResultDetail implements Serializable { +public class SmsReceiveRespDTO implements Serializable { /** * 唯一标识 diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java index 6e334cf0a..8eb681868 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java @@ -4,6 +4,7 @@ import cn.iocoder.dashboard.common.core.KeyValue; import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping; import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; import lombok.extern.slf4j.Slf4j; @@ -68,12 +69,12 @@ public abstract class AbstractSmsClient implements SmsClient { } @Override - public final SmsCommonResult send(Long logId, String mobile, - String apiTemplateId, List> templateParams) { + public final SmsCommonResult sendSms(Long logId, String mobile, + String apiTemplateId, List> templateParams) { // 执行短信发送 SmsCommonResult result; try { - result = doSend(logId, mobile, apiTemplateId, templateParams); + result = doSendSms(logId, mobile, apiTemplateId, templateParams); } catch (Throwable ex) { // 打印异常日志 log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]", @@ -84,17 +85,20 @@ public abstract class AbstractSmsClient implements SmsClient { return result; } - /** - * 发送消息 - * - * @param sendLogId 发送日志编号 - * @param mobile 手机号 - * @param apiTemplateId 短信 API 的模板编号 - * @param templateParams 短信模板参数 - * @return 短信发送结果 - */ - protected abstract SmsCommonResult doSend(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) + protected abstract SmsCommonResult doSendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) throws Throwable; + @Override + public SmsCommonResult parseSmsReceiveStatus(String text) throws Throwable { + try { + return doParseSmsReceiveStatus(text); + } catch (Throwable ex) { + log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex); + throw ex; + } + } + + protected abstract SmsCommonResult doParseSmsReceiveStatus(String text) throws Throwable; + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java index 4f0db5c02..cd5197193 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java @@ -10,8 +10,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; -import java.util.Map; +import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * 短信客户端工厂接口 @@ -26,20 +27,45 @@ public class SmsClientFactoryImpl implements SmsClientFactory { * 短信客户端 Map * key:渠道编号,使用 {@link SmsChannelProperties#getId()} */ - private final Map clients = new ConcurrentHashMap<>(); + private final ConcurrentMap channelIdClients = new ConcurrentHashMap<>(); + + /** + * 短信客户端 Map + * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()} + * + * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。 + * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients} + */ + private final ConcurrentMap channelCodeClients = new ConcurrentHashMap<>(); + + public SmsClientFactoryImpl() { + // 初始化 channelCodeClients 集合 + Arrays.stream(SmsChannelEnum.values()).forEach(channel -> { + // 创建一个空的 SmsChannelProperties 对象 + SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode()); + // 创建 Sms 客户端 + AbstractSmsClient smsClient = createSmsClient(properties); + channelCodeClients.put(channel.getCode(), smsClient); + }); + } @Override public SmsClient getSmsClient(Long channelId) { - return clients.get(channelId); + return channelIdClients.get(channelId); + } + + @Override + public SmsClient getSmsClient(String channelCode) { + return channelCodeClients.get(channelCode); } @Override public void createOrUpdateSmsClient(SmsChannelProperties properties) { - AbstractSmsClient client = clients.get(properties.getId()); + AbstractSmsClient client = channelIdClients.get(properties.getId()); if (client == null) { client = this.createSmsClient(properties); client.init(); - clients.put(client.getId(), client); + channelIdClients.put(client.getId(), client); } else { client.refresh(properties); } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java index acc8a0193..cf7d25160 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java @@ -5,7 +5,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.dashboard.common.core.KeyValue; import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; -import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail; +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; @@ -61,8 +61,8 @@ public class AliyunSmsClient extends AbstractSmsClient { } @Override - protected SmsCommonResult doSend(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) { + protected SmsCommonResult doSendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) { // 构建参数 SendSmsRequest request = new SendSmsRequest(); request.setSysMethod(MethodType.POST); @@ -110,11 +110,10 @@ public class AliyunSmsClient extends AbstractSmsClient { * @return * @throws Exception */ - @Override - public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception { + public SmsReceiveRespDTO smsSendCallbackHandle(ServletRequest request) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); String paramStr = reader.readLine(); - List> params = JsonUtils.parseByType(paramStr, new TypeReference>>() { + List> params = JsonUtils.parseObject(paramStr, new TypeReference>>() { }); if (CollectionUtil.isNotEmpty(params)) { Map sendResultParamMap = params.get(0); @@ -123,6 +122,11 @@ public class AliyunSmsClient extends AbstractSmsClient { return null; } + @Override + protected SmsCommonResult doParseSmsReceiveStatus(String text) throws Throwable { + return null; + } + /** * 短信发送回调辅助类 */ @@ -168,8 +172,8 @@ public class AliyunSmsClient extends AbstractSmsClient { return sendResultParamMap.get(CallbackField.OUT_ID).toString(); } - public SmsResultDetail toResultDetail() { - SmsResultDetail resultDetail = new SmsResultDetail(); + public SmsReceiveRespDTO toResultDetail() { + SmsReceiveRespDTO resultDetail = new SmsReceiveRespDTO(); resultDetail.setSendStatus(getSendStatus()); resultDetail.setApiId(getBizId()); resultDetail.setSendTime(getSendTime()); diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java index 249d8c481..d67bc55be 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import cn.iocoder.dashboard.common.core.KeyValue; import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; -import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail; +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.enums.SmsConstants; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; +import cn.iocoder.dashboard.util.date.DateUtils; import cn.iocoder.dashboard.util.json.JsonUtils; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; import com.yunpian.sdk.YunpianClient; import com.yunpian.sdk.constant.YunpianConstant; import com.yunpian.sdk.model.Result; import com.yunpian.sdk.model.SmsSingleSend; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import javax.servlet.ServletRequest; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; +import java.util.*; /** * 云片短信客户端的实现类 @@ -65,8 +63,8 @@ public class YunpianSmsClient extends AbstractSmsClient { } @Override - protected SmsCommonResult doSend(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) throws Throwable { + protected SmsCommonResult doSendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) throws Throwable { // 构建参数 Map request = new HashMap<>(); request.put(YunpianConstant.APIKEY, properties.getApiKey()); @@ -74,7 +72,7 @@ public class YunpianSmsClient extends AbstractSmsClient { request.put(YunpianConstant.TPL_ID, apiTemplateId); request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams)); request.put(YunpianConstant.UID, String.valueOf(sendLogId)); - request.put(Helper.CALLBACK, properties.getCallbackUrl()); + request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl()); // 执行发送 Result sendResult = client.sms().tpl_single_send(request); @@ -107,15 +105,6 @@ public class YunpianSmsClient extends AbstractSmsClient { return sendResult.getMsg() + " => " + sendResult.getDetail(); } - /** - * 云片的比较复杂,又是加密又是套娃的 - */ - @Override - public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws UnsupportedEncodingException { - Map map = getRequestParams(request); - return Helper.getSmsResultDetailByParam(map); - } - /** * 从 request 中获取请求中传入的短信发送结果信息 * @@ -127,7 +116,7 @@ public class YunpianSmsClient extends AbstractSmsClient { Map parameterMap = request.getParameterMap(); String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS); String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8); - List> paramList = JsonUtils.parseByType(encode, callbackType); + List> paramList = JsonUtils.parseObject(encode, callbackType); if (CollectionUtil.isNotEmpty(paramList)) { return paramList.get(0); } @@ -135,46 +124,63 @@ public class YunpianSmsClient extends AbstractSmsClient { + 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 map) { - String reportStatus = map.get(REPORT_STATUS); - return SmsConstants.SUCCESS.equals(reportStatus) - ? SysSmsSendStatusEnum.SUCCESS.getStatus() - : SysSmsSendStatusEnum.FAILURE.getStatus(); - } - - public static SmsResultDetail getSmsResultDetailByParam(Map 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; - } + @Override + protected SmsCommonResult doParseSmsReceiveStatus(String text) throws Throwable { + return null; } + + /** + * 短信接收状态 + * + * 参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档 + * + * @author 芋道源码 + */ + @Data + public static class SmsReceiveStatus { + + /** + * 运营商反馈代码的中文解释 + * + * 默认不推送此字段,如需推送,请联系客服 + */ + @JsonProperty("error_detail") + private String errorDetail; + /** + * 短信编号 + */ + private Long sid; + /** + * 用户自定义 id + * + * 这里我们传递的是 SysSmsLogDO 的日志编号 + */ + private Long uid; + /** + * 用户接收时间 + */ + @JsonProperty("user_receive_time") + @JsonFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date userReceiveTime; + /** + * 运营商返回的代码,如:"DB:0103" + * + * 由于不同运营商信息不同,此字段仅供参考; + */ + @JsonProperty("error_msg") + private String errorMsg; + /** + * 接收手机号 + */ + private String mobile; + /** + * 接收状态 + * + * 目前仅有 SUCCESS / FAIL,所以使用 Boolean 接收 + */ + @JsonProperty("report_status") + private String reportStatus; + + } + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java index 213034882..f2e675198 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java @@ -16,8 +16,9 @@ public enum SmsChannelEnum { YUN_PIAN("YUN_PIAN", "云片"), ALIYUN("ALIYUN", "阿里云"), - TENCENT("TENCENT", "腾讯云"), - HUA_WEI("HUA_WEI", "华为云"),; +// TENCENT("TENCENT", "腾讯云"), +// HUA_WEI("HUA_WEI", "华为云"), + ; /** * 编码 diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsConstants.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsConstants.java deleted file mode 100644 index e699db96e..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsConstants.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.dashboard.framework.sms.core.enums; - -/** - * 短信相关常量类 - * - * @author zzf - * @date 2021/3/5 10:42 - */ -public interface SmsConstants { - - - String COMMA = ","; - - String SUCCESS = "SUCCESS"; - -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsCallbackController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsCallbackController.java new file mode 100644 index 000000000..f8e845380 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsCallbackController.java @@ -0,0 +1,34 @@ +package cn.iocoder.dashboard.modules.system.controller.sms; + +import cn.hutool.core.util.URLUtil; +import cn.iocoder.dashboard.common.pojo.CommonResult; +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Api(tags = "短信回调") +@RestController +@RequestMapping("/system/sms/callback") +public class SmsCallbackController { + + @Resource + private SysSmsService smsService; + + @PostMapping("/sms/yunpian") + @ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档") + @ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = Long.class) + public CommonResult receiveYunpianSmsStatus(@RequestParam("sms_status") String smsStatus) throws Throwable { + String text = URLUtil.decode(smsStatus); // decode 解码参数,因为它被 encode + smsService.receiveSmsStatus(SmsChannelEnum.YUN_PIAN.getCode(), text); + return CommonResult.success(true); + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java deleted file mode 100644 index 3d4661076..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.dashboard.modules.system.controller.sms; - -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.servlet.ServletRequest; - -/** - * 短信默认回调接口 - * - * @author zzf - * @date 2021/3/5 8:59 - */ -@Api(tags = "短信回调api") -@RestController -@RequestMapping("/sms/callback") -public class SmsDefaultCallbackController { - - @Resource - private SysSmsService smsService; - - - @ApiOperation(value = "短信发送回调接口") - @PostMapping("/sms-send") - public Object sendSmsCallback(ServletRequest request) { - return smsService.smsSendCallbackHandle(request); - } - -/* - @Resource - private SmsSendStreamProducer smsSendStreamProducer; - - @ApiOperation("redis stream测试") - @GetMapping("/test/redis/stream") - public void test() { - SmsBody smsBody = new SmsBody(); - smsBody.setSmsLogId(1L); - smsBody.setTemplateCode("sdf"); - smsBody.setTemplateContent("sdf"); - smsSendStreamProducer.sendSmsSendMessage(smsBody, "18216466755"); - }*/ - -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java index 34fcf4e9c..ec3691377 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.sms; import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; -import javax.servlet.ServletRequest; import java.util.List; import java.util.Map; @@ -24,11 +23,12 @@ public interface SysSmsService { void doSendSms(SysSmsSendMessage message); /** - * 处理短信发送回调函数 + * 接收短信的接收结果 * - * @param request 请求 - * @return 响应数据 + * @param channelCode 渠道编码 + * @param text 结果内容 + * @throws Throwable 处理失败时,抛出异常 */ - Object smsSendCallbackHandle(ServletRequest request); + void receiveSmsStatus(String channelCode, String text) throws Throwable; } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java index 87f8298da..2e6624de6 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.dashboard.common.enums.UserTypeEnum; import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; @@ -21,7 +22,6 @@ import org.springframework.stereotype.Service; import org.springframework.util.Assert; import javax.annotation.Resource; -import javax.servlet.ServletRequest; import java.util.List; import java.util.Map; import java.util.Objects; @@ -139,7 +139,7 @@ public class SysSmsServiceImpl implements SysSmsService { SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId()); Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", message.getChannelId())); // 发送短信 - SmsCommonResult sendResult = smsClient.send(message.getLogId(), message.getMobile(), + SmsCommonResult sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(), message.getApiTemplateId(), message.getTemplateParams()); smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(), sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(), @@ -147,11 +147,12 @@ public class SysSmsServiceImpl implements SysSmsService { } @Override - public Object smsSendCallbackHandle(ServletRequest request) { -// SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request); -// logService.updateSendLogByResultDetail(smsResultDetail); -// return smsResultDetail.getCallbackResponseBody(); - return null; + public void receiveSmsStatus(String channelCode, String text) throws Throwable { + // 获得渠道对应的 SmsClient 客户端 + SmsClient smsClient = smsClientFactory.getSmsClient(channelCode); + Assert.notNull(smsClient, String.format("短信客户端(%s) 不存在", channelCode)); + // 解析内容 + SmsCommonResult receiveResult = smsClient.parseSmsReceiveStatus(text); } } diff --git a/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java b/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java index 2a735e214..c7a9483eb 100644 --- a/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java +++ b/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java @@ -2,12 +2,14 @@ package cn.iocoder.dashboard.util.json; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian.YunpianSmsClient; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * JSON 工具类 @@ -59,7 +61,7 @@ public class JsonUtils { } } - public static Object parseObject(String text, TypeReference> typeReference) { + public static T parseObject(String text, TypeReference typeReference) { try { return objectMapper.readValue(text, typeReference); } catch (IOException e) { @@ -67,12 +69,21 @@ public class JsonUtils { } } - public static T parseByType(String text, TypeReference typeReference) { + public static List parseArray(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return new ArrayList<>(); + } try { - return objectMapper.readValue(text, typeReference); + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); } catch (IOException e) { throw new RuntimeException(e); } } + public static void main(String[] args) { + 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\"}]"; + List result = parseArray(text, YunpianSmsClient.SmsReceiveStatus.class); + System.out.println(result); + } + } diff --git a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java index baae08886..4e3fccba4 100644 --- a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java +++ b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java @@ -32,7 +32,7 @@ public class AliyunSmsClientTest { templateParams.add(new KeyValue<>("code", "1024")); // templateParams.put("operation", "嘿嘿"); // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); - SmsCommonResult result = smsClient.send(1L, "15601691399", "SMS_207945135", templateParams); + SmsCommonResult result = smsClient.sendSms(1L, "15601691399", "SMS_207945135", templateParams); System.out.println(result); } diff --git a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java index 6e4d4315c..039ed3bff 100644 --- a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java +++ b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java @@ -31,7 +31,7 @@ public class YunpianSmsClientIntegrationTest { templateParams.add(new KeyValue<>("code", "1024")); templateParams.add(new KeyValue<>("operation", "嘿嘿")); // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); - SmsCommonResult result = smsClient.send(1L, "15601691399", "4383920", templateParams); + SmsCommonResult result = smsClient.sendSms(1L, "15601691399", "4383920", templateParams); System.out.println(result); }