build: continew-starter 2.12.2 => 2.13.0

1.引入 continew-starter-validation(从 core 拆分)、sa-token-sign(从 sa-token-core 拆分并调整了部分 API)
2.Starter import 包路径调整
- EasyExcel 替换为 FastExcel:com.alibaba.excel(EasyExcel) => cn.idev.excel(FastExcel)
- top.continew.starter.file.excel => top.continew.starter.excel
- top.continew.starter.core.validation.constraints => top.continew.starter.validation.constraints
- top.continew.starter.core.validation.ValidationUtils、CheckUtils、Validator => top.continew.starter.core.util.validation
- cn.dev33.satoken.sign => cn.dev33.satoken.sign.template
- top.continew.starter.core.autoconfigure.project => top.continew.starter.core.autoconfigure.application
- top.continew.starter.data.core、top.continew.starter.data.mp => top.continew.starter.data
- top.continew.starter.data.mp.base.BaseMapper => top.continew.starter.data.mapper.BaseMapper
2.Starter 基础类命名调整
CRUD:AbstractBaseController => AbstractCrudController,BaseService => CrudService,BaseServiceImpl => CrudServiceImpl
Core:ProjectProperties(项目配置,project.xxx) => ApplicationProperties(应用配置更为贴切,且变量 application.xx 可以和 Maven 变量显著区分开)
3.groupId 调整:top.continew.starter、top.continew.admin(避免部分童鞋全局替换包名时出现把 starter 也一起替换了!)
4.Admin import 包路径调整:BaseController、BaseDO等 => common.base
5.新增 BaseService、BaseServiceImpl 替代 Starter 原 BaseXxx,方便用户根据项目实际需要重写或新增全局通用接口、方法
6.snail-job server 数据库脚本更新至 v1.5.0
7.Valid 及 Validated 使用梳理(CrudService 支持通过在实现类添加 Validated 注解来实现 Service 层基础校验)
This commit is contained in:
2025-07-05 21:33:45 +08:00
parent efb65c21a1
commit 2138bee42c
184 changed files with 714 additions and 575 deletions

View File

@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.continew</groupId>
<groupId>top.continew.admin</groupId>
<artifactId>continew-admin</artifactId>
<version>${revision}</version>
</parent>
@@ -30,31 +30,31 @@
<dependencies>
<!-- 系统管理模块 -->
<dependency>
<groupId>top.continew</groupId>
<groupId>top.continew.admin</groupId>
<artifactId>continew-system</artifactId>
</dependency>
<!-- 任务调度插件 -->
<dependency>
<groupId>top.continew</groupId>
<groupId>top.continew.admin</groupId>
<artifactId>continew-plugin-schedule</artifactId>
</dependency>
<!-- 能力开放插件 -->
<dependency>
<groupId>top.continew</groupId>
<groupId>top.continew.admin</groupId>
<artifactId>continew-plugin-open</artifactId>
</dependency>
<!-- 代码生成器插件 -->
<dependency>
<groupId>top.continew</groupId>
<groupId>top.continew.admin</groupId>
<artifactId>continew-plugin-generator</artifactId>
</dependency>
<!-- ContiNew Starter 链路追踪模块 -->
<dependency>
<groupId>top.continew</groupId>
<groupId>top.continew.starter</groupId>
<artifactId>continew-starter-trace</artifactId>
</dependency>

View File

@@ -29,12 +29,13 @@ import org.dromara.x.file.storage.spring.EnableFileStorage;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.openfeign.EnableFeignClients;
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.core.autoconfigure.application.ApplicationProperties;
import top.continew.starter.extension.crud.annotation.EnableCrudRestController;
import top.continew.starter.web.annotation.EnableGlobalResponse;
import top.continew.starter.web.model.R;
@@ -56,7 +57,7 @@ import top.continew.starter.web.model.R;
@RequiredArgsConstructor
public class ContiNewAdminApplication implements ApplicationRunner {
private final ProjectProperties projectProperties;
private final ApplicationProperties applicationProperties;
private final ServerProperties serverProperties;
public static void main(String[] args) {
@@ -67,7 +68,7 @@ public class ContiNewAdminApplication implements ApplicationRunner {
@SaIgnore
@GetMapping("/")
public R index() {
return R.ok(projectProperties);
return R.ok(applicationProperties);
}
@Override
@@ -76,18 +77,19 @@ public class ContiNewAdminApplication implements ApplicationRunner {
Integer port = serverProperties.getPort();
String contextPath = serverProperties.getServlet().getContextPath();
String baseUrl = URLUtil.normalize("%s:%s%s".formatted(hostAddress, port, contextPath));
log.info("----------------------------------------------");
log.info("{} service started successfully.", projectProperties.getName());
log.info("Profile: {}", SpringUtil.getProperty("spring.profiles.active"));
log.info("项目版本: v{} (ContiNew Starter: v{})", projectProperties.getVersion(), SpringUtil
.getProperty("project.starter"));
log.info("API 地址: {}", baseUrl);
log.info("-----------------------------------------------------");
log.info("{} server started successfully.", applicationProperties.getName());
log.info("ContiNew Starter: v{} (Spring Boot: v{})", SpringUtil
.getProperty("application.starter"), SpringBootVersion.getVersion());
log.info("当前版本: v{} (Profile: {})", applicationProperties.getVersion(), SpringUtil
.getProperty("spring.profiles.active"));
log.info("服务地址: {}", baseUrl);
Knife4jProperties knife4jProperties = SpringUtil.getBean(Knife4jProperties.class);
if (!knife4jProperties.isProduction()) {
log.info("API 文档: {}/doc.html", baseUrl);
log.info("接口文档: {}/doc.html", baseUrl);
}
log.info("在线文档: https://continew.top");
log.info("常见问题: https://continew.top/admin/faq.html");
log.info("----------------------------------------------");
log.info("-----------------------------------------------------");
}
}

View File

@@ -16,14 +16,14 @@
package top.continew.admin.config.satoken;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.sign.SaSignUtil;
import cn.dev33.satoken.sign.SaSignManager;
import cn.dev33.satoken.sign.template.SaSignTemplate;
import cn.dev33.satoken.sign.template.SaSignUtil;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import lombok.RequiredArgsConstructor;
@@ -41,7 +41,7 @@ import top.continew.admin.open.sign.OpenApiSignTemplate;
import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.exception.BusinessException;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import java.util.*;
@@ -75,7 +75,7 @@ public class SaTokenConfiguration {
*/
@Bean
public SaInterceptor saInterceptor() {
SaManager.setSaSignTemplate(signTemplate);
SaSignManager.setSaSignTemplate(signTemplate);
return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN)
.notMatch(properties.getSecurity().getExcludes())
.check(r -> {
@@ -101,7 +101,7 @@ public class SaTokenConfiguration {
}
/**
* 配置 sa-token SaIgnore 注解排除路径
* 配置 sa-token {@link SaIgnore} 注解排除路径
* <p>主要针对 @CrudRequestMapping 注解</p>
*/
@EventListener(ApplicationReadyEvent.class)
@@ -113,11 +113,11 @@ public class SaTokenConfiguration {
if (AopUtils.isAopProxy(bean)) {
clazz = AopProxyUtils.ultimateTargetClass(bean);
}
// 使用 @CrudRequestMapping 的 Controller如果使用了 @SaIgnore 注解,则表示忽略校验
CrudRequestMapping crudRequestMapping = AnnotationUtils.findAnnotation(clazz, CrudRequestMapping.class);
SaIgnore saIgnore = AnnotationUtils.findAnnotation(clazz, SaIgnore.class);
if (crudRequestMapping != null && saIgnore != null) {
return crudRequestMapping.value() + "/**";
return crudRequestMapping.value() + StringConstants.PATH_PATTERN;
}
return null;
}).filter(Objects::nonNull).toList();

View File

@@ -52,16 +52,16 @@ import top.continew.admin.system.service.OptionService;
import top.continew.admin.system.service.SmsConfigService;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.captcha.graphic.core.GraphicCaptchaService;
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
import top.continew.starter.core.autoconfigure.application.ApplicationProperties;
import top.continew.starter.core.util.TemplateUtils;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.core.validation.constraints.Mobile;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.log.annotation.Log;
import top.continew.starter.messaging.mail.util.MailUtils;
import top.continew.starter.ratelimiter.annotation.RateLimiter;
import top.continew.starter.ratelimiter.annotation.RateLimiters;
import top.continew.starter.ratelimiter.enums.LimitType;
import top.continew.starter.validation.constraints.Mobile;
import top.continew.starter.web.model.R;
import java.time.Duration;
@@ -84,7 +84,7 @@ import java.util.concurrent.TimeUnit;
@RequestMapping("/captcha")
public class CaptchaController {
private final ProjectProperties projectProperties;
private final ApplicationProperties applicationProperties;
private final CaptchaProperties captchaProperties;
private final CaptchaService behaviorCaptchaService;
private final GraphicCaptchaService graphicCaptchaService;
@@ -162,12 +162,12 @@ public class CaptchaController {
Long expirationInMinutes = captchaMail.getExpirationInMinutes();
Map<String, String> siteConfig = optionService.getByCategory(OptionCategoryEnum.SITE);
String content = TemplateUtils.render(captchaMail.getTemplatePath(), Dict.create()
.set("siteUrl", projectProperties.getUrl())
.set("siteUrl", applicationProperties.getUrl())
.set("siteTitle", siteConfig.get("SITE_TITLE"))
.set("siteCopyright", siteConfig.get("SITE_COPYRIGHT"))
.set("captcha", captcha)
.set("expiration", expirationInMinutes));
MailUtils.sendHtml(email, "【%s】邮箱验证码".formatted(projectProperties.getName()), content);
MailUtils.sendHtml(email, "【%s】邮箱验证码".formatted(applicationProperties.getName()), content);
// 保存验证码
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email;
RedisUtils.set(captchaKey, captcha, Duration.ofMinutes(expirationInMinutes));

View File

@@ -35,7 +35,7 @@ import top.continew.admin.system.enums.OptionCategoryEnum;
import top.continew.admin.system.model.query.*;
import top.continew.admin.system.model.resp.file.FileUploadResp;
import top.continew.admin.system.service.*;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.log.annotation.Log;
@@ -68,7 +68,7 @@ public class CommonController {
@Operation(summary = "上传文件", description = "上传文件")
@Parameter(name = "parentPath", description = "上级目录", example = "/", in = ParameterIn.QUERY)
@PostMapping("/file")
public FileUploadResp upload(@NotNull(message = "文件不能为空") @RequestPart MultipartFile file,
public FileUploadResp upload(@RequestPart @NotNull(message = "文件不能为空") MultipartFile file,
@RequestParam(required = false) String parentPath) throws IOException {
ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
FileInfo fileInfo = fileService.upload(file, parentPath);

View File

@@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -36,7 +35,7 @@ import top.continew.admin.system.model.resp.dashboard.DashboardChartCommonResp;
import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp;
import top.continew.admin.system.model.resp.dashboard.DashboardOverviewCommonResp;
import top.continew.admin.system.service.DashboardService;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.log.annotation.Log;
import java.io.IOException;
@@ -50,7 +49,6 @@ import java.util.List;
*/
@Tag(name = "仪表盘 API")
@Log(ignore = true)
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/dashboard")

View File

@@ -22,13 +22,13 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.continew.admin.auth.model.query.OnlineUserQuery;
import top.continew.admin.auth.model.resp.OnlineUserResp;
import top.continew.admin.auth.service.OnlineUserService;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.resp.PageResp;
@@ -49,7 +49,7 @@ public class OnlineUserController {
@Operation(summary = "分页查询列表", description = "分页查询列表")
@SaCheckPermission("monitor:online:list")
@GetMapping
public PageResp<OnlineUserResp> page(OnlineUserQuery query, @Validated PageQuery pageQuery) {
public PageResp<OnlineUserResp> page(@Valid OnlineUserQuery query, @Valid PageQuery pageQuery) {
return baseService.page(query, pageQuery);
}

View File

@@ -4,6 +4,6 @@
| |___| (_) || | | || |_ | || |\ || __/ \ V V /|_____|/ ___ \| (_| || | | | | || || | | |
\____|\___/ |_| |_| \__||_||_| \_| \___| \_/\_/ /_/ \_\\__,_||_| |_| |_||_||_| |_|
:: ${project.name} :: v${project.version}
:: ContiNew Starter :: v${project.starter}
:: ${application.name} :: v${application.version} (Profile: ${spring.profiles.active})
:: ContiNew Starter :: v${application.starter}
:: Spring Boot :: v${spring-boot.version}

View File

@@ -1,5 +1,5 @@
--- ### 项目配置
project:
--- ### 应用配置
application:
# URL跨域配置默认放行此 URL第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL
url: http://localhost:5173
@@ -234,11 +234,11 @@ justauth:
GITEE:
client-id: 5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${project.url}/social/callback?source=gitee
redirect-uri: ${application.url}/social/callback?source=gitee
GITHUB:
client-id: 38080dad08cfbdfacca9
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${project.url}/social/callback?source=github
redirect-uri: ${application.url}/social/callback?source=github
cache:
type: REDIS

View File

@@ -1,5 +1,5 @@
--- ### 项目配置
project:
--- ### 应用配置
application:
# URL跨域配置默认放行此 URL第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL
url: https://admin.continew.top
# 是否为生产环境
@@ -99,14 +99,14 @@ jetcache:
## 接口文档增强配置
knife4j:
# 开启生产环境屏蔽
production: ${project.production}
production: ${application.production}
--- ### 跨域配置
continew-starter.web.cors:
enabled: true
# 配置允许跨域的域名
allowed-origins:
- ${project.url}
- ${application.url}
# 配置允许跨域的请求方式
allowed-methods: '*'
# 配置允许跨域的请求头
@@ -213,7 +213,7 @@ continew-starter.messaging.websocket:
path: /websocket
# 配置允许跨域的域名
allowed-origins:
- ${project.url}
- ${application.url}
--- ### Sa-Token 扩展配置
sa-token.extension:
@@ -236,11 +236,11 @@ justauth:
GITEE:
client-id: 5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${project.url}/social/callback?source=gitee
redirect-uri: ${application.url}/social/callback?source=gitee
GITHUB:
client-id: 38080dad08cfbdfacca9
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${project.url}/social/callback?source=github
redirect-uri: ${application.url}/social/callback?source=github
cache:
type: REDIS

View File

@@ -1,14 +1,13 @@
--- ### 项目配置
project:
--- ### 应用配置
application:
id: continew-admin
# 名称
name: ContiNew Admin
# 应用名称
app-name: continew-admin
# 版本
version: 4.0.0-SNAPSHOT
starter: 2.12.2
# 描述
description: 持续迭代优化的前后端分离中后台管理系统框架,开箱即用,持续提供舒适的开发体验。
# 版本
version: 4.0.0-SNAPSHOT
starter: 2.13.0
# 基本包
base-package: top.continew.admin
## 作者信息配置
@@ -44,7 +43,7 @@ server:
--- ### Spring 项目配置
spring:
application:
name: ${project.app-name}
name: ${application.id}
## 环境配置
profiles:
# 启用的环境
@@ -133,7 +132,7 @@ knife4j:
# 是否自定义 footer默认 false非自定义
enable-footer-custom: true
# 自定义 footer 内容,支持 Markdown 语法
footer-custom-content: 'Copyright © 2022-present [${project.contact.name}](${project.contact.url})&nbsp;⋅&nbsp;[${project.name}](${project.url}) v${project.version}'
footer-custom-content: 'Copyright © 2022-present [${application.contact.name}](${application.contact.url})&nbsp;⋅&nbsp;[${application.name}](${application.url}) v${application.version}'
--- ### 全局响应配置
continew-starter.web:
@@ -236,7 +235,7 @@ mybatis-plus:
# Mapper XML 文件目录配置
mapper-locations: classpath*:/mapper/**/*Mapper.xml
# 类型别名扫描包配置
type-aliases-package: ${project.base-package}.**.model
type-aliases-package: ${application.base-package}.**.model
## MyBatis 配置
configuration:
# MyBatis 自动映射策略
@@ -255,7 +254,7 @@ mybatis-plus:
extension:
enabled: true
# Mapper 接口扫描包配置
mapper-package: ${project.base-package}.**.mapper
mapper-package: ${application.base-package}.**.mapper
# ID 生成器配置
id-generator:
type: COSID