目前系统集成短信似乎是必不可少的部分,由于各种云平台都提供了不同的短信通道,这里我们增加多租户多通道的短信验证码,并增加配置项,使系统可以支持多家云平台提供的短信服务。这里以阿里云和腾讯云为例,集成短信通知服务。
1、在GitEgg-Platform中新建gitegg-platform-sms基础工程,定义抽象方法和配置类
SmsSendService发送短信抽象接口:
public interface SmsSendService {
default SmsResponse sendSms(SmsData smsData, String phoneNumber){
if (StrUtil.isEmpty(phoneNumber)) {
return new SmsResponse();
}
return this.sendSms(smsData, Collections.singletonList(phoneNumber));
}
SmsResponse sendSms(SmsData smsData, Collection<string> phoneNumbers);
}
SmsResultCodeEnum定义短信发送结果
@Getter
@AllArgsConstructor
public enum SmsResultCodeEnum {
SUCCESS(200, "操作成功"),
ERROR(429, "短信发送失败,请稍后重试"),
PHONE_NUMBER_ERROR(500, "手机号错误");
public int code;
public String msg;
}
2、新建gitegg-platform-sms-aliyun工程,实现阿里云短信发送接口
AliyunSmsProperties配置类
@Data
@Component
@ConfigurationProperties(prefix = "sms.aliyun")
public class AliyunSmsProperties {
private String product = "Dysmsapi";
private String domain = "dysmsapi.aliyuncs.com";
private String regionId = "cn-hangzhou";
private String accessKeyId;
private String accessKeySecret;
private String signName;
}
AliyunSmsSendServiceImpl阿里云短信发送接口实现类
@Slf4j
@AllArgsConstructor
public class AliyunSmsSendServiceImpl implements SmsSendService {
private static final String successCode = "OK";
private final AliyunSmsProperties properties;
private final IAcsClient acsClient;
@Override
public SmsResponse sendSms(SmsData smsData, Collection<string> phoneNumbers) {
SmsResponse smsResponse = new SmsResponse();
SendSmsRequest request = new SendSmsRequest();
request.setSysMethod(MethodType.POST);
request.setPhoneNumbers(StrUtil.join(",", phoneNumbers));
request.setSignName(properties.getSignName());
request.setTemplateCode(smsData.getTemplateId());
request.setTemplateParam(JsonUtils.mapToJson(smsData.getParams()));
try {
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
if (null != sendSmsResponse && !StringUtils.isEmpty(sendSmsResponse.getCode())) {
if (this.successCode.equals(sendSmsResponse.getCode())) {
smsResponse.setSuccess(true);
} else {
log.error("Send Aliyun Sms Fail: [code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
}
smsResponse.setCode(sendSmsResponse.getCode());
smsResponse.setMessage(sendSmsResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
log.error("Send Aliyun Sms Fail: {}", e);
smsResponse.setMessage("Send Aliyun Sms Fail!");
}
return smsResponse;
}
}
3、新建gitegg-platform-sms-tencent工程,实现腾讯云短信发送接口
TencentSmsProperties配置类
@Data
@Component
@ConfigurationProperties(prefix = "sms.tencent")
public class TencentSmsProperties {
private String SmsSdkAppId;
private String senderId;
private String extendCode;
private String signName;
}
TencentSmsSendServiceImpl腾讯云短信发送接口实现类
@Slf4j
@AllArgsConstructor
public class TencentSmsSendServiceImpl implements SmsSendService {
private static final String successCode = "Ok";
private final TencentSmsProperties properties;
private final SmsClient client;
@Override
public SmsResponse sendSms(SmsData smsData, Collection<string> phoneNumbers) {
SmsResponse smsResponse = new SmsResponse();
SendSmsRequest request = new SendSmsRequest();
request.setSmsSdkAppid(properties.getSmsSdkAppId());
request.setSign(properties.getSignName());
if (!StringUtils.isEmpty(properties.getSenderId()))
{
request.setSenderId(properties.getSenderId());
}
request.setTemplateID(smsData.getTemplateId());
String[] phoneNumbersArray = (String[]) phoneNumbers.toArray();
request.setPhoneNumberSet(phoneNumbersArray);
String[] templateParams = new String[]{};
if (!CollectionUtils.isEmpty(smsData.getParams())) {
templateParams = (String[]) smsData.getParams().values().toArray();
}
request.setTemplateParamSet(templateParams);
try {
SendSmsResponse sendSmsResponse = client.SendSms(request);
//如果是批量发送,那么腾讯云短信会返回每条短信的发送状态,这里默认返回第一条短信的状态
if (null != sendSmsResponse && null != sendSmsResponse.getSendStatusSet()) {
SendStatus sendStatus = sendSmsResponse.getSendStatusSet()[0];
if (this.successCode.equals(sendStatus.getCode()))
{
smsResponse.setSuccess(true);
}
else
{
smsResponse.setCode(sendStatus.getCode());
smsResponse.setMessage(sendStatus.getMessage());
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("Send Aliyun Sms Fail: {}", e);
smsResponse.setMessage("Send Aliyun Sms Fail!");
}
return smsResponse;
}
}
4、在GitEgg-Cloud中新建业务调用方法,这里要考虑到不同租户调用不同的短信配置进行短信发送,所以新建SmsFactory短信接口实例化工厂,根据不同的租户实例化不同的短信发送接口,这里以实例化com.gitegg.service.extension.sms.factory.SmsAliyunFactory类为例,进行实例化操作,实际使用中,这里需要配置和租户的对应关系,从租户的短信配置中获取。
@Component
public class SmsFactory {
private final ISmsTemplateService smsTemplateService;
private final Map<long, smssendservice=""> SmsSendServiceMap = new ConcurrentHashMap<>();
public SmsFactory(ISmsTemplateService smsTemplateService) {
this.smsTemplateService = smsTemplateService;
}
public SmsSendService getSmsSendService(SmsTemplateDTO smsTemplateDTO) {
//根据channelId获取对应的发送短信服务接口,channelId是唯一的,每个租户有其自有的channelId
Long channelId = smsTemplateDTO.getChannelId();
SmsSendService smsSendService = SmsSendServiceMap.get(channelId);
if (null == smsSendService) {
Class cls = null;
try {
cls = Class.forName("com.gitegg.service.extension.sms.factory.SmsAliyunFactory");
Method staticMethod = cls.getDeclaredMethod("getSmsSendService", SmsTemplateDTO.class);
smsSendService = (SmsSendService) staticMethod.invoke(cls,smsTemplateDTO);
SmsSendServiceMap.put(channelId, smsSendService);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return smsSendService;
}
}
public class SmsAliyunFactory {
public static SmsSendService getSmsSendService(SmsTemplateDTO sms) {
AliyunSmsProperties aliyunSmsProperties = new AliyunSmsProperties();
aliyunSmsProperties.setAccessKeyId(sms.getSecretId());
aliyunSmsProperties.setAccessKeySecret(sms.getSecretKey());
aliyunSmsProperties.setRegionId(sms.getRegionId());
aliyunSmsProperties.setSignName(sms.getSignName());
IClientProfile profile = DefaultProfile.getProfile(aliyunSmsProperties.getRegionId(), aliyunSmsProperties.getAccessKeyId(), aliyunSmsProperties.getAccessKeySecret());
IAcsClient acsClient = new DefaultAcsClient(profile);
return new AliyunSmsSendServiceImpl(aliyunSmsProperties, acsClient);
}
}
public class SmsTencentFactory {
public static SmsSendService getSmsSendService(SmsTemplateDTO sms) {
TencentSmsProperties tencentSmsProperties = new TencentSmsProperties();
tencentSmsProperties.setSmsSdkAppId(sms.getSecretId());
tencentSmsProperties.setExtendCode(sms.getSecretKey());
tencentSmsProperties.setSenderId(sms.getRegionId());
tencentSmsProperties.setSignName(sms.getSignName());
Credential cred = new Credential(sms.getSecretId(), sms.getSecretKey());
// 实例化一个 http 选项,可选,无特殊需求时可以跳过
HttpProfile httpProfile = new HttpProfile();
// 设置代理
// httpProfile.setProxyHost("host");
// httpProfile.setProxyPort(port);
httpProfile.setReqMethod("POST");
httpProfile.setConnTimeout(60);
if (!StringUtils.isEmpty(sms.getRegionId()))
{
httpProfile.setEndpoint(sms.getRegionId());
}
ClientProfile clientProfile = new ClientProfile();
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
SmsClient client = new SmsClient(cred, "",clientProfile);
return new TencentSmsSendServiceImpl(tencentSmsProperties, client);
}
}
5、定义短信发送接口及实现类
ISmsService业务短信发送接口定义
public interface ISmsService {
SmsResponse sendSmsNormal(String smsCode, String smsData, String phoneNumbers);
SmsResponse sendSmsVerificationCode( String smsCode, String phoneNumber);
boolean checkSmsVerificationCode(String smsCode, String phoneNumber, String verificationCode);
}
SmsServiceImpl 短信发送接口实现类
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SmsServiceImpl implements ISmsService {
private final SmsFactory smsFactory;
private final ISmsTemplateService smsTemplateService;
private final RedisTemplate redisTemplate;
@Override
public SmsResponse sendSmsNormal(String smsCode, String smsData, String phoneNumbers) {
SmsResponse smsResponse = new SmsResponse();
try {
QuerySmsTemplateDTO querySmsTemplateDTO = new QuerySmsTemplateDTO();
querySmsTemplateDTO.setSmsCode(smsCode);
//获取短信code的相关信息,租户信息会根据mybatis plus插件获取
SmsTemplateDTO smsTemplateDTO = smsTemplateService.querySmsTemplate(querySmsTemplateDTO);
ObjectMapper mapper = new ObjectMapper();
Map smsDataMap = mapper.readValue(smsData, Map.class);
List<string> phoneNumberList = JsonUtils.jsonToList(phoneNumbers, String.class);
SmsData smsDataParam = new SmsData();
smsDataParam.setTemplateId(smsTemplateDTO.getTemplateId());
smsDataParam.setParams(smsDataMap);
SmsSendService smsSendService = smsFactory.getSmsSendService(smsTemplateDTO);
smsResponse = smsSendService.sendSms(smsDataParam, phoneNumberList);
} catch (Exception e) {
smsResponse.setMessage("短信发送失败");
e.printStackTrace();
}
return smsResponse;
}
@Override
public SmsResponse sendSmsVerificationCode(String smsCode, String phoneNumber) {
String verificationCode = RandomUtil.randomNumbers(6);
Map<string, string=""> smsDataMap = new HashMap<>();
smsDataMap.put(SmsConstant.SMS_CAPTCHA_TEMPLATE_CODE, verificationCode);
List<string> phoneNumbers = Arrays.asList(phoneNumber);
SmsResponse smsResponse = this.sendSmsNormal(smsCode, JsonUtils.mapToJson(smsDataMap), JsonUtils.listToJson(phoneNumbers));
if (null != smsResponse && smsResponse.isSuccess()) {
// 将短信验证码存入redis并设置过期时间为5分钟
redisTemplate.opsForValue().set(SmsConstant.SMS_CAPTCHA_KEY + smsCode + phoneNumber, verificationCode, 30,
TimeUnit.MINUTES);
}
return smsResponse;
}
@Override
public boolean checkSmsVerificationCode(String smsCode, String phoneNumber, String verificationCode) {
String verificationCodeRedis = (String) redisTemplate.opsForValue().get(SmsConstant.SMS_CAPTCHA_KEY + smsCode + phoneNumber);
if (!StrUtil.isAllEmpty(verificationCodeRedis, verificationCode) && verificationCode.equalsIgnoreCase(verificationCodeRedis)) {
return true;
}
return false;
}
}
6、新建SmsFeign类,供其他微服务调用发送短信
@RestController
@RequestMapping(value = "/feign/sms")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Api(value = "SmsFeign|提供微服务调用接口")
@RefreshScope
public class SmsFeign {
private final ISmsService smsService;
@GetMapping(value = "/send/normal")
@ApiOperation(value = "发送普通短信", notes = "发送普通短信")
Result<object> sendSmsNormal(@RequestParam("smsCode") String smsCode, @RequestParam("smsData") String smsData, @RequestParam("phoneNumbers") String phoneNumbers) {
SmsResponse smsResponse = smsService.sendSmsNormal(smsCode, smsData, phoneNumbers);
return Result.data(smsResponse);
}
@GetMapping(value = "/send/verification/code")
@ApiOperation(value = "发送短信验证码", notes = "发送短信验证码")
Result<object> sendSmsVerificationCode(@RequestParam("smsCode") String smsCode, @RequestParam("phoneNumber") String phoneNumber) {
SmsResponse smsResponse = smsService.sendSmsVerificationCode(smsCode, phoneNumber);
return Result.data(smsResponse);
}
@GetMapping(value = "/check/verification/code")
@ApiOperation(value = "校验短信验证码", notes = "校验短信验证码")
Result<boolean> checkSmsVerificationCode(@RequestParam("smsCode") String smsCode, @RequestParam("phoneNumber") String phoneNumber, @RequestParam("verificationCode") String verificationCode) {
boolean checkResult = smsService.checkSmsVerificationCode(smsCode, phoneNumber, verificationCode);
return Result.data(checkResult);
}
}
项目源码:
Gitee: https://gitee.com/wmz1930/GitEgg
GitHub: https://github.com/wmz1930/GitEgg
到此这篇关于SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务的文章就介绍到这了,更多相关SpringCloud 短信通知服务内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!