refactor: 统一注释规范与代码风格,优化分层结构
This commit is contained in:
@@ -5,7 +5,7 @@ import java.lang.annotation.*;
|
||||
/**
|
||||
* 数据权限注解
|
||||
*
|
||||
* @author zc
|
||||
* @author Ray.Hao
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Documented
|
||||
|
||||
@@ -3,13 +3,17 @@ package com.youlai.boot.common.annotation;
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 日志注解
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2024/6/25
|
||||
* @author Ray.Hao
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
|
||||
@@ -6,6 +6,9 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* 接口限流
|
||||
* <p>标注在 Controller 方法上,基于 Redis 计数窗口实现</p>
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 4.3.1
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.youlai.boot.common.aspect;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.common.constant.SecurityConstants;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.common.annotation.RepeatSubmit;
|
||||
import com.youlai.boot.common.util.IPUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 防重复提交切面
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RepeatSubmitAspect {
|
||||
|
||||
private final RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 防重复提交切点
|
||||
*/
|
||||
@Pointcut("@annotation(repeatSubmit)")
|
||||
public void repeatSubmitPointCut(RepeatSubmit repeatSubmit) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕通知:处理防重复提交逻辑
|
||||
*/
|
||||
@Around(value = "repeatSubmitPointCut(repeatSubmit)", argNames = "pjp,repeatSubmit")
|
||||
public Object handleRepeatSubmit(ProceedingJoinPoint pjp, RepeatSubmit repeatSubmit) throws Throwable {
|
||||
String lockKey = buildLockKey();
|
||||
|
||||
int expire = repeatSubmit.expire();
|
||||
RLock lock = redissonClient.getLock(lockKey);
|
||||
|
||||
boolean locked = lock.tryLock(0, expire, TimeUnit.SECONDS);
|
||||
if (!locked) {
|
||||
throw new BusinessException(ResultCode.DUPLICATE_SUBMISSION);
|
||||
}
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成防重复提交锁的 key
|
||||
* @return 锁的 key
|
||||
*/
|
||||
private String buildLockKey() {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
// 用户唯一标识
|
||||
String userIdentifier = getUserIdentifier(request);
|
||||
// 请求唯一标识 = 请求方法 + 请求路径 + 请求参数(严谨的做法)
|
||||
String requestIdentifier = StrUtil.join(":", request.getMethod(), request.getRequestURI());
|
||||
return StrUtil.format(RedisConstants.Lock.RESUBMIT, userIdentifier, requestIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户唯一标识
|
||||
* 1. 从请求头中获取 Token,使用 SHA-256 加密 Token 作为用户唯一标识
|
||||
* 2. 如果 Token 为空,使用 IP 作为用户唯一标识
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 用户唯一标识
|
||||
*/
|
||||
private String getUserIdentifier(HttpServletRequest request) {
|
||||
// 用户身份唯一标识
|
||||
String userIdentifier;
|
||||
// 从请求头中获取 Token
|
||||
String tokenHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if (StrUtil.isNotBlank(tokenHeader) && tokenHeader.startsWith(SecurityConstants.BEARER_TOKEN_PREFIX)) {
|
||||
String rawToken = tokenHeader.substring(SecurityConstants.BEARER_TOKEN_PREFIX.length()); // 去掉 Bearer 后的 Token
|
||||
userIdentifier = DigestUtil.sha256Hex(rawToken); // 使用 SHA-256 加密 Token 作为用户唯一标识
|
||||
} else {
|
||||
userIdentifier = IPUtils.getIpAddr(request); // 使用 IP 作为用户唯一标识
|
||||
}
|
||||
return userIdentifier;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.youlai.boot.common.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
@@ -14,8 +17,8 @@ import java.time.LocalDateTime;
|
||||
*
|
||||
* <p>实体类的基类,包含了实体类的公共属性,如创建时间、更新时间、逻辑删除标识等</p>
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2024/6/23
|
||||
* @author Ray.Hao
|
||||
* @since 4.3.1
|
||||
*/
|
||||
@Data
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.youlai.boot.common.base;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
@@ -9,8 +8,8 @@ import java.util.Objects;
|
||||
/**
|
||||
* 枚举通用接口
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/3/27 12:06
|
||||
* @author Ray.Hao
|
||||
* @since 4.3.1
|
||||
*/
|
||||
public interface IBaseEnum<T> {
|
||||
|
||||
@@ -37,12 +36,12 @@ public interface IBaseEnum<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文本标签获取值
|
||||
* 根据值获取标签
|
||||
*
|
||||
* @param value
|
||||
* @param clazz
|
||||
* @param <E>
|
||||
* @return
|
||||
* @param value 枚举值
|
||||
* @param clazz 枚举类型
|
||||
* @param <E> 枚举
|
||||
* @return 标签文本
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> String getLabelByValue(Object value, Class<E> clazz) {
|
||||
Objects.requireNonNull(value);
|
||||
@@ -59,14 +58,13 @@ public interface IBaseEnum<T> {
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据文本标签获取值
|
||||
* 根据标签获取值
|
||||
*
|
||||
* @param label
|
||||
* @param clazz
|
||||
* @param <E>
|
||||
* @return
|
||||
* @param label 标签文本
|
||||
* @param clazz 枚举类型
|
||||
* @param <E> 枚举
|
||||
* @return 枚举值
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> Object getValueByLabel(String label, Class<E> clazz) {
|
||||
Objects.requireNonNull(label);
|
||||
@@ -84,5 +82,4 @@ public interface IBaseEnum<T> {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.youlai.boot.common.constant;
|
||||
* Redis 常量
|
||||
*
|
||||
* @author Theo
|
||||
* @since 2024-7-29 11:46:08
|
||||
* @since 4.3.1
|
||||
*/
|
||||
public interface RedisConstants {
|
||||
|
||||
@@ -32,7 +32,7 @@ public interface RedisConstants {
|
||||
String REFRESH_TOKEN_USER = "auth:token:refresh:{}";
|
||||
// 用户与访问令牌的映射(userId -> accessToken)
|
||||
String USER_ACCESS_TOKEN = "auth:user:access:{}";
|
||||
// 用户与刷新令牌的映射(userId -> refreshToken
|
||||
// 用户与刷新令牌的映射(userId -> refreshToken))
|
||||
String USER_REFRESH_TOKEN = "auth:user:refresh:{}";
|
||||
// 已撤销 Token 的 JTI(单端退出/会话注销):如果 jti 在撤销列表中,则 Token 立即无效
|
||||
String BLACKLIST_TOKEN = "auth:token:blacklist:{}";
|
||||
|
||||
@@ -6,7 +6,7 @@ import lombok.Getter;
|
||||
/**
|
||||
* 环境枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @author Ray.Hao
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Getter
|
||||
|
||||
@@ -6,18 +6,17 @@ import lombok.Getter;
|
||||
/**
|
||||
* 状态枚举
|
||||
*
|
||||
* @author haoxr
|
||||
* @author Ray.Hao
|
||||
* @since 2022/10/14
|
||||
*/
|
||||
@Getter
|
||||
public enum StatusEnum implements IBaseEnum<Integer> {
|
||||
|
||||
ENABLE(1, "启用"),
|
||||
DISABLE (0, "禁用");
|
||||
DISABLE(0, "禁用");
|
||||
|
||||
private final Integer value;
|
||||
|
||||
|
||||
private final String label;
|
||||
|
||||
StatusEnum(Integer value, String label) {
|
||||
|
||||
@@ -20,13 +20,11 @@ public class BusinessException extends RuntimeException {
|
||||
this.resultCode = errorCode;
|
||||
}
|
||||
|
||||
|
||||
public BusinessException(IResultCode errorCode, String message) {
|
||||
super(message);
|
||||
this.resultCode = errorCode;
|
||||
}
|
||||
|
||||
|
||||
public BusinessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.Serializable;
|
||||
* 统一响应结构体
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2022/1/30
|
||||
* @since 4.3.1
|
||||
**/
|
||||
@Data
|
||||
public class Result<T> implements Serializable {
|
||||
@@ -64,10 +64,6 @@ public class Result<T> implements Serializable {
|
||||
return result(resultCode.getCode(), StrUtil.isNotBlank(msg) ? msg : resultCode.getMsg(), data);
|
||||
}
|
||||
|
||||
private static <T> Result<T> result(IResultCode resultCode, T data) {
|
||||
return result(resultCode.getCode(), resultCode.getMsg(), data);
|
||||
}
|
||||
|
||||
private static <T> Result<T> result(String code, String msg, T data) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(code);
|
||||
|
||||
@@ -48,7 +48,7 @@ import java.io.Serializable;
|
||||
* </pre>
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2020/6/23
|
||||
* @since 4.3.1
|
||||
**/
|
||||
public enum ResultCode implements IResultCode, Serializable {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user