result = new Result<>();
result.setCode(code);
diff --git a/src/main/java/com/youlai/boot/common/result/ResultCode.java b/src/main/java/com/youlai/boot/common/result/ResultCode.java
index dd7fa3d2..60b8668f 100644
--- a/src/main/java/com/youlai/boot/common/result/ResultCode.java
+++ b/src/main/java/com/youlai/boot/common/result/ResultCode.java
@@ -48,7 +48,7 @@ import java.io.Serializable;
*
*
* @author Ray.Hao
- * @since 2020/6/23
+ * @since 4.3.1
**/
public enum ResultCode implements IResultCode, Serializable {
diff --git a/src/main/java/com/youlai/boot/framework/mybatis/config/MybatisConfig.java b/src/main/java/com/youlai/boot/framework/mybatis/config/MybatisConfig.java
index 2da50bd3..b06bb4cb 100644
--- a/src/main/java/com/youlai/boot/framework/mybatis/config/MybatisConfig.java
+++ b/src/main/java/com/youlai/boot/framework/mybatis/config/MybatisConfig.java
@@ -12,10 +12,10 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
- * mybatis-plus 配置类
+ * MyBatis-Plus 配置类
*
* @author Ray.Hao
- * @since 2022/7/2
+ * @since 4.3.1
*/
@Configuration
@EnableTransactionManagement
@@ -38,7 +38,7 @@ public class MybatisConfig {
}
/**
- * 自动填充数据库创建人、创建时间、更新人、更新时间
+ * 自动填充创建时间和更新时间
*/
@Bean
public GlobalConfig globalConfig() {
diff --git a/src/main/java/com/youlai/boot/framework/mybatis/handler/AutoFillMetaObjectHandler.java b/src/main/java/com/youlai/boot/framework/mybatis/handler/AutoFillMetaObjectHandler.java
index 8cb13732..f5e9d893 100644
--- a/src/main/java/com/youlai/boot/framework/mybatis/handler/AutoFillMetaObjectHandler.java
+++ b/src/main/java/com/youlai/boot/framework/mybatis/handler/AutoFillMetaObjectHandler.java
@@ -1,17 +1,18 @@
package com.youlai.boot.framework.mybatis.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
-import lombok.RequiredArgsConstructor;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
- * mybatis-plus 字段自动填充
+ * MyBatis-Plus 字段自动填充
+ *
+ * @author Ray.Hao
+ * @since 3.0.0
*/
@Component
-@RequiredArgsConstructor
public class AutoFillMetaObjectHandler implements MetaObjectHandler {
@Override
diff --git a/src/main/java/com/youlai/boot/framework/mybatis/interceptor/MyDataPermissionHandler.java b/src/main/java/com/youlai/boot/framework/mybatis/interceptor/MyDataPermissionHandler.java
index d2bd122e..7dd4e003 100644
--- a/src/main/java/com/youlai/boot/framework/mybatis/interceptor/MyDataPermissionHandler.java
+++ b/src/main/java/com/youlai/boot/framework/mybatis/interceptor/MyDataPermissionHandler.java
@@ -11,7 +11,8 @@ import com.youlai.boot.framework.security.model.SysUserDetails;
import com.youlai.boot.framework.security.util.SecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
-import net.sf.jsqlparser.expression.*;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
@@ -30,8 +31,8 @@ import java.util.List;
*
* 使用 JSQLParser 构建 SQL 条件,避免字符串拼接,提高代码安全性和可读性。
*
- * @author zc
- * @since 2021-12-10 13:28
+ * @author Ray.Hao
+ * @since 2021/12/10
*/
@Slf4j
public class MyDataPermissionHandler implements DataPermissionHandler {
diff --git a/src/main/java/com/youlai/boot/framework/security/config/SecurityConfig.java b/src/main/java/com/youlai/boot/framework/security/config/SecurityConfig.java
index b64b6de0..fa541460 100644
--- a/src/main/java/com/youlai/boot/framework/security/config/SecurityConfig.java
+++ b/src/main/java/com/youlai/boot/framework/security/config/SecurityConfig.java
@@ -13,10 +13,9 @@ import com.youlai.boot.framework.security.token.TokenManager;
import com.youlai.boot.framework.security.service.SysUserDetailsService;
import com.youlai.boot.system.service.UserService;
import lombok.RequiredArgsConstructor;
-import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-
+import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -35,7 +34,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
* Spring Security 配置类
*
* @author Ray.Hao
- * @since 2023/2/17
+ * @since 4.3.1
*/
@Configuration
@EnableWebSecurity
@@ -147,7 +146,7 @@ public class SecurityConfig {
return new ProviderManager(
daoAuthenticationProvider,
smsAuthenticationProvider,
- wxMaAuthenticationProvider
+ wxMaAuthenticationProvider
);
}
diff --git a/src/main/java/com/youlai/boot/framework/web/advice/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/framework/web/advice/GlobalExceptionHandler.java
index 9b74c532..0692e0de 100644
--- a/src/main/java/com/youlai/boot/framework/web/advice/GlobalExceptionHandler.java
+++ b/src/main/java/com/youlai/boot/framework/web/advice/GlobalExceptionHandler.java
@@ -1,7 +1,6 @@
package com.youlai.boot.framework.web.advice;
import cn.hutool.core.util.StrUtil;
-import tools.jackson.core.JacksonException;
import com.youlai.boot.common.exception.BusinessException;
import com.youlai.boot.common.result.Result;
import com.youlai.boot.common.result.ResultCode;
@@ -25,6 +24,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
+import tools.jackson.core.JacksonException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLSyntaxErrorException;
@@ -34,18 +34,14 @@ import java.util.stream.Collectors;
/**
* 全局系统异常处理器
- *
- * 调整异常处理的HTTP状态码,丰富异常处理类型
+ *
+ * @author Ray.Hao
+ * @since 4.3.1
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
- /**
- * 处理绑定异常
- *
- * 当请求参数绑定到对象时发生错误,会抛出 BindException 异常。
- */
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(BindException e) {
@@ -54,12 +50,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.USER_REQUEST_PARAMETER_ERROR, msg);
}
- /**
- * 处理 @RequestParam 参数校验异常
- *
- * 当请求参数在校验过程中发生违反约束条件的异常时(如 @RequestParam 验证不通过),
- * 会捕获到 ConstraintViolationException 异常。
- */
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(ConstraintViolationException e) {
@@ -68,12 +58,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.INVALID_USER_INPUT, msg);
}
- /**
- * 处理方法参数校验异常
- *
- * 当使用 @Valid 或 @Validated 注解对方法参数进行验证时,如果验证失败,
- * 会抛出 MethodArgumentNotValidException 异常。
- */
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(MethodArgumentNotValidException e) {
@@ -82,11 +66,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.INVALID_USER_INPUT, msg);
}
- /**
- * 处理接口不存在的异常
- *
- * 当客户端请求一个不存在的路径时,会抛出 NoHandlerFoundException 异常。
- */
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Result processException(NoHandlerFoundException e) {
@@ -94,11 +73,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.INTERFACE_NOT_EXIST);
}
- /**
- * 处理缺少请求参数的异常
- *
- * 当请求缺少必需的参数时,会抛出 MissingServletRequestParameterException 异常。
- */
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(MissingServletRequestParameterException e) {
@@ -106,11 +80,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.REQUEST_REQUIRED_PARAMETER_IS_EMPTY);
}
- /**
- * 处理方法参数类型不匹配的异常
- *
- * 当请求参数类型不匹配时,会抛出 MethodArgumentTypeMismatchException 异常。
- */
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(MethodArgumentTypeMismatchException e) {
@@ -118,11 +87,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.PARAMETER_FORMAT_MISMATCH, "类型错误");
}
- /**
- * 处理 Servlet 异常
- *
- * 当 Servlet 处理请求时发生异常时,会抛出 ServletException 异常。
- */
@ExceptionHandler(ServletException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(ServletException e) {
@@ -130,11 +94,6 @@ public class GlobalExceptionHandler {
return Result.failed(e.getMessage());
}
- /**
- * 处理非法参数异常
- *
- * 当方法接收到非法参数时,会抛出 IllegalArgumentException 异常。
- */
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.OK)
public Result handleIllegalArgumentException(IllegalArgumentException e) {
@@ -142,11 +101,6 @@ public class GlobalExceptionHandler {
return Result.failed(e.getMessage());
}
- /**
- * 处理 JSON 处理异常
- *
- * 当处理 JSON 数据时发生错误,会抛出 JacksonException 异常。
- */
@ExceptionHandler(JacksonException.class)
@ResponseStatus(HttpStatus.OK)
public Result handleJacksonException(JacksonException e) {
@@ -154,11 +108,6 @@ public class GlobalExceptionHandler {
return Result.failed(e.getMessage());
}
- /**
- * 处理请求体不可读的异常
- *
- * 当请求体不可读时,会抛出 HttpMessageNotReadableException 异常。
- */
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(HttpMessageNotReadableException e) {
@@ -171,11 +120,6 @@ public class GlobalExceptionHandler {
return Result.failed(errorMessage);
}
- /**
- * 处理类型不匹配异常
- *
- * 当方法参数类型不匹配时,会抛出 TypeMismatchException 异常。
- */
@ExceptionHandler(TypeMismatchException.class)
@ResponseStatus(HttpStatus.OK)
public Result processException(TypeMismatchException e) {
@@ -183,11 +127,6 @@ public class GlobalExceptionHandler {
return Result.failed(e.getMessage());
}
- /**
- * 处理 SQL 语法错误异常
- *
- * 当 SQL 语法错误时,会抛出 BadSqlGrammarException 异常。
- */
@ExceptionHandler(BadSqlGrammarException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result handleBadSqlGrammarException(BadSqlGrammarException e) {
@@ -198,11 +137,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.DATABASE_EXECUTION_ERROR);
}
- /**
- * 处理 SQL 语法错误异常
- *
- * 当 SQL 语法错误时,会抛出 SQLSyntaxErrorException 异常。
- */
@ExceptionHandler(SQLSyntaxErrorException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result processSQLSyntaxErrorException(SQLSyntaxErrorException e) {
@@ -210,12 +144,6 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.DATABASE_EXECUTION_SYNTAX_ERROR);
}
-
- /**
- * 处理 SQL 违反了完整性约束
- *
- * 当 SQL 违反了完整性约束时,会抛出 SQLIntegrityConstraintViolationException 异常。
- */
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
@@ -223,22 +151,12 @@ public class GlobalExceptionHandler {
return Result.failed(ResultCode.INTEGRITY_CONSTRAINT_VIOLATION);
}
- /**
- * 处理 Token 无效异常
- *
- * 当 access_token 或 refresh_token 过期/无效时,返回 401。
- */
@ExceptionHandler(TokenInvalidException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public Result handleTokenInvalidException(TokenInvalidException e) {
return Result.failed(e.getResultCode());
}
- /**
- * 处理业务异常
- *
- * 当业务逻辑发生错误时,会抛出 BusinessException 异常。
- */
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.OK)
public Result handleBizException(BusinessException e) {
@@ -249,15 +167,9 @@ public class GlobalExceptionHandler {
return Result.failed(e.getMessage());
}
- /**
- * 处理所有未捕获的异常
- *
- * 当发生未捕获的异常时,会抛出 Exception 异常。
- */
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result handleException(Exception e) throws Exception {
- // 将 Spring Security 异常继续抛出,以便交给自定义处理器处理
if (e instanceof AccessDeniedException
|| e instanceof AuthenticationException) {
throw e;
@@ -266,12 +178,6 @@ public class GlobalExceptionHandler {
return Result.failed(e.getLocalizedMessage());
}
- /**
- * 传参类型错误时,用于消息转换
- *
- * @param throwable 异常
- * @return 错误信息
- */
private String convertMessage(Throwable throwable) {
String error = throwable.toString();
String regulation = "\\[\"(.*?)\"]+";
diff --git a/src/main/java/com/youlai/boot/framework/web/aspect/RateLimitAspect.java b/src/main/java/com/youlai/boot/framework/web/aspect/RateLimitAspect.java
index 4276a604..bcbc377c 100644
--- a/src/main/java/com/youlai/boot/framework/web/aspect/RateLimitAspect.java
+++ b/src/main/java/com/youlai/boot/framework/web/aspect/RateLimitAspect.java
@@ -26,6 +26,9 @@ import java.util.concurrent.TimeUnit;
/**
* 接口限流切面
* 对 @RateLimit 标注的方法做 Redis 计数,超阈值则拒绝
+ *
+ * @author Ray.Hao
+ * @since 4.3.1
*/
@Aspect
@Component
diff --git a/src/main/java/com/youlai/boot/framework/web/aspect/RepeatSubmitAspect.java b/src/main/java/com/youlai/boot/framework/web/aspect/RepeatSubmitAspect.java
index e69de29b..63e2456c 100644
--- a/src/main/java/com/youlai/boot/framework/web/aspect/RepeatSubmitAspect.java
+++ b/src/main/java/com/youlai/boot/framework/web/aspect/RepeatSubmitAspect.java
@@ -0,0 +1,84 @@
+package com.youlai.boot.framework.web.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 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 4.3.1
+ */
+@Aspect
+@Component
+@RequiredArgsConstructor
+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
+ */
+ 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 作为用户唯一标识
+ */
+ private String getUserIdentifier(HttpServletRequest request) {
+ String userIdentifier;
+ 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());
+ userIdentifier = DigestUtil.sha256Hex(rawToken);
+ } else {
+ userIdentifier = IPUtils.getIpAddr(request);
+ }
+ return userIdentifier;
+ }
+
+}
diff --git a/src/main/java/com/youlai/boot/framework/web/config/OperationLogExecutorConfig.java b/src/main/java/com/youlai/boot/framework/web/config/OperationLogExecutorConfig.java
index 88354e30..c50edf6d 100644
--- a/src/main/java/com/youlai/boot/framework/web/config/OperationLogExecutorConfig.java
+++ b/src/main/java/com/youlai/boot/framework/web/config/OperationLogExecutorConfig.java
@@ -9,11 +9,9 @@ import java.util.concurrent.ThreadPoolExecutor;
/**
* 操作日志线程池
- * core=1 确保日志按序写入,max=2 峰值扩容,queue=1000 缓冲突发流量,
- * CallerRunsPolicy 保证满载时由调用线程兜底执行不丢数据。
*
* @author Ray.Hao
- * @since 0.0.1
+ * @since 3.0.0
*/
@Configuration
public class OperationLogExecutorConfig {
diff --git a/src/main/java/com/youlai/boot/system/controller/UserController.java b/src/main/java/com/youlai/boot/system/controller/UserController.java
index f90e3f25..ce9a926c 100644
--- a/src/main/java/com/youlai/boot/system/controller/UserController.java
+++ b/src/main/java/com/youlai/boot/system/controller/UserController.java
@@ -2,11 +2,11 @@ package com.youlai.boot.system.controller;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.youlai.boot.common.annotation.Log;
import com.youlai.boot.common.annotation.RepeatSubmit;
import com.youlai.boot.common.enums.ActionTypeEnum;
import com.youlai.boot.common.enums.LogModuleEnum;
+import com.youlai.boot.common.enums.StatusEnum;
import com.youlai.boot.framework.security.util.SecurityUtils;
import com.youlai.boot.common.model.Option;
import com.youlai.boot.common.result.ExcelResult;
@@ -14,9 +14,14 @@ import com.youlai.boot.common.result.PageResult;
import com.youlai.boot.common.result.Result;
import com.youlai.boot.common.util.ExcelUtils;
import com.youlai.boot.system.listener.UserImportListener;
-import com.youlai.boot.system.model.form.*;
+import com.youlai.boot.system.model.form.EmailUpdateForm;
+import com.youlai.boot.system.model.form.MobileUpdateForm;
+import com.youlai.boot.system.model.form.PasswordUpdateForm;
+import com.youlai.boot.system.model.form.PasswordVerifyForm;
+import com.youlai.boot.system.model.form.UserForm;
+import com.youlai.boot.system.model.form.UserImportForm;
+import com.youlai.boot.system.model.form.UserProfileForm;
import com.youlai.boot.system.model.vo.UserExportVO;
-import com.youlai.boot.system.model.entity.SysUser;
import com.youlai.boot.system.model.query.UserQuery;
import com.youlai.boot.system.model.vo.CurrentUserVO;
import com.youlai.boot.system.model.vo.UserPageVO;
@@ -32,7 +37,16 @@ import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
@@ -46,7 +60,7 @@ import java.util.List;
* 用户控制层
*
* @author Ray.Hao
- * @since 2022/10/16
+ * @since 4.3.1
*/
@Tag(name = "02.用户接口")
@RestController
@@ -120,12 +134,9 @@ public class UserController {
@Parameter(description = "用户ID") @PathVariable Long userId,
@Parameter(description = "用户状态(1:启用;0:禁用)") @RequestParam Integer status
) {
- boolean result = userService.update(new LambdaUpdateWrapper()
- .eq(SysUser::getId, userId)
- .set(SysUser::getStatus, status)
- );
+ boolean result = userService.updateUserStatus(userId, status);
// 用户禁用时立即失效其会话
- if (result && status == 0) {
+ if (result && StatusEnum.DISABLE.getValue().equals(status)) {
tokenManager.invalidateUserSessions(userId);
}
return Result.judge(result);
diff --git a/src/main/java/com/youlai/boot/system/model/entity/Menu.java b/src/main/java/com/youlai/boot/system/model/entity/Menu.java
index 180435f0..4839f40b 100644
--- a/src/main/java/com/youlai/boot/system/model/entity/Menu.java
+++ b/src/main/java/com/youlai/boot/system/model/entity/Menu.java
@@ -48,6 +48,11 @@ public class Menu extends BaseEntity {
*/
private String component;
+ /**
+ * 外链地址
+ */
+ private String externalUrl;
+
/**
* 权限标识
*/
diff --git a/src/main/java/com/youlai/boot/system/model/form/MenuForm.java b/src/main/java/com/youlai/boot/system/model/form/MenuForm.java
index dabc73ce..41cda185 100644
--- a/src/main/java/com/youlai/boot/system/model/form/MenuForm.java
+++ b/src/main/java/com/youlai/boot/system/model/form/MenuForm.java
@@ -38,6 +38,9 @@ public class MenuForm {
@Schema(description = "组件路径(vue页面完整路径,省略.vue后缀)")
private String component;
+ @Schema(description = "外链地址")
+ private String externalUrl;
+
@Schema(description = "权限标识")
private String perm;
diff --git a/src/main/java/com/youlai/boot/system/model/vo/MenuVO.java b/src/main/java/com/youlai/boot/system/model/vo/MenuVO.java
index 81010597..2353a18e 100644
--- a/src/main/java/com/youlai/boot/system/model/vo/MenuVO.java
+++ b/src/main/java/com/youlai/boot/system/model/vo/MenuVO.java
@@ -31,6 +31,9 @@ public class MenuVO {
@Schema(description = "组件路径")
private String component;
+ @Schema(description = "外链地址")
+ private String externalUrl;
+
@Schema(description = "菜单排序(数字越小排名越靠前)")
private Integer sort;
diff --git a/src/main/java/com/youlai/boot/system/service/UserService.java b/src/main/java/com/youlai/boot/system/service/UserService.java
index d0abf23f..48c5f58f 100644
--- a/src/main/java/com/youlai/boot/system/service/UserService.java
+++ b/src/main/java/com/youlai/boot/system/service/UserService.java
@@ -55,6 +55,15 @@ public interface UserService extends IService {
*/
boolean updateUser(Long userId, UserForm userForm);
+ /**
+ * 修改用户状态
+ *
+ * @param userId 用户ID
+ * @param status 状态值
+ * @return {@link Boolean} 是否修改成功
+ */
+ boolean updateUserStatus(Long userId, Integer status);
+
/**
* 删除用户
diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java
index 8251829c..8ffef8d4 100644
--- a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java
@@ -664,6 +664,14 @@ public class UserServiceImpl extends ServiceImpl implements
);
}
+ @Override
+ public boolean updateUserStatus(Long userId, Integer status) {
+ return this.update(new LambdaUpdateWrapper()
+ .eq(SysUser::getId, userId)
+ .set(SysUser::getStatus, status)
+ );
+ }
+
/**
* 获取用户选项列表
*
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 12d1d96c..f3758e1b 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -162,14 +162,16 @@ springdoc:
packages-to-scan: # 扫描的 Controller 包,限制只生成指定包的接口文档
- com.youlai.boot.auth.controller
- com.youlai.boot.system.controller
- - com.youlai.boot.module.file.controller
- - com.youlai.boot.module.codegen.controller
- default-flat-param-object: true # 将对象参数扁平化显示在文档中
+ - com.youlai.boot.file.controller
+ - com.youlai.boot.codegen.controller
+ - com.youlai.boot.message.controller
+ default-flat-param-object: true
# knife4j 配置
knife4j:
- enable: true # 是否启用 Knife4j 增强功能
- production: false # 是否启用生产环境保护(true=生产环境隐藏文档,false=开发环境可访问)
+ enable: true
+ # 生产环境设置为 true 关闭文档
+ production: false
setting:
language: zh_cn