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

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