refactor: 统一注释规范与代码风格,优化分层结构

This commit is contained in:
Ray.Hao
2026-06-28 19:04:21 +08:00
parent 8fd648c2e0
commit 49e3d7a71b
30 changed files with 215 additions and 286 deletions

View File

@@ -5,7 +5,7 @@ import java.lang.annotation.*;
/**
* 数据权限注解
*
* @author zc
* @author Ray.Hao
* @since 2.0.0
*/
@Documented

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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:{}";

View File

@@ -6,7 +6,7 @@ import lombok.Getter;
/**
* 环境枚举
*
* @author Ray
* @author Ray.Hao
* @since 4.0.0
*/
@Getter

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 {