新增腾讯短信

This commit is contained in:
liaozetao
2023-08-14 16:11:47 +08:00
parent 43ab404312
commit f201b22349
18 changed files with 596 additions and 63 deletions

View File

@@ -1817,6 +1817,11 @@ public class Constant {
*/
public static final String INCOME_METHOD = "income_method";
/**
* 短信发送方式(阿里 aliyun 腾讯 tencent)
*/
public static final String SMS_SDK_TYPE = "sms_sdk_type";
}
public static class ActiveMq {

View File

@@ -5,6 +5,8 @@ package com.accompany.common.constant;
*/
public class SmsConstant {
public static final int SMS_EXPIRE_MINUTE = 3;
/**
* 短信过期时间60秒
*/
@@ -46,4 +48,11 @@ public class SmsConstant {
public static final String OK = "OK";
public static final String BUSINESS_LIMIT_CONTROL = "isv.BUSINESS_LIMIT_CONTROL";
}
public interface ResCode {
String OK = "Ok";
String LimitExceeded = "LimitExceeded";
}
}

View File

@@ -45,4 +45,12 @@ public class SpringContextHolder {
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
@SuppressWarnings("unchecked")
public static <T> T getBeanOfClass(Class<T> requiredType) {
String simpleName = requiredType.getSimpleName();
char[] ch = simpleName.toCharArray();
ch[0] += 32;
return (T) applicationContext.getBean(new String(ch));
}
}

View File

@@ -24,6 +24,12 @@
<artifactId>accompany-core</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>${tencentcloud-sdk-java.version}</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,46 @@
package com.accompany.sms.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* @author: liaozetao
* @date: 2023/8/14 11:43
* @description:
*/
@Data
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "tencent.sms")
public class TencentSmsConfig {
private String secretId;
private String secretKey;
private String appId;
private String appKey;
private Map<String, AppConfig> apps;
@Data
public static class AppConfig {
/**
* 签名ID
*/
private String signId;
/**
* 模版ID
*/
private String templateId;
}
}

View File

@@ -0,0 +1,65 @@
package com.accompany.sms.config;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: liaozetao
* @date: 2023/8/14 11:55
* @description:
*/
@Configuration
public class TencentSmsConfiguration {
@Autowired
private TencentSmsConfig tencentSmsConfig;
@Bean
public Credential credential() {
/* 必要步骤:
* 实例化一个认证对象入参需要传入腾讯云账户密钥对secretIdsecretKey。
* 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
* 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
* 以免泄露密钥对危及你的财产安全。
* SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
return new Credential(tencentSmsConfig.getSecretId(), tencentSmsConfig.getSecretKey());
}
@Bean
public HttpProfile httpProfile() {
// 实例化一个http选项可选没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// 设置代理(无需要直接忽略)
// httpProfile.setProxyHost("真实代理ip");
// httpProfile.setProxyPort(真实代理端口);
/* SDK默认使用POST方法。
* 如果你一定要使用GET方法可以在这里设置。GET方法无法处理一些较大的请求 */
httpProfile.setReqMethod("POST");
/* SDK有默认的超时时间非必要请不要进行调整
* 如有需要请在代码中查阅以获取最新的默认值 */
httpProfile.setConnTimeout(60);
/* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
httpProfile.setEndpoint("sms.tencentcloudapi.com");
return httpProfile;
}
@Bean
public SmsClient smsClient(Credential credential, HttpProfile httpProfile) {
/* 非必要步骤:
* 实例化一个客户端配置对象,可以指定超时时间等配置 */
ClientProfile clientProfile = new ClientProfile();
/* SDK默认用TC3-HMAC-SHA256进行签名
* 非必要请不要修改这个字段 */
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
/* 实例化要请求产品(以sms为例)的client对象
* 第二个参数是地域信息可以直接填写字符串ap-guangzhou支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
return new SmsClient(credential, "ap-guangzhou", clientProfile);
}
}

View File

@@ -0,0 +1,13 @@
package com.accompany.sms.enums;
/**
* @author: liaozetao
* @date: 2023/8/14 15:14
* @description:
*/
public enum SmsTypeEnum {
ALIYUN,
TENCENT;
}

View File

@@ -1,7 +1,15 @@
package com.accompany.sms.service;
import cn.hutool.core.util.StrUtil;
import com.accompany.common.constant.Constant;
import com.accompany.common.constant.SmsConstant;
import com.accompany.common.device.DeviceInfo;
import com.accompany.common.status.BusiStatus;
import com.accompany.common.utils.StringUtils;
import com.accompany.core.exception.ServiceException;
import com.accompany.sms.common.BeanMapper;
import com.accompany.sms.config.AliyunSmsConfig;
import com.accompany.sms.result.AliyunSmsRet;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
@@ -10,13 +18,9 @@ import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.accompany.sms.config.AliyunSmsConfig;
import com.accompany.sms.result.AliyunSmsRet;
import com.accompany.sms.common.BeanMapper;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -41,38 +45,41 @@ public class AliyunSmsService implements InitializingBean {
public AliyunSmsRet sendSms(String mobile, String code, DeviceInfo deviceInfo) throws ClientException {
return sendSms(mobile, (deviceInfo != null ? deviceInfo.getApp() : null), code);
}
public AliyunSmsRet sendSms(String mobile, String app, String code) throws ClientException {
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
//必填:待发送手机号
request.setPhoneNumbers(mobile);
//必填:短信签名-可在短信控制台中找到
// 根据app参数选择不同的签名如果没有配置特殊的则使用默认的
if (aliyunSmsConfig.getAppSignNameDic() != null && deviceInfo != null && aliyunSmsConfig.getAppSignNameDic().containsKey(deviceInfo.getApp())) {
String signName = aliyunSmsConfig.getAppSignNameDic().get(deviceInfo.getApp());
log.info("{} use signName {}", deviceInfo.getApp(), signName);
if (aliyunSmsConfig.getAppSignNameDic() != null && StrUtil.isNotEmpty(app) && aliyunSmsConfig.getAppSignNameDic().containsKey(app)) {
String signName = aliyunSmsConfig.getAppSignNameDic().get(app);
log.info("{} use signName {}", app, signName);
request.setSignName(signName);
} else {
request.setSignName(aliyunSmsConfig.getSignName());
}
request.setTemplateCode(aliyunSmsConfig.getTemplateCode());
// 国际区分
if (!mobile.startsWith(Constant.CHINA_MAINLAND_PHONE_AREA_CODE)) {
request.setSignName(aliyunSmsConfig.getIntlSignName());
request.setTemplateCode(aliyunSmsConfig.getIntlTemplateCode());
}
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam(gson.toJson(ImmutableMap.of("code", code)));
//可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
//hint 此处可能会抛出异常注意catch
SendSmsResponse acsResponse = acsClient.getAcsResponse(request);
AliyunSmsRet smsRet = BeanMapper.map(acsResponse, AliyunSmsRet.class);
return smsRet;
log.info("acsResponse : {}", JSONObject.toJSON(acsResponse));
return BeanMapper.map(acsResponse, AliyunSmsRet.class);
}
/**
* 发送短信
*
* @param mobile 手机号
* @param templateId 模板id
* @param param 参数
@@ -86,6 +93,7 @@ public class AliyunSmsService implements InitializingBean {
/**
* 发送短信
*
* @param mobile 手机号
* @param templateId 模板id
* @param signName 签名
@@ -105,10 +113,24 @@ public class AliyunSmsService implements InitializingBean {
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam(gson.toJson(param));
SendSmsResponse acsResponse = acsClient.getAcsResponse(request);
AliyunSmsRet smsRet = BeanMapper.map(acsResponse, AliyunSmsRet.class);
return smsRet;
return BeanMapper.map(acsResponse, AliyunSmsRet.class);
}
/**
* 校验
*
* @param resCode
*/
public void validResponseCode(String resCode) {
if (StringUtils.isNotEmpty(resCode) &&
resCode.equalsIgnoreCase(SmsConstant.AliyunSmsResponseCode.BUSINESS_LIMIT_CONTROL)) {
throw new ServiceException(BusiStatus.SMS_SENDING_FREQUENCY_TOO_HIGH);
}
if (StringUtils.isEmpty(resCode) ||
(!resCode.equalsIgnoreCase(SmsConstant.AliyunSmsResponseCode.OK))) {
throw new ServiceException(BusiStatus.SMS_SEND_ERROR);
}
}
@Override

View File

@@ -11,6 +11,7 @@ import com.accompany.common.utils.StringUtils;
import com.accompany.core.exception.ServiceException;
import com.accompany.core.service.SysConfService;
import com.accompany.core.service.base.BaseService;
import com.accompany.sms.enums.SmsTypeEnum;
import com.accompany.sms.result.AliyunSmsRet;
import com.aliyuncs.exceptions.ClientException;
import com.google.i18n.phonenumbers.NumberParseException;
@@ -38,11 +39,10 @@ public class SmsService extends BaseService {
@Autowired
private SysConfService sysConfService;
public BusiResult sendSmsCode(String mobile, Integer type, DeviceInfo deviceInfo, String ip, String code) {
Boolean exits = jedisService.exits(getSmsIntervalKey(mobile,type));
public BusiResult<Void> sendSmsCode(String mobile, Integer type, DeviceInfo deviceInfo, String ip, String code) {
Boolean exits = jedisService.exits(getSmsIntervalKey(mobile, type));
if (exits != null && exits) {
return new BusiResult(BusiStatus.SMS_NOT_EXPIRED);
return new BusiResult<>(BusiStatus.SMS_NOT_EXPIRED);
}
if (StringUtils.isBlank(code)) {
code = String.format("%d", RandomUtil.getFiveRandomNumber());
@@ -52,13 +52,12 @@ public class SmsService extends BaseService {
String phone = "+" + mobile;
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Phonenumber.PhoneNumber swissNumberProto = phoneUtil.parse(phone, null);
String realPhone = String.valueOf(swissNumberProto.getCountryCode()) + String.valueOf(swissNumberProto.getNationalNumber());
String realPhone = swissNumberProto.getCountryCode() + String.valueOf(swissNumberProto.getNationalNumber());
AliyunSmsRet smsRet = aliyunSmsService.sendSms(realPhone, code, deviceInfo);
log.info("sendSmsCode to {}, response msg:{}", realPhone, gson.toJson(smsRet));
String smsType = sysConfService.getDefaultSysConfValueById(Constant.SysConfId.SMS_SDK_TYPE, SmsTypeEnum.ALIYUN.name().toLowerCase());
log.info("sendSmsCode to {}, response msg : {}", realPhone, gson.toJson(smsRet));
// 写入短信发送频率
jedisService.setex(getSmsIntervalKey(mobile,type), SmsConstant.SMS_SEND_INTERVAL_SECONDS, "");
jedisService.setex(getSmsIntervalKey(mobile, type), SmsConstant.SMS_SEND_INTERVAL_SECONDS, "");
// 写入缓存
jedisService.setex(getSmsKey(mobile), SmsConstant.SMS_EXPIRE_SECONDS, code);
// 新增短信记录
@@ -72,18 +71,17 @@ public class SmsService extends BaseService {
(!resCode.equalsIgnoreCase(SmsConstant.AliyunSmsResponseCode.OK))) {
throw new ServiceException(BusiStatus.SMS_SEND_ERROR);
}
} catch (ClientException | NumberParseException e) {
} catch (RuntimeException | ClientException | NumberParseException e) {
log.error("sendSmsCode error. mobile:{}", mobile, e);
throw new ServiceException(BusiStatus.SMS_SEND_ERROR);
}
return new BusiResult(BusiStatus.SMS_SEND_SUCCESS);
return new BusiResult<>(BusiStatus.SMS_SEND_SUCCESS);
}
public boolean verifySmsCode(String mobile, String code) {
logger.info("verifySmsCode mobile : {}, code : {}", mobile, code);
// 是否关掉验证码验证
Boolean checkSms = Boolean.parseBoolean(
sysConfService.getDefaultSysConfValueById(Constant.SysConfId.VERIFY_SMS_CODE, "true"));
boolean checkSms = Boolean.parseBoolean(sysConfService.getDefaultSysConfValueById(Constant.SysConfId.VERIFY_SMS_CODE, "true"));
if (!checkSms) {
return true;
}
@@ -93,7 +91,7 @@ public class SmsService extends BaseService {
String smsKey = getSmsKey(mobile);
String codeCache = jedisService.get(smsKey);
logger.info("verifySmsCode smsKey : {}, codeCache : {}", smsKey, codeCache);
if (org.apache.commons.lang3.StringUtils.equalsIgnoreCase(codeCache, code)) {
if (StringUtils.equalsIgnoreCase(codeCache, code)) {
jedisService.del(getSmsKey(mobile));
return true;
}
@@ -102,6 +100,7 @@ public class SmsService extends BaseService {
/**
* 获取短信验证码的RedisKey
*
* @param mobile
* @return
*/
@@ -111,11 +110,12 @@ public class SmsService extends BaseService {
/**
* 短信验证码发送频率RedisKey
*
* @param mobile
* @param type
* @return
*/
private String getSmsIntervalKey(String mobile, Integer type) {
return RedisKey.sms_send_interval.getKey(mobile+"_"+type);
return RedisKey.sms_send_interval.getKey(mobile + "_" + type);
}
}

View File

@@ -0,0 +1,30 @@
package com.accompany.sms.service;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
/**
* @author: liaozetao
* @date: 2023/8/14 11:52
* @description:
*/
public interface TencentSmsService {
/**
* 发送短信
*
* @param mobile
* @param app
* @param code
* @return
* @throws TencentCloudSDKException
*/
SendStatus sendSms(String mobile, String app, String code) throws TencentCloudSDKException;
/**
* 校验返回结果
*
* @param resCode
*/
void validResponseCode(String resCode);
}

View File

@@ -0,0 +1,105 @@
package com.accompany.sms.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.accompany.common.constant.SmsConstant;
import com.accompany.common.exception.ApiException;
import com.accompany.common.status.BusiStatus;
import com.accompany.common.utils.StringUtils;
import com.accompany.sms.config.TencentSmsConfig;
import com.accompany.sms.service.TencentSmsService;
import com.alibaba.fastjson.JSONObject;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author: liaozetao
* @date: 2023/8/14 11:52
* @description:
*/
@Slf4j
@Service
public class TencentSmsServiceImpl implements TencentSmsService {
@Autowired
private TencentSmsConfig tencentSmsConfig;
@Autowired
private SmsClient smsClient;
@Override
public SendStatus sendSms(String mobile, String app, String code) throws TencentCloudSDKException {
log.info("mobile : {}, app : {}, code : {}", mobile, app, code);
String appId = tencentSmsConfig.getAppId();
Map<String, TencentSmsConfig.AppConfig> apps = tencentSmsConfig.getApps();
if (CollectionUtil.isEmpty(apps) || !apps.containsKey(app)) {
log.info("sms config app : {}", app);
return null;
}
String signId = apps.get(app).getSignId();
String templateId = apps.get(app).getTemplateId();
/* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
* 你可以直接查询SDK源码确定接口有哪些属性可以设置
* 属性可能是基本类型,也可能引用了另一个数据结构
* 推荐使用IDE进行开发可以方便的跳转查阅各个接口和数据结构的文档说明 */
SendSmsRequest req = new SendSmsRequest();
/* 填充请求参数,这里request对象的成员变量即对应接口的入参
* 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接:
* 短信控制台: https://console.cloud.tencent.com/smsv2
* 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId示例如1400006666 */
// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
req.setSmsSdkAppId(appId);
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
/* 模板 ID: 必须填写已审核通过的模板 ID */
// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
req.setSignName(signId);
req.setTemplateId(templateId);
/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
String[] templateParamSet = {code, String.valueOf(SmsConstant.SMS_EXPIRE_MINUTE)};
req.setTemplateParamSet(templateParamSet);
/* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
* 示例如:+8613711112222 其中前面有一个+号 86为国家码13711112222为手机号最多不要超过200个手机号 */
mobile = "+" + mobile;
String[] phoneNumberSet = {mobile};
req.setPhoneNumberSet(phoneNumberSet);
/* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
SendSmsResponse res = smsClient.SendSms(req);
if (res != null) {
log.info("SendSmsResponse : {}", JSONObject.toJSON(res));
/* 当出现以下错误码时,快速解决方案参考
* [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
*/
return res.getSendStatusSet()[0];
}
return null;
}
@Override
public void validResponseCode(String resCode) {
if (SmsConstant.ResCode.OK.equals(resCode)) {
return;
}
if (StringUtils.isNotEmpty(resCode) && resCode.startsWith(SmsConstant.ResCode.LimitExceeded)) {
throw new ApiException(BusiStatus.SMS_SENDING_FREQUENCY_TOO_HIGH);
}
throw new ApiException(BusiStatus.SMS_SEND_ERROR);
}
}

View File

@@ -0,0 +1,49 @@
package com.accompany.sms.strategy;
import com.accompany.core.base.SpringContextHolder;
import com.accompany.sms.enums.SmsTypeEnum;
import com.accompany.sms.result.AliyunSmsRet;
import com.accompany.sms.service.AliyunSmsService;
import lombok.extern.slf4j.Slf4j;
/**
* @author: liaozetao
* @date: 2023/8/14 15:17
* @description:
*/
@Slf4j
public class AliYunSmsStrategy extends SmsStrategy {
private final AliyunSmsService aliyunSmsService;
public AliYunSmsStrategy() {
aliyunSmsService = SpringContextHolder.getBeanOfClass(AliyunSmsService.class);
}
@Override
String type() {
return SmsTypeEnum.ALIYUN.name().toLowerCase();
}
@Override
SmsResponse doSend(SmsContext context) {
SmsResponse response = new SmsResponse();
String mobile = context.getMobile();
String app = context.getApp();
String code = context.getCode();
try {
AliyunSmsRet aliyunSmsRet = aliyunSmsService.sendSms(mobile, app, code);
response.setCode(aliyunSmsRet.getCode());
response.setMessage(aliyunSmsRet.getMessage());
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
return response;
}
@Override
public void validResCode(String resCode) {
aliyunSmsService.validResponseCode(resCode);
}
}

View File

@@ -0,0 +1,27 @@
package com.accompany.sms.strategy;
import lombok.Data;
/**
* @author: liaozetao
* @date: 2023/8/14 14:57
* @description:
*/
@Data
public class SmsContext {
/**
* 手机号码
*/
private String mobile;
/**
* 应用
*/
private String app;
/**
* 验证码
*/
private String code;
}

View File

@@ -0,0 +1,27 @@
package com.accompany.sms.strategy;
import com.accompany.sms.enums.SmsTypeEnum;
/**
* @author: liaozetao
* @date: 2023/8/14 16:05
* @description:
*/
public class SmsFactory {
public SmsResponse send(String type, String mobile, String app, String code) {
SmsContext context = new SmsContext();
context.setMobile(mobile);
context.setApp(app);
context.setCode(code);
SmsStrategy smsStrategy = null;
if (SmsTypeEnum.ALIYUN.name().toLowerCase().equalsIgnoreCase(type)) {
smsStrategy = new AliYunSmsStrategy();
} else if (SmsTypeEnum.TENCENT.name().toLowerCase().equalsIgnoreCase(type)) {
smsStrategy = new TencentSmsStrategy();
} else {
smsStrategy = new AliYunSmsStrategy();
}
return smsStrategy.send(context);
}
}

View File

@@ -0,0 +1,22 @@
package com.accompany.sms.strategy;
import lombok.Data;
/**
* @author: liaozetao
* @date: 2023/8/14 15:06
* @description:
*/
@Data
public class SmsResponse {
/**
* 异常编码
*/
private String code;
/**
* 异常信息
*/
private String message;
}

View File

@@ -0,0 +1,42 @@
package com.accompany.sms.strategy;
/**
* @author: liaozetao
* @date: 2023/8/14 14:59
* @description:
*/
public abstract class SmsStrategy {
/**
* 类型
*
* @return
*/
abstract String type();
/**
* 发送验证码
*
* @param context
* @return
*/
public SmsResponse send(SmsContext context) {
return doSend(context);
}
/**
* 发送验证码
*
* @param context
* @return
*/
abstract SmsResponse doSend(SmsContext context);
/**
* 校验返回结果
*
* @param resCode
*/
public abstract void validResCode(String resCode);
}

View File

@@ -0,0 +1,50 @@
package com.accompany.sms.strategy;
import com.accompany.core.base.SpringContextHolder;
import com.accompany.sms.enums.SmsTypeEnum;
import com.accompany.sms.service.TencentSmsService;
import com.accompany.sms.service.impl.TencentSmsServiceImpl;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
import lombok.extern.slf4j.Slf4j;
/**
* @author: liaozetao
* @date: 2023/8/14 15:04
* @description:
*/
@Slf4j
public class TencentSmsStrategy extends SmsStrategy {
private final TencentSmsService tencentSmsService;
public TencentSmsStrategy() {
tencentSmsService = SpringContextHolder.getBeanOfClass(TencentSmsServiceImpl.class);
}
@Override
public String type() {
return SmsTypeEnum.TENCENT.name().toLowerCase();
}
@Override
public SmsResponse doSend(SmsContext context) {
SmsResponse response = new SmsResponse();
String mobile = context.getMobile();
String app = context.getApp();
String code = context.getCode();
try {
SendStatus sendStatus = tencentSmsService.sendSms(mobile, app, code);
response.setCode(sendStatus.getCode());
response.setMessage(sendStatus.getMessage());
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
return response;
}
@Override
public void validResCode(String resCode) {
tencentSmsService.validResponseCode(resCode);
}
}

View File

@@ -101,6 +101,7 @@
<ip2region.version>2.7.0</ip2region.version>
<opencc4j.version>1.7.2</opencc4j.version>
<rocketmq-spring-boot-starter.version>2.2.3</rocketmq-spring-boot-starter.version>
<tencentcloud-sdk-java.version>3.1.781</tencentcloud-sdk-java.version>
</properties>
<dependencyManagement>
@@ -966,6 +967,12 @@
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>${tencentcloud-sdk-java.version}</version>
</dependency>
</dependencies>
<build>