build: continew-starter 2.13.0 => 2.13.1

1.DataPermissionUserContextProvider 相关命名调整,以避免和 Admin 内类名冲突
DataPermissionUserContextProvider => DataPermissionUserDataProvider
UserContext => UserData
RoleContext => RoleData
2.引入 crane4j 依赖及填充处理(Starter 为了扩展性,移除了此依赖及相关处理)
3.API 替换
3.1SpringUtil.getBean(TenantHandler.class) => TenantUtils
3.2JakartaServletUtil.write => ServletUtils.writeJSON
3.3tenantExtensionProperties.isEnabled() => TenantContextHolder.isTenantEnabled()
4.Starter 内部修复
4.1FastExcel POI 版本冲突导致的导出报错
4.2EnumValue 校验支持了 BaseEnum
This commit is contained in:
2025-07-17 22:29:09 +08:00
parent 7e9a950694
commit 6136797588
16 changed files with 72 additions and 59 deletions

View File

@@ -4,7 +4,7 @@
<img src="https://img.shields.io/badge/SNAPSHOT-v4.0.0-%23ff3f59.svg" alt="Release" />
</a>
<a href="https://github.com/continew-org/continew-starter" title="ContiNew Starter" target="_blank">
<img src="https://img.shields.io/badge/ContiNew Starter-2.13.0-%236CB52D.svg" alt="ContiNew Starter" />
<img src="https://img.shields.io/badge/ContiNew Starter-2.13.1-%236CB52D.svg" alt="ContiNew Starter" />
</a>
<a href="https://spring.io/projects/spring-boot" title="Spring Boot" target="_blank">
<img src="https://img.shields.io/badge/Spring Boot-3.3.12-%236CB52D.svg?logo=Spring-Boot" alt="Spring Boot" />
@@ -221,13 +221,13 @@ public class DeptController extends BaseController<DeptService, DeptResp, DeptDe
## 核心技术栈
| 名称 | 版本 | 简介 |
| :----------------------------------------------------------- | :----------- | :----------------------------------------------------------- |
| 名称 | 版本 | 简介 |
| :----------------------------------------------------------- |:-------------| :----------------------------------------------------------- |
| <a href="https://vuejs.org/" target="_blank">Vue</a> | 3.5.4 | 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 |
| <a href="https://arco.design/vue/docs/start" target="_blank">Arco Design</a> | 2.57.0 | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。 |
| <a href="https://www.typescriptlang.org/zh/" target="_blank">TypeScript</a> | 5.0.4 | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 |
| <a href="https://vite.dev/" target="_blank">Vite</a> | 5.1.5 | 下一代的前端工具链,为开发提供极速响应。 |
| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.13.0 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken可轻松集成到应用中为开发人员减少手动引入依赖及配置的麻烦为 Spring Boot Web 项目的灵活快速构建提供支持。 |
| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.13.1 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken可轻松集成到应用中为开发人员减少手动引入依赖及配置的麻烦为 Spring Boot Web 项目的灵活快速构建提供支持。 |
| <a href="https://spring.io/projects/spring-boot" target="_blank">Spring Boot</a> | 3.3.12 | 简化 Spring 应用的初始搭建和开发过程基于“约定优于配置”的理念使开发人员不再需要定义样板化的配置。Spring Boot 3.0 开始,要求 Java 17 作为最低版本) |
| <a href="https://undertow.io/" target="_blank">Undertow</a> | 2.3.18.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 |
| <a href="https://sa-token.dev33.cn/" target="_blank">Sa-Token + JWT</a> | 1.44.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 |

View File

@@ -55,6 +55,12 @@
<artifactId>postgresql</artifactId>
</dependency>-->
<!-- Crane4j基于注解的用于完成一切 “根据 A 的 key 值拿到 B再把 B 的属性映射到 A” 这类需求的字段填充框架) -->
<dependency>
<groupId>cn.crane4j</groupId>
<artifactId>crane4j-spring-boot-starter</artifactId>
</dependency>
<!-- ContiNew Starter JSON 模块 - Jackson -->
<dependency>
<groupId>top.continew.starter</groupId>

View File

@@ -16,6 +16,8 @@
package top.continew.admin.common.base.service;
import cn.crane4j.core.support.OperateTemplate;
import cn.hutool.extra.spring.SpringUtil;
import top.continew.starter.data.mapper.BaseMapper;
import top.continew.starter.extension.crud.model.entity.BaseIdDO;
import top.continew.starter.extension.crud.service.CrudServiceImpl;
@@ -38,4 +40,18 @@ import top.continew.starter.extension.crud.service.CrudServiceImpl;
* @since 2024/12/6 20:30
*/
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseIdDO, L, D, Q, C> extends CrudServiceImpl<M, T, L, D, Q, C> implements BaseService<L, D, Q, C> {
/**
* 填充数据
*
* @param obj 待填充信息
*/
@Override
protected void fill(Object obj) {
if (obj == null) {
return;
}
OperateTemplate operateTemplate = SpringUtil.getBean(OperateTemplate.class);
operateTemplate.execute(obj);
}
}

View File

@@ -35,11 +35,6 @@ import java.util.List;
@ConfigurationProperties(prefix = PropertiesConstants.TENANT)
public class TenantExtensionProperties {
/**
* 是否启用
*/
private boolean enabled;
/**
* 请求头中租户编码键名
*/

View File

@@ -17,21 +17,22 @@
package top.continew.admin.common.config.mybatis;
import cn.hutool.core.convert.Convert;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
import top.continew.starter.extension.datapermission.enums.DataScope;
import top.continew.starter.extension.datapermission.filter.DataPermissionUserContextProvider;
import top.continew.starter.extension.datapermission.model.RoleContext;
import top.continew.starter.extension.datapermission.model.UserContext;
import top.continew.starter.extension.datapermission.filter.DataPermissionUserDataProvider;
import top.continew.starter.extension.datapermission.model.RoleData;
import top.continew.starter.extension.datapermission.model.UserData;
import java.util.stream.Collectors;
/**
* 数据权限用户上下文提供者
* 数据权限用户数据提供者
*
* @author Charles7c
* @since 2023/12/21 21:19
*/
public class DefaultDataPermissionUserContextProvider implements DataPermissionUserContextProvider {
public class DefaultDataPermissionUserDataProvider implements DataPermissionUserDataProvider {
@Override
public boolean isFilter() {
@@ -39,15 +40,15 @@ public class DefaultDataPermissionUserContextProvider implements DataPermissionU
}
@Override
public UserContext getUserContext() {
top.continew.admin.common.context.UserContext context = UserContextHolder.getContext();
UserContext userContext = new UserContext();
userContext.setUserId(Convert.toStr(context.getId()));
userContext.setDeptId(Convert.toStr(context.getDeptId()));
userContext.setRoles(context.getRoles()
public UserData getUserData() {
UserContext userContext = UserContextHolder.getContext();
UserData userData = new UserData();
userData.setUserId(Convert.toStr(userContext.getId()));
userData.setDeptId(Convert.toStr(userContext.getDeptId()));
userData.setRoles(userContext.getRoles()
.stream()
.map(r -> new RoleContext(Convert.toStr(r.getId()), DataScope.valueOf(r.getDataScope().name())))
.map(r -> new RoleData(Convert.toStr(r.getId()), DataScope.valueOf(r.getDataScope().name())))
.collect(Collectors.toSet()));
return userContext;
return userData;
}
}

View File

@@ -21,6 +21,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import top.continew.starter.extension.datapermission.filter.DataPermissionUserContextProvider;
import top.continew.starter.extension.datapermission.filter.DataPermissionUserDataProvider;
/**
* MyBatis Plus 配置
@@ -40,11 +41,11 @@ public class MybatisPlusConfiguration {
}
/**
* 数据权限用户上下文提供者
* 数据权限用户数据提供者
*/
@Bean
public DataPermissionUserContextProvider dataPermissionUserContextProvider() {
return new DefaultDataPermissionUserContextProvider();
public DataPermissionUserDataProvider dataPermissionUserDataProvider() {
return new DefaultDataPermissionUserDataProvider();
}
/**

View File

@@ -71,10 +71,10 @@ public class RegexConstants {
* </p>
*/
public static final String HTTP_HOST = """
^((?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}|
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|
""";
^((?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}|
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|
""";
private RegexConstants() {
}

View File

@@ -17,7 +17,6 @@
package top.continew.admin.tenant.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.extra.spring.SpringUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
@@ -42,7 +41,7 @@ import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import top.continew.starter.extension.crud.enums.Api;
import top.continew.starter.extension.tenant.TenantHandler;
import top.continew.starter.extension.tenant.util.TenantUtils;
/**
* 租户管理 API
@@ -65,7 +64,7 @@ public class TenantController extends BaseController<TenantService, TenantResp,
public void updateAdminUserPwd(@Valid @RequestBody TenantAdminUserPwdUpdateReq req, @PathVariable Long id) {
TenantDO tenant = baseService.getById(id);
String encryptPassword = req.getPassword();
SpringUtil.getBean(TenantHandler.class).execute(id, () -> {
TenantUtils.execute(id, () -> {
UserDO user = userService.getById(tenant.getAdminUser());
String password = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(encryptPassword));
ValidationUtils.throwIfNull(password, "新密码解密失败");

View File

@@ -19,7 +19,6 @@ package top.continew.admin.tenant.handler;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
@@ -51,7 +50,7 @@ import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.crud.model.entity.BaseIdDO;
import top.continew.starter.extension.tenant.TenantHandler;
import top.continew.starter.extension.tenant.util.TenantUtils;
import java.time.LocalDateTime;
import java.util.List;
@@ -90,7 +89,7 @@ public class TenantDataHandlerForSystem implements TenantDataHandler {
@Transactional(rollbackFor = Exception.class)
public void init(TenantReq tenant) {
Long tenantId = tenant.getId();
SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> {
TenantUtils.execute(tenantId, () -> {
// 初始化部门
Long deptId = this.initDeptData(tenant);
// 初始化菜单

View File

@@ -17,7 +17,6 @@
package top.continew.admin.tenant.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alicp.jetcache.anno.Cached;
import lombok.RequiredArgsConstructor;
import me.ahoo.cosid.provider.IdGeneratorProvider;
@@ -40,8 +39,8 @@ import top.continew.admin.tenant.service.PackageService;
import top.continew.admin.tenant.service.TenantService;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.extension.tenant.TenantHandler;
import top.continew.starter.extension.tenant.autoconfigure.TenantProperties;
import top.continew.starter.extension.tenant.util.TenantUtils;
import java.io.Serializable;
import java.time.LocalDateTime;
@@ -103,7 +102,7 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
public void beforeDelete(List<Long> ids) {
// 在租户中执行数据清除
for (Long id : ids) {
SpringUtil.getBean(TenantHandler.class).execute(id, tenantDataHandler::clear);
TenantUtils.execute(id, tenantDataHandler::clear);
}
}
@@ -153,7 +152,7 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
deleteMenuIds.removeAll(newMenuIds);
if (CollUtil.isNotEmpty(deleteMenuIds)) {
List<MenuDO> deleteMenus = menuService.listByIds(deleteMenuIds);
tenantIdList.forEach(tenantId -> SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> menuService
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> menuService
.deleteTenantMenus(deleteMenus)));
}
// 如果有新增的菜单则绑定了套餐的租户对应的菜单也会新增
@@ -163,7 +162,7 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
List<MenuDO> addMenus = menuService.listByIds(addMenuIds);
for (MenuDO addMenu : addMenus) {
MenuDO parentMenu = addMenu.getParentId() != 0 ? menuService.getById(addMenu.getParentId()) : null;
tenantIdList.forEach(tenantId -> SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> menuService
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> menuService
.addTenantMenu(addMenu, parentMenu)));
}
}

View File

@@ -23,7 +23,6 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
@@ -42,8 +41,8 @@ import top.continew.admin.system.service.UserService;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.StrUtils;
import top.continew.starter.extension.tenant.TenantHandler;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import top.continew.starter.extension.tenant.util.TenantUtils;
import top.continew.starter.log.dao.LogDao;
import top.continew.starter.log.model.LogRecord;
import top.continew.starter.log.model.LogRequest;
@@ -89,7 +88,7 @@ public class LogDaoLocalImpl implements LogDao {
// 设置操作人
this.setCreateUser(logDO, logRequest, logResponse);
Long tenantId = TenantContextHolder.getTenantId();
SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> logMapper.insert(logDO));
TenantUtils.execute(tenantId, () -> logMapper.insert(logDO));
}
/**

View File

@@ -19,16 +19,14 @@ package top.continew.admin.config.satoken;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import top.continew.starter.web.model.R;
@@ -59,12 +57,13 @@ public class SaExtensionInterceptor extends SaInterceptor {
return true;
}
// 检查用户租户权限
Long userTenantId = userContext.getTenantId();
Long tenantId = TenantContextHolder.getTenantId();
if (!userTenantId.equals(tenantId)) {
JakartaServletUtil.write(response, JSONUtil.toJsonStr(R.fail(String.valueOf(HttpStatus.FORBIDDEN
.value()), "您当前没有访问该租户的权限")), MediaType.APPLICATION_JSON_VALUE);
return false;
if (TenantContextHolder.isTenantEnabled()) {
Long userTenantId = userContext.getTenantId();
Long tenantId = TenantContextHolder.getTenantId();
if (!userTenantId.equals(tenantId)) {
ServletUtils.writeJSON(response, R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "您当前没有访问该租户的权限"));
return false;
}
}
UserContextHolder.getExtraContext();
return true;

View File

@@ -7,7 +7,7 @@ application:
description: 持续迭代优化的前后端分离中后台管理系统框架,开箱即用,持续提供舒适的开发体验。
# 版本
version: 4.0.0-SNAPSHOT
starter: 2.13.0
starter: 2.13.1
# 基本包
base-package: top.continew.admin
## 作者信息配置

View File

@@ -19,7 +19,6 @@ package top.continew.admin.auth;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.spring.SpringUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -40,8 +39,8 @@ import top.continew.admin.system.service.UserService;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.core.util.validation.Validator;
import top.continew.starter.extension.tenant.TenantHandler;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import top.continew.starter.extension.tenant.util.TenantUtils;
import java.util.HashSet;
import java.util.Set;
@@ -97,14 +96,14 @@ public abstract class AbstractLoginHandler<T extends LoginReq> implements LoginH
Long tenantId = TenantContextHolder.getTenantId();
CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> {
Set<String> permissions = new HashSet<>();
SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> {
TenantUtils.execute(tenantId, () -> {
permissions.addAll(roleService.listPermissionByUserId(userId));
});
return permissions;
}, threadPoolTaskExecutor);
CompletableFuture<Set<RoleContext>> roleFuture = CompletableFuture.supplyAsync(() -> {
Set<RoleContext> roles = new HashSet<>();
SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> {
TenantUtils.execute(tenantId, () -> {
roles.addAll(roleService.listByUserId(userId));
});
return roles;

View File

@@ -93,7 +93,7 @@ public class OnlineUserServiceImpl implements OnlineUserService {
continue;
}
// 只显示本租户数据
if (tenantExtensionProperties.isEnabled()) {
if (TenantContextHolder.isTenantEnabled()) {
if (!TenantContextHolder.getTenantId().equals(userContext.getTenantId())) {
continue;
}

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>top.continew.starter</groupId>
<artifactId>continew-starter</artifactId>
<version>2.13.0</version>
<version>2.13.1</version>
</parent>
<groupId>top.continew.admin</groupId>