From 85f71935cd4161192c96b443f14c59235013a5a2 Mon Sep 17 00:00:00 2001 From: wangw <1594593906@qq.com> Date: Tue, 17 Dec 2024 11:25:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sqx/common/aspect/AppApiMethodAspect.java | 13 +- .../sqx/common/utils/ApiAccessLimitUtil.java | 147 ++++++++++++++++++ .../java/com/sqx/common/utils/RedisUtils.java | 15 ++ .../app/controller/app/AppController.java | 5 + 4 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/sqx/common/utils/ApiAccessLimitUtil.java diff --git a/src/main/java/com/sqx/common/aspect/AppApiMethodAspect.java b/src/main/java/com/sqx/common/aspect/AppApiMethodAspect.java index 488f7f7b..9fb41360 100644 --- a/src/main/java/com/sqx/common/aspect/AppApiMethodAspect.java +++ b/src/main/java/com/sqx/common/aspect/AppApiMethodAspect.java @@ -1,7 +1,6 @@ package com.sqx.common.aspect; import com.google.gson.Gson; -import com.sqx.common.utils.DateUtils; import com.sqx.common.utils.HttpContextUtils; import com.sqx.common.utils.IPUtils; import lombok.extern.slf4j.Slf4j; @@ -10,11 +9,9 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; -import java.util.Date; /** * 方法调用统一切面处理 @@ -23,12 +20,10 @@ import java.util.Date; @Component @Slf4j public class AppApiMethodAspect { - @Pointcut("execution(public * (" + - "com.sqx.modules.*.controller.* " + - ").*(..)) && " + - "!execution(public * (" + - "com.sqx.modules.*.controller.SysLoginController " + - ").*(..))") + + @Pointcut("execution(public * (com.sqx.modules.*.controller.*).*(..)) " + + "&& " + + "!execution(public * (com.sqx.modules.sys.controller.SysLoginController).*(..))") public void pkg() { } diff --git a/src/main/java/com/sqx/common/utils/ApiAccessLimitUtil.java b/src/main/java/com/sqx/common/utils/ApiAccessLimitUtil.java new file mode 100644 index 00000000..3d7a869b --- /dev/null +++ b/src/main/java/com/sqx/common/utils/ApiAccessLimitUtil.java @@ -0,0 +1,147 @@ +package com.sqx.common.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.temporal.TemporalAdjusters; +import java.util.Objects; + + +/** + * 接口访问次数 月为单位 + */ +@Component +public class ApiAccessLimitUtil { + + private static final String ACCESS_COUNT_KEY_PREFIX = "sys:limit:"; + + private static RedisUtils redisUtils; + + private static final int DEFAULT_ACCESS_COUNT = 5; + private static final String DATE_TIME_FORMAT = "month"; + + + @Autowired + public void setRedisUtils(RedisUtils redisUtils) { + ApiAccessLimitUtil.redisUtils = redisUtils; + } + + /** + * 默认 当月5次 + * @param id 唯一值 + * @param key 接口名称 sys:limit:接口名称 + * @return + */ + public static boolean isAccessAllowed(String id, String key) { + String redisKey = generateRedisKey(key, id); + Object countObj = redisUtils.get(redisKey); + if (countObj == null) { + // 根据不同时间周期设置过期时间并初始化访问次数为1 + long expireAt = calculateExpireAt(DATE_TIME_FORMAT); + redisUtils.set(redisKey, 1, expireAt); + return true; + } + if ((int) countObj < DEFAULT_ACCESS_COUNT) { + // 访问次数未达上限,次数加1 + redisUtils.incr(redisKey); + return true; + } + return false; + } + + + /** + * 默认月 month/月/自然月 + * @param id 唯一值 + * @param key 接口名称 sys:limit:接口名称 + * @param count 次数限制 + * @return + */ + public static boolean isAccessAllowed(String id, String key, Integer count) { + String redisKey = generateRedisKey(key, id); + Object countObj = redisUtils.get(redisKey); + if (countObj == null) { + // 根据不同时间周期设置过期时间并初始化访问次数为1 + long expireAt = calculateExpireAt(DATE_TIME_FORMAT); + redisUtils.set(redisKey, 1, expireAt); + return true; + } + if ((int) countObj < count) { + // 访问次数未达上限,次数加1 + redisUtils.incr(redisKey); + return true; + } + return false; + } + + /** + * 默认 5次 + * @param id 唯一值 + * @param key 接口名称 sys:limit:接口名称 + * @param timeFormat day/天/自然天 week/周/本周日 month/月/自然月 year/年/自然年 + * @return + */ + public static boolean isAccessAllowed(String id, String key, String timeFormat) { + String redisKey = generateRedisKey(key, id); + Object countObj = redisUtils.get(redisKey); + if (countObj == null) { + // 根据不同时间周期设置过期时间并初始化访问次数为1 + long expireAt = calculateExpireAt(timeFormat); + redisUtils.set(redisKey, 1, expireAt); + return true; + } + if ((int) countObj < DEFAULT_ACCESS_COUNT) { + // 访问次数未达上限,次数加1 + redisUtils.incr(redisKey); + return true; + } + return false; + } + + /** + * @param id 唯一值 + * @param key 接口名称 sys:limit:接口名称 + * @param count 次数限制 + * @param timeFormat day/天/自然天 week/周/本周日 month/月/自然月 year/年/自然年 + * @return + */ + public static boolean isAccessAllowed(String id, String key, Integer count, String timeFormat) { + String redisKey = generateRedisKey(key, id); + Object countObj = redisUtils.get(redisKey); + if (countObj == null) { + // 根据不同时间周期设置过期时间并初始化访问次数为1 + long expireAt = calculateExpireAt(timeFormat); + redisUtils.set(redisKey, 1, expireAt); + return true; + } + if ((int) countObj < count) { + // 访问次数未达上限,次数加1 + redisUtils.incr(redisKey); + return true; + } + return false; + } + + + private static String generateRedisKey(String key, String id) { + return ACCESS_COUNT_KEY_PREFIX + key + ":" + id; + } + + private static long calculateExpireAt(String timePeriod) { + LocalDate now = LocalDate.now(); + LocalDate expireDate = null; + if ("day".equals(timePeriod)) { + expireDate = now.plusDays(1).atStartOfDay().toLocalDate(); + } else if ("week".equals(timePeriod)) { + expireDate = now.plusWeeks(0).with(TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY)); + } else if ("month".equals(timePeriod)) { + expireDate = now.plusMonths(1).withDayOfMonth(1).minusDays(1); + } else if ("year".equals(timePeriod)) { + expireDate = now.plusYears(1).withDayOfYear(1).minusDays(1); + } + return Objects.requireNonNull(expireDate).atTime(23, 59, 59).toEpochSecond(ZoneOffset.UTC); + } +} \ No newline at end of file diff --git a/src/main/java/com/sqx/common/utils/RedisUtils.java b/src/main/java/com/sqx/common/utils/RedisUtils.java index fd51264d..3b90d246 100644 --- a/src/main/java/com/sqx/common/utils/RedisUtils.java +++ b/src/main/java/com/sqx/common/utils/RedisUtils.java @@ -128,6 +128,21 @@ public class RedisUtils { return get(key, NOT_EXPIRE); } + /** + * 对指定的键执行自增操作,返回自增后的值 + * + * @param key 要自增的键 + * @return 自增后的值,如果键不存在则初始化为1后返回1,如果出现异常返回null + */ + public Long incr(String key) { + try { + return redisTemplate.opsForValue().increment(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public void delete(String key) { redisTemplate.delete(key); } diff --git a/src/main/java/com/sqx/modules/app/controller/app/AppController.java b/src/main/java/com/sqx/modules/app/controller/app/AppController.java index d8f77bc0..a92014c5 100644 --- a/src/main/java/com/sqx/modules/app/controller/app/AppController.java +++ b/src/main/java/com/sqx/modules/app/controller/app/AppController.java @@ -2,6 +2,8 @@ package com.sqx.modules.app.controller.app; import com.alibaba.fastjson.JSONObject; +import com.sqx.common.annotation.Debounce; +import com.sqx.common.utils.ApiAccessLimitUtil; import com.sqx.common.utils.Result; import com.sqx.modules.app.annotation.Login; import com.sqx.modules.app.annotation.LoginUser; @@ -72,6 +74,9 @@ public class AppController { @ApiOperation("用户修改个人信息") @ResponseBody public Result updateUserImageUrl(@RequestAttribute("userId") Long userId,String zhiFuBao,String zhiFuBaoName) { + if(!ApiAccessLimitUtil.isAccessAllowed(userId.toString(), "updateZFB", 2, "month")){ + return Result.error("每月仅支持修改两次,请联系管理员"); + } UserEntity userEntity=new UserEntity(); userEntity.setZhiFuBao(zhiFuBao); userEntity.setZhiFuBaoName(zhiFuBaoName);