完善短信封装

This commit is contained in:
zk
2026-03-11 11:48:22 +08:00
parent cea131c5e5
commit ae48a1264f
9 changed files with 341 additions and 304 deletions
@@ -1,5 +1,8 @@
package org.jiayunet.sms;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.teautil.models.RuntimeOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -7,10 +10,9 @@ import org.springframework.stereotype.Component;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import org.springframework.util.Assert;
import org.jiayunet.tool.server.RedisServerTool;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
/**
* @author zk
@@ -18,75 +20,59 @@ import java.util.concurrent.TimeUnit;
@Component("smsAbility")
@ConditionalOnProperty(name = "app.sms.service_provider", havingValue = "aliyun")
@Slf4j
public class AliYunSmsAbility implements ISmsAbility {
public class AliYunSmsAbility {
@Autowired
private com.aliyun.dysmsapi20170525.Client aliyunSmsClient;
@Autowired
private RedisServerTool redisServerTool;
private ObjectMapper objectMapper;
@Override
public void sendVerificationCode(String phone, VerifyCodeAttribute attribute) {
String redisKey = attribute.getRedisKeyPre()+phone;
// 验证是否发送
boolean existKey = redisServerTool.hasKey(redisKey);
if (!attribute.getCover()){
Assert.isTrue(!existKey,"验证码已发送");
}
// 生成验证码
int randomNumber = new Random().nextInt(1000000);
String number = String.format("%06d", randomNumber);
// 发送验证码
Assert.isTrue(sendVerificationCode(phone,number,attribute.getSignName(),attribute.getTemplateCode()),"短信发送失败,请稍后重试");
// 跟新redisK
redisServerTool.set(redisKey,number,attribute.getEffectiveTime(), TimeUnit.MINUTES);
}
@Override
public String getVerificationCode(String phone, VerifyCodeAttribute attribute) {
return redisServerTool.get(attribute.getRedisKeyPre()+phone,String.class);
}
@Override
public void delVerificationCode(String phone, VerifyCodeAttribute attribute) {
redisServerTool.delete(attribute.getRedisKeyPre()+phone);
}
@Override
public void sendNotification(String phone, String message) {
}
/**
* ali 发送验证
* @param phone 手机号
* @param validCode 验证码
* @param signName 签名
* 发送验证短信
*
* @param phone 手机号
* @param signName 签名
* @param templateCode 模板
* @param variableMap 短信变量参数 key;变量名 value: 替换值
* @return 发送结果
*/
private boolean sendVerificationCode(String phone, String validCode, String signName, String templateCode) {
com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest =
new com.aliyun.dysmsapi20170525.models.SendSmsRequest().setSignName(signName).setTemplateCode(templateCode)
.setPhoneNumbers(phone).setTemplateParam("{\"code\":\"" + validCode + "\"}");
public boolean sendSms(String phone, String signName, String templateCode, Map<String, String> variableMap) {
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// 复制代码运行请自行打印 API 的返回值
Assert.hasText(phone, "手机号码空");
Assert.hasText(signName, "签名为空");
Assert.hasText(templateCode, "短信模板code为空");
// 确保非空
variableMap = variableMap == null ? new HashMap<>() : variableMap;
String templateParam = objectMapper.writeValueAsString(variableMap);
SendSmsRequest sendSmsRequest =
new SendSmsRequest().setSignName(signName).setTemplateCode(templateCode).setPhoneNumbers(phone).setTemplateParam(templateParam);
// 运行时配置对象
RuntimeOptions runtime = new RuntimeOptions()
.setReadTimeout(5000) // 读取超时 5 秒
.setConnectTimeout(3000); // 连接超时 3 秒
// 发送短信
SendSmsResponse response = aliyunSmsClient.sendSmsWithOptions(sendSmsRequest, runtime);
if (!response.getStatusCode().equals(200)||!response.getBody().getCode().equals("OK")){
log.error("短信发送失败: 失败原因:{}",response.getBody());
if (response.getStatusCode() != 200 || !response.getBody().getCode().equals("OK")) {
log.error("短信发送失败: phone={}, templateCode={}, 失败原因:{}", phone, templateCode, response.getBody());
return false;
}
log.info("短信发送成功: phone={}, templateCode={}", phone, templateCode);
return true;
}catch (Throwable e){
log.error("短信发送异常: 异常:{}",e.getMessage());
} catch (Exception e) {
log.error("短信发送异常: phone={}, templateCode={}", phone, templateCode, e);
return false;
}
}
}
@@ -1,43 +0,0 @@
package org.jiayunet.sms;
/**
* 抽象短信发送接口
*
* @author zk
*/
public interface ISmsAbility {
/**
* 发送验证码
* @param phone 手机号
* @param attribute 配置
*/
void sendVerificationCode(String phone, VerifyCodeAttribute attribute);
/**
* 获取验证码
* @param phone 手机号
* @param attribute 配置
*/
String getVerificationCode(String phone, VerifyCodeAttribute attribute);
/**
* 删除验证码
* @param phone 手机号
* @param attribute 配置
*/
void delVerificationCode(String phone, VerifyCodeAttribute attribute);
/**
* 发送通知短信
*
* @param phone 手机号
* @param message 消息
*/
void sendNotification(String phone, String message);
}
@@ -1,125 +0,0 @@
package org.jiayunet.sms;
import lombok.Getter;
import org.springframework.util.Assert;
/**
* 短信发送配配置
*
* @author zk
*/
@Getter
public class VerificationConfig implements VerifyCodeAttribute {
/**
* 模板签名
*/
private final String signName;
/**
* 模板code
*/
private final String templateCode;
/**
* redis 名
*/
private final String redisKeyPre;
/**
* 有效时间 单位分钟
*/
private final Integer effectiveTime;
/**
* 覆盖已存在的redis值
*/
private final Boolean cover;
/**
* 构造器
*/
public static class Builder {
private String signName;
private String templateCode;
private String redisKeyPre;
private Integer effectiveTime;
private Boolean cover;
public VerificationConfig.Builder config(VerifyCodeAttribute attribute){
this.signName = attribute.getSignName();
this.templateCode = attribute.getTemplateCode();
this.redisKeyPre = attribute.getRedisKeyPre();
this.effectiveTime = attribute.getEffectiveTime();
this.cover = attribute.getCover();
return this;
}
public VerificationConfig.Builder config(String signName, String templateCode,String redisKeyPre) {
this.signName = signName;
this.templateCode = templateCode;
this.redisKeyPre = redisKeyPre;
this.effectiveTime = 5;
this.cover = true;
return this;
}
public VerificationConfig.Builder config(String signName, String templateCode,String redisKeyPre,Integer effectiveTime) {
this.signName = signName;
this.templateCode = templateCode;
this.redisKeyPre = redisKeyPre;
this.effectiveTime = effectiveTime;
this.cover = true;
return this;
}
public VerificationConfig.Builder config(String signName, String templateCode,String redisKeyPre,Integer effectiveTime,Boolean cover) {
this.signName = signName;
this.templateCode = templateCode;
this.redisKeyPre = redisKeyPre;
this.effectiveTime = effectiveTime;
this.cover = cover;
return this;
}
public VerificationConfig.Builder redisKeyPre(String redisKeyPre) {
this.redisKeyPre = redisKeyPre;
return this;
}
public VerificationConfig.Builder templateCode(String templateCode) {
this.templateCode = templateCode;
return this;
}
public VerificationConfig.Builder signName(String signName) {
this.signName = signName;
return this;
}
public VerificationConfig.Builder effectiveTime(Integer effectiveTime) {
this.effectiveTime = effectiveTime;
return this;
}
public VerificationConfig.Builder cover(Boolean cover) {
this.cover = cover;
return this;
}
public VerificationConfig build() {
Assert.hasText(signName,"signName不能为空");
Assert.hasText(templateCode,"templateCode不能为空");
Assert.hasText(redisKeyPre,"redisKey不能为空");
Assert.notNull(effectiveTime,"effectiveTime不能为空");
Assert.notNull(cover,"cover不能为空");
return new VerificationConfig(signName, templateCode, redisKeyPre,effectiveTime,cover);
}
}
/**
* 私有化构造
* @param signName 签名
* @param templateCode 短信模板
* @param redisKeyPre redis键名
* @param effectiveTime 有效时间
* @param cover 是否覆盖发送
*/
private VerificationConfig(String signName, String templateCode, String redisKeyPre, Integer effectiveTime, Boolean cover) {
this.signName = signName;
this.templateCode = templateCode;
this.redisKeyPre = redisKeyPre;
this.effectiveTime = effectiveTime;
this.cover = cover;
}
}
@@ -1,33 +0,0 @@
package org.jiayunet.sms;
/**
* @author zk
*/
public interface VerifyCodeAttribute {
/**
* 签名
*
*/
String getSignName();
/**
* 模板code
*/
String getTemplateCode();
/**
* redisKey 名字
*/
String getRedisKeyPre();
/**
* 有效时间
*/
Integer getEffectiveTime();
/**
* 是否覆盖发送
*/
Boolean getCover();
}