🔖 简介
ℹ️ 使用Redis实现比如 一小时内允许用户发送5次短信,一天内允许用户发送10条短信的需求,改需要怎么做呢
🎯 方法 / 步骤
☝️ 方法一:使用redis : 格式
appc_verifyCode_20201118_13312341234 :
{
"verifyCode": 1234,
"sentTimes":6,
"sendDateTime":[
"12:00:01 01:01:01",
"12:00:01 01:01:01",
"12:00:01 01:01:01",
"12:00:01 01:01:01",
"12:00:01 01:01:01",
"12:00:01 01:01:01"
]
}
-
Redis 数据模型
-
关键代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Description:
*
* @Author: Yanggc
* DateTime: 11/17 11:04
*/
@Service
public class SmsServiceImpl implements SmsService {
@Autowired
RedisTemplate redisTemplate;
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String nowDateTime = dateTimeFormatter.format(now);
@Override
public String getVerifyCode(String phoneNumber) {
String cacheKey = "appc_verifyCode_"+DateTimeFormatter.ofPattern("yyyyMMdd").format(now)+"_"+phoneNumber;
if(redisTemplate.hasKey(cacheKey)){
Map<String,Object> cacheMap = (Map<String, Object>) redisTemplate.opsForValue().get(cacheKey);
Integer sentTimes = (Integer) cacheMap.get("sentTimes");
List<String> sendTimeList = (List<String>) cacheMap.get("sendTimeList");
int sendTimeListSize = sendTimeList.size();
/**
*60S发送一次:最后一次发送时间距离现在不超过一分钟,返回发送次数太快
*/
LocalDateTime lastSendTime = LocalDateTime.parse(sendTimeList.get(sendTimeListSize-1),dateTimeFormatter);
if(lastSendTime.plusSeconds(60).isAfter(now)){
return "overRate";
}
/**
* 一小时最多发送5次,如果超过5次,取最后5次的“第一次”,判断时间距离现在是否超过一小时,如果超过一小时,提示过一个小时候再发送
*/
if(sendTimeList.size()>=5){
String lastFiveTimesFirst = sendTimeList.get(sendTimeListSize - 5);
LocalDateTime lastFiveTimesFirstTime = LocalDateTime.parse(lastFiveTimesFirst,dateTimeFormatter);
if(lastFiveTimesFirstTime.plusHours(1).isAfter(now)){
return "overRateOneHour";
}
}
//一天发送10次
if(sentTimes > 10){
return "timesExceededOneDay";
}
/**
* 更新cache操作:超过一分钟,并且是合法请求,重新生成验证码
*/
String verifyCode = generateVerifyCode();
Map<String, Object> resultCacheMap = new HashMap<>();
resultCacheMap.put("sentTimes",sentTimes+1);
resultCacheMap.put("verifyCode",verifyCode);
sendTimeList.add(nowDateTime);
resultCacheMap.put("sendTimeList",sendTimeList);
redisTemplate.boundValueOps(cacheKey).set(resultCacheMap,24,TimeUnit.HOURS);
return verifyCode;
}
dateTimeFormatter.format(now);
Map<String, Object> verifyCodeMap = new HashMap<>();
String verifyCode = generateVerifyCode();
verifyCodeMap.put("verifyCode",verifyCode);
verifyCodeMap.put("sentTimes",1);
List<String> dateTimeList = new ArrayList<>();
dateTimeList.add(nowDateTime);
verifyCodeMap.put("sendTimeList",dateTimeList);
redisTemplate.boundValueOps(cacheKey).set(verifyCodeMap,24,TimeUnit.HOURS);
return verifyCode;
}
public String generateVerifyCode(){
Integer randNum = (int) (Math.random() * (9999) + 1);
String verifyCode = String.format("%04d", randNum);
return verifyCode;
}
✌️ 方法二:
2.1 编写Redis工具类
编写一个工具类,用于与Redis进行交互。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Autowired
private StringRedisTemplate redisTemplate;
public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
}
public void set(String key, String value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
public void increment(String key, long value) {
redisTemplate.opsForValue().increment(key, value);
}
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
}
2.2 编写短信发送限制逻辑
实现短信发送次数和频率限制逻辑。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class SmsService {
private static final String SMS_COUNT_KEY = "sms:count:";
private static final String SMS_RATE_LIMIT_KEY = "sms:rate:";
@Autowired
private RedisUtil redisUtil;
public boolean sendSms(String phoneNumber, String message) {
String countKey = SMS_COUNT_KEY + phoneNumber;
String rateKey = SMS_RATE_LIMIT_KEY + phoneNumber;
// 检查发送频率
if (redisUtil.get(rateKey) != null) {
// 发送频率过快
return false;
}
// 增加发送次数
redisUtil.increment(countKey, 1);
// 设置过期时间为24小时(或其他时间)
if (redisUtil.getExpire(countKey) == -1) {
redisUtil.set(countKey, "1", 24, TimeUnit.HOURS);
}
// 获取发送次数
String countStr = redisUtil.get(countKey);
if (countStr != null && Integer.parseInt(countStr) > 10) { // 限制为每天最多发送10条短信
// 发送次数超过限制
return false;
}
// 设置发送频率限制
redisUtil.set(rateKey, "1", 1, TimeUnit.MINUTES); // 限制为每分钟只能发送一条短信
// 发送短信逻辑...
// sendSmsLogic(phoneNumber, message);
return true;
}
}
2.3 使用示例
在控制器或其他业务逻辑中调用SmsService发送短信。
@RestController
@RequestMapping("/sms")
public class SmsController {
@Autowired
private SmsService smsService;
@PostMapping("/send")
public ResponseEntity<String> sendSms(@RequestParam String phoneNumber, @RequestParam String message) {
boolean result = smsService.sendSms(phoneNumber, message);
if (result) {
return ResponseEntity.ok("短信发送成功");
} else {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("短信发送频率过高或次数超过限制");
}
}
}