diff --git a/README.md b/README.md
index e7f31580..80b314a5 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
-
+
@@ -210,13 +210,13 @@ public class DeptController extends BaseControllerVue | 3.4.21 | 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 |
| Arco Design | 2.55.0 | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。 |
| TypeScript | 5.0.4 | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 |
| Vite | 5.1.5 | 下一代的前端工具链,为开发提供极速响应。 |
-| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.4.0 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 |
+| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.5.0 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 |
| Spring Boot | 3.2.7 | 简化 Spring 应用的初始搭建和开发过程,基于“约定优于配置”的理念,使开发人员不再需要定义样板化的配置。(Spring Boot 3.0 开始,要求 Java 17 作为最低版本) |
| Undertow | 2.3.13.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 |
| Sa-Token + JWT | 1.38.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 |
diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/WebMvcConfiguration.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/WebMvcConfiguration.java
deleted file mode 100644
index fa41fd5f..00000000
--- a/continew-admin-common/src/main/java/top/continew/admin/common/config/WebMvcConfiguration.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package top.continew.admin.common.config;
-
-import lombok.RequiredArgsConstructor;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.converter.ByteArrayHttpMessageConverter;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Web MVC 配置
- *
- * @author Charles7c
- * @since 2022/12/11 19:40
- */
-@EnableWebMvc
-@Configuration
-@RequiredArgsConstructor
-public class WebMvcConfiguration implements WebMvcConfigurer {
-
- private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
-
- /**
- * 解决 Jackson2ObjectMapperBuilderCustomizer 配置不生效的问题
- *
- * MappingJackson2HttpMessageConverter 对象在程序启动时创建了多个,移除多余的,保证只有一个
- *
- */
- @Override
- public void extendMessageConverters(List> converters) {
- converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
- if (Objects.isNull(mappingJackson2HttpMessageConverter)) {
- converters.add(0, new MappingJackson2HttpMessageConverter());
- } else {
- converters.add(0, mappingJackson2HttpMessageConverter);
- }
- // 自定义 converters 时,需要手动在最前面添加 ByteArrayHttpMessageConverter
- // 否则 Spring Doc OpenAPI 的 /*/api-docs/**(例如:/v3/api-docs/default)接口响应内容会变为 Base64 编码后的内容,最终导致接口文档解析失败
- // 详情请参阅:https://github.com/springdoc/springdoc-openapi/issues/2143
- converters.add(0, new ByteArrayHttpMessageConverter());
- }
-}
diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalExceptionHandler.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalExceptionHandler.java
new file mode 100644
index 00000000..a7e520d3
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalExceptionHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.common.config.exception;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.util.NumberUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MultipartException;
+import top.continew.starter.core.exception.BadRequestException;
+import top.continew.starter.core.exception.BusinessException;
+import top.continew.starter.web.model.R;
+
+/**
+ * 全局异常处理器
+ *
+ * @author Charles7c
+ * @since 2024/8/7 20:21
+ */
+@Slf4j
+@Order(99)
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ /**
+ * 拦截业务异常
+ */
+ @ExceptionHandler(BusinessException.class)
+ public R handleBusinessException(BusinessException e, HttpServletRequest request) {
+ log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e);
+ return R.fail(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), e.getMessage());
+ }
+
+ /**
+ * 拦截自定义验证异常-错误请求
+ */
+ @ExceptionHandler(BadRequestException.class)
+ public R handleBadRequestException(BadRequestException e, HttpServletRequest request) {
+ log.warn("请求地址 [{}],自定义验证失败。", request.getRequestURI(), e);
+ return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), e.getMessage());
+ }
+
+ /**
+ * 拦截文件上传异常-超过上传大小限制
+ */
+ @ExceptionHandler(MultipartException.class)
+ public R handleRequestTooBigException(MultipartException e, HttpServletRequest request) {
+ String msg = e.getMessage();
+ R defaultFail = R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), msg);
+ if (CharSequenceUtil.isBlank(msg)) {
+ return defaultFail;
+ }
+ String sizeLimit;
+ Throwable cause = e.getCause();
+ if (null != cause) {
+ msg = msg.concat(cause.getMessage().toLowerCase());
+ }
+ if (msg.contains("size") && msg.contains("exceed")) {
+ sizeLimit = CharSequenceUtil.subBetween(msg, "the maximum size ", " for");
+ } else if (msg.contains("larger than")) {
+ sizeLimit = CharSequenceUtil.subAfter(msg, "larger than ", true);
+ } else {
+ return defaultFail;
+ }
+ String errorMsg = "请上传小于 %sKB 的文件".formatted(NumberUtil.parseLong(sizeLimit) / 1024);
+ log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e);
+ return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMsg);
+ }
+}
\ No newline at end of file
diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalSaTokenExceptionHandler.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalSaTokenExceptionHandler.java
new file mode 100644
index 00000000..9b09b3fe
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalSaTokenExceptionHandler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.common.config.exception;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.exception.NotPermissionException;
+import cn.dev33.satoken.exception.NotRoleException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import top.continew.starter.web.model.R;
+
+/**
+ * 全局 SaToken 异常处理器
+ *
+ * @author Charles7c
+ * @since 2024/8/7 20:21
+ */
+@Slf4j
+@Order(99)
+@RestControllerAdvice
+public class GlobalSaTokenExceptionHandler {
+
+ /**
+ * 认证异常-登录认证
+ */
+ @ExceptionHandler(NotLoginException.class)
+ public R handleNotLoginException(NotLoginException e, HttpServletRequest request) {
+ log.error("请求地址 [{}],认证失败,无法访问系统资源。", request.getRequestURI(), e);
+ String errorMsg = switch (e.getType()) {
+ case NotLoginException.KICK_OUT -> "您已被踢下线。";
+ case NotLoginException.BE_REPLACED_MESSAGE -> "您已被顶下线。";
+ default -> "您的登录状态已过期,请重新登录。";
+ };
+ return R.fail(String.valueOf(HttpStatus.UNAUTHORIZED.value()), errorMsg);
+ }
+
+ /**
+ * 认证异常-权限认证
+ */
+ @ExceptionHandler(NotPermissionException.class)
+ public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
+ log.error("请求地址 [{}],权限码校验失败。", request.getRequestURI(), e);
+ return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权");
+ }
+
+ /**
+ * 认证异常-角色认证
+ */
+ @ExceptionHandler(NotRoleException.class)
+ public R handleNotRoleException(NotRoleException e, HttpServletRequest request) {
+ log.error("请求地址 [{}],角色权限校验失败。", request.getRequestURI(), e);
+ return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权");
+ }
+}
\ No newline at end of file
diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java
index baad0745..b420f8eb 100644
--- a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java
+++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java
@@ -16,7 +16,6 @@
package top.continew.admin.generator.service;
-import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import top.continew.admin.generator.model.entity.FieldConfigDO;
import top.continew.admin.generator.model.entity.GenConfigDO;
@@ -86,8 +85,7 @@ public interface GeneratorService {
* 生成代码
*
* @param tableNames 表明层
- * @param request 请求对象
* @param response 响应对象
*/
- void generate(List tableNames, HttpServletRequest request, HttpServletResponse response);
+ void generate(List tableNames, HttpServletResponse response);
}
diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java
index d95b17bd..03491145 100644
--- a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java
+++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java
@@ -27,7 +27,6 @@ import cn.hutool.core.util.ZipUtil;
import cn.hutool.db.meta.Column;
import cn.hutool.system.SystemUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -281,7 +280,7 @@ public class GeneratorServiceImpl implements GeneratorService {
}
@Override
- public void generate(List tableNames, HttpServletRequest request, HttpServletResponse response) {
+ public void generate(List tableNames, HttpServletResponse response) {
try {
String tempDir = SystemUtil.getUserInfo().getTempDir();
// 删除旧代码
@@ -296,7 +295,7 @@ public class GeneratorServiceImpl implements GeneratorService {
File tempDirFile = new File(tempDir, projectProperties.getAppName());
String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT;
ZipUtil.zip(tempDirFile.getPath(), zipFilePath);
- FileUploadUtils.download(request, response, new File(zipFilePath), true);
+ FileUploadUtils.download(response, new File(zipFilePath));
} catch (Exception e) {
log.error("Generate code of table '{}' occurred an error. {}", tableNames, e.getMessage(), e);
throw new BusinessException("代码生成失败,请手动清理生成文件");
diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java
index 17abd4d9..63dad64b 100644
--- a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java
+++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java
@@ -20,12 +20,14 @@ import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
-import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.*;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.EnumUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.validation.ValidationUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
@@ -77,6 +79,7 @@ import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.CommonUserService;
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
+import top.continew.starter.web.util.FileUploadUtils;
import java.io.IOException;
import java.time.Duration;
@@ -129,13 +132,8 @@ public class UserServiceImpl extends BaseServiceImpl
-
+
top.continew
- continew-starter-log-httptrace-pro
+ continew-starter-log-interceptor
diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java b/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java
index 79764610..54c9b512 100644
--- a/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java
+++ b/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java
@@ -35,7 +35,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
import top.continew.starter.extension.crud.annotation.EnableCrudRestController;
-import top.continew.starter.web.annotation.EnableGlobalExceptionHandler;
+import top.continew.starter.web.annotation.EnableGlobalResponse;
/**
* 启动程序
@@ -44,13 +44,13 @@ import top.continew.starter.web.annotation.EnableGlobalExceptionHandler;
* @since 2022/12/8 23:15
*/
@Slf4j
-@RestController
@EnableFileStorage
+@EnableMethodCache(basePackages = "top.continew.admin")
+@EnableGlobalResponse
+@EnableCrudRestController
+@RestController
@SpringBootApplication
@RequiredArgsConstructor
-@EnableCrudRestController
-@EnableGlobalExceptionHandler
-@EnableMethodCache(basePackages = "top.continew.admin")
public class ContiNewAdminApplication implements ApplicationRunner {
private final ProjectProperties projectProperties;
diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java
index 51428312..8a7f8f47 100644
--- a/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java
+++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java
@@ -21,7 +21,7 @@ import org.springframework.context.annotation.Configuration;
import top.continew.admin.system.mapper.LogMapper;
import top.continew.admin.system.service.UserService;
import top.continew.starter.log.core.dao.LogDao;
-import top.continew.starter.log.httptracepro.autoconfigure.ConditionalOnEnabledLog;
+import top.continew.starter.log.interceptor.autoconfigure.ConditionalOnEnabledLog;
import top.continew.starter.web.autoconfigure.trace.TraceProperties;
/**
diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java
index 19368d06..8bccd7ba 100644
--- a/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java
+++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java
@@ -43,7 +43,6 @@ import top.continew.admin.system.service.UserService;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.validate.ValidationUtils;
-import top.continew.starter.web.model.R;
import top.continew.starter.log.core.annotation.Log;
import java.util.List;
@@ -69,7 +68,7 @@ public class AuthController {
@SaIgnore
@Operation(summary = "账号登录", description = "根据账号和密码进行登录认证")
@PostMapping("/account")
- public R accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
+ public LoginResp accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid();
String captcha = RedisUtils.get(captchaKey);
ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
@@ -79,13 +78,13 @@ public class AuthController {
String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(loginReq.getPassword()));
ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
String token = loginService.accountLogin(loginReq.getUsername(), rawPassword, request);
- return R.ok(LoginResp.builder().token(token).build());
+ return LoginResp.builder().token(token).build();
}
@SaIgnore
@Operation(summary = "手机号登录", description = "根据手机号和验证码进行登录认证")
@PostMapping("/phone")
- public R phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) {
+ public LoginResp phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) {
String phone = loginReq.getPhone();
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
String captcha = RedisUtils.get(captchaKey);
@@ -93,13 +92,13 @@ public class AuthController {
ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR);
RedisUtils.delete(captchaKey);
String token = loginService.phoneLogin(phone);
- return R.ok(LoginResp.builder().token(token).build());
+ return LoginResp.builder().token(token).build();
}
@SaIgnore
@Operation(summary = "邮箱登录", description = "根据邮箱和验证码进行登录认证")
@PostMapping("/email")
- public R emailLogin(@Validated @RequestBody EmailLoginReq loginReq) {
+ public LoginResp emailLogin(@Validated @RequestBody EmailLoginReq loginReq) {
String email = loginReq.getEmail();
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email;
String captcha = RedisUtils.get(captchaKey);
@@ -107,35 +106,35 @@ public class AuthController {
ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR);
RedisUtils.delete(captchaKey);
String token = loginService.emailLogin(email);
- return R.ok(LoginResp.builder().token(token).build());
+ return LoginResp.builder().token(token).build();
}
@Operation(summary = "用户退出", description = "注销用户的当前登录")
@Parameter(name = "Authorization", description = "令牌", required = true, example = "Bearer xxxx-xxxx-xxxx-xxxx", in = ParameterIn.HEADER)
@PostMapping("/logout")
- public R