mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-15 14:57:15 +08:00
feat(plugin/tenant): 新增多租户插件模块 (#175)
This commit is contained in:
@@ -167,5 +167,18 @@
|
||||
<groupId>top.continew.starter</groupId>
|
||||
<artifactId>continew-starter-extension-crud-mp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 多租户组件-->
|
||||
<dependency>
|
||||
<groupId>top.continew.starter</groupId>
|
||||
<artifactId>continew-starter-extension-tenant-mp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Dynamic Datasource(基于 Spring Boot 的快速集成多数据源的启动器) -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 多租户配置
|
||||
* @author: 小熊
|
||||
* @create: 2024-11-29 12:05
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = PropertiesConstants.TENANT)
|
||||
@Data
|
||||
public class TenantProperties {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private List<Long> ignoreMenus;
|
||||
|
||||
}
|
@@ -71,6 +71,16 @@ public class CacheConstants {
|
||||
*/
|
||||
public static final String DATA_IMPORT_KEY = "SYSTEM" + DELIMITER + "DATA_IMPORT" + DELIMITER;
|
||||
|
||||
/**
|
||||
* 数据连接键前缀
|
||||
*/
|
||||
public static final String DB_CONNECT_KEY_PREFIX = "DB_CONNECT" + DELIMITER;
|
||||
|
||||
/**
|
||||
* 租户信息前缀
|
||||
*/
|
||||
public static final String TENANT_KEY = "TENANT" + DELIMITER;
|
||||
|
||||
private CacheConstants() {
|
||||
}
|
||||
}
|
||||
|
@@ -84,6 +84,31 @@ public class SysConstants {
|
||||
*/
|
||||
public static final String LOGOUT_URI = "/auth/logout";
|
||||
|
||||
/**
|
||||
* 描述类字段后缀
|
||||
*/
|
||||
public static final String DESCRIPTION_FIELD_SUFFIX = "String";
|
||||
|
||||
/**
|
||||
* 租户数据库前缀
|
||||
*/
|
||||
public static final String TENANT_DB_PREFIX = "tenant_";
|
||||
|
||||
/**
|
||||
* 默认租户
|
||||
*/
|
||||
public static final String DEFAULT_TENANT = "0";
|
||||
|
||||
/**
|
||||
* 默认数据源
|
||||
*/
|
||||
public static final String DEFAULT_DATASOURCE = "master";
|
||||
|
||||
/**
|
||||
* 租户管理员角色编码
|
||||
*/
|
||||
public static final String TENANT_ADMIN_CODE = "tenant_admin";
|
||||
|
||||
private SysConstants() {
|
||||
}
|
||||
}
|
||||
|
@@ -80,7 +80,7 @@ public class UserContext implements Serializable {
|
||||
*/
|
||||
private Set<RoleContext> roles;
|
||||
|
||||
/**
|
||||
/*
|
||||
* 客户端类型
|
||||
*/
|
||||
private String clientType;
|
||||
@@ -90,6 +90,11 @@ public class UserContext implements Serializable {
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 租户 ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
public UserContext(Set<String> permissions, Set<RoleContext> roles, Integer passwordExpirationDays) {
|
||||
this.permissions = permissions;
|
||||
this.setRoles(roles);
|
||||
@@ -129,4 +134,5 @@ public class UserContext implements Serializable {
|
||||
}
|
||||
return this.pwdResetTime.plusDays(this.passwordExpirationDays).isBefore(LocalDateTime.now());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -180,4 +180,11 @@ public class UserContextHolder {
|
||||
StpUtil.checkLogin();
|
||||
return getContext().isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户ID
|
||||
*/
|
||||
public static Long getTenantId() {
|
||||
return ExceptionUtils.exToNull(() -> getContext().getTenantId());
|
||||
}
|
||||
}
|
||||
|
@@ -55,4 +55,5 @@ public interface AppService extends BaseService<AppResp, AppDetailResp, AppQuery
|
||||
* @return 应用信息
|
||||
*/
|
||||
AppDO getByAccessKey(String accessKey);
|
||||
|
||||
}
|
21
continew-plugin/continew-plugin-tenant/pom.xml
Normal file
21
continew-plugin/continew-plugin-tenant/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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.admin</groupId>
|
||||
<artifactId>continew-plugin</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>continew-plugin-tenant</artifactId>
|
||||
<description>多租户插件</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>top.continew.admin</groupId>
|
||||
<artifactId>continew-system</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.tenant.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.continew.admin.common.base.controller.BaseController;
|
||||
import top.continew.admin.common.config.properties.TenantProperties;
|
||||
import top.continew.admin.common.util.SecureUtils;
|
||||
import top.continew.admin.system.model.entity.MenuDO;
|
||||
import top.continew.admin.system.model.entity.user.UserDO;
|
||||
import top.continew.admin.system.model.req.user.UserPasswordResetReq;
|
||||
import top.continew.admin.system.service.*;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.query.TenantQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantLoginUserInfoReq;
|
||||
import top.continew.admin.tenant.model.req.TenantReq;
|
||||
import top.continew.admin.tenant.model.resp.*;
|
||||
import top.continew.admin.tenant.service.TenantDbConnectService;
|
||||
import top.continew.admin.tenant.service.TenantPackageService;
|
||||
import top.continew.admin.tenant.service.TenantService;
|
||||
import top.continew.starter.core.util.ExceptionUtils;
|
||||
import top.continew.starter.core.util.validation.CheckUtils;
|
||||
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.crud.model.entity.BaseIdDO;
|
||||
import top.continew.admin.common.base.model.resp.BaseResp;
|
||||
import top.continew.starter.extension.crud.model.req.IdsReq;
|
||||
import top.continew.starter.extension.crud.model.resp.IdResp;
|
||||
import top.continew.starter.extension.tenant.TenantHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户管理 API
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Tag(name = "租户管理 API")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@CrudRequestMapping(value = "/tenant/user", api = {Api.PAGE, Api.GET, Api.CREATE, Api.UPDATE, Api.DELETE})
|
||||
public class TenantController extends BaseController<TenantService, TenantResp, TenantDetailResp, TenantQuery, TenantReq> {
|
||||
|
||||
private final TenantProperties tenantProperties;
|
||||
private final DeptService deptService;
|
||||
private final MenuService menuService;
|
||||
private final TenantPackageService packageService;
|
||||
private final RoleService roleService;
|
||||
private final UserService userService;
|
||||
private final TenantSysDataService tenantSysDataService;
|
||||
private final RoleMenuService roleMenuService;
|
||||
private final TenantDbConnectService dbConnectService;
|
||||
|
||||
@GetMapping("/common")
|
||||
@SaIgnore
|
||||
@Operation(summary = "多租户通用信息查询", description = "多租户通用信息查询")
|
||||
public TenantCommonResp common() {
|
||||
TenantCommonResp commonResp = new TenantCommonResp();
|
||||
commonResp.setIsEnabled(tenantProperties.isEnabled());
|
||||
commonResp.setAvailableList(baseService.getAvailableList());
|
||||
return commonResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DSTransactional
|
||||
public IdResp<Long> create(TenantReq req) {
|
||||
//套餐菜单
|
||||
TenantPackageDetailResp detailResp = packageService.get(req.getPackageId());
|
||||
CheckUtils.throwIf(detailResp.getMenuIds().isEmpty(), "该套餐无可用菜单");
|
||||
List<MenuDO> menuRespList = menuService.listByIds(detailResp.getMenuIds());
|
||||
//租户添加
|
||||
IdResp<Long> baseIdResp = super.create(req);
|
||||
//在租户中执行数据插入
|
||||
SpringUtil.getBean(TenantHandler.class).execute(baseIdResp.getId(), () -> {
|
||||
//租户部门初始化
|
||||
Long deptId = deptService.initTenantDept(req.getName());
|
||||
//租户菜单初始化
|
||||
menuService.menuInit(menuRespList, 0L, 0L);
|
||||
//租户角色初始化
|
||||
Long roleId = roleService.initTenantRole();
|
||||
//角色绑定菜单
|
||||
roleMenuService.add(menuService.listAll(baseIdResp.getId()).stream().map(BaseResp::getId).toList(), roleId);
|
||||
//管理用户初始化
|
||||
Long userId = userService.initTenantUser(req.getUsername(), req.getPassword(), deptId);
|
||||
//用户绑定角色
|
||||
roleService.assignToUsers(roleId, ListUtil.of(userId));
|
||||
//租户绑定用户
|
||||
baseService.bindUser(baseIdResp.getId(), userId);
|
||||
});
|
||||
return baseIdResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Long id) {
|
||||
SpringUtil.getBean(TenantHandler.class).execute(id, () -> {
|
||||
//系统数据清除
|
||||
tenantSysDataService.clear();
|
||||
});
|
||||
super.delete(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batchDelete(@Valid IdsReq ids) {
|
||||
for (Long id : ids.getIds()) {
|
||||
//在租户中执行数据清除
|
||||
SpringUtil.getBean(TenantHandler.class).execute(id, () -> {
|
||||
//系统数据清除
|
||||
tenantSysDataService.clear();
|
||||
});
|
||||
}
|
||||
super.batchDelete(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户管理账号用户名
|
||||
*/
|
||||
@GetMapping("/loginUser/{tenantId}")
|
||||
@Operation(summary = "获取租户管理账号信息", description = "获取租户管理账号信息")
|
||||
@SaCheckPermission("tenant:user:editLoginUserInfo")
|
||||
public String loginUserInfo(@PathVariable Long tenantId) {
|
||||
TenantDO tenantDO = baseService.getTenantById(tenantId);
|
||||
CheckUtils.throwIfNull(tenantDO, "租户不存在");
|
||||
StringBuilder username = new StringBuilder();
|
||||
SpringUtil.getBean(TenantHandler.class).execute(tenantDO.getId(), () -> {
|
||||
UserDO userDO = userService.getById(tenantDO.getUserId());
|
||||
CheckUtils.throwIfNull(userDO, "租户管理用户不存在");
|
||||
username.append(userDO.getUsername());
|
||||
});
|
||||
return username.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户管理账号信息更新
|
||||
*/
|
||||
@PutMapping("/loginUser")
|
||||
@Operation(summary = "租户管理账号信息更新", description = "租户管理账号信息更新")
|
||||
@SaCheckPermission("tenant:user:editLoginUserInfo")
|
||||
@DSTransactional
|
||||
public void editLoginUserInfo(@Validated @RequestBody TenantLoginUserInfoReq req) {
|
||||
TenantDO tenantDO = baseService.getTenantById(req.getTenantId());
|
||||
CheckUtils.throwIfNull(tenantDO, "租户不存在");
|
||||
SpringUtil.getBean(TenantHandler.class).execute(tenantDO.getId(), () -> {
|
||||
UserDO userDO = userService.getById(tenantDO.getUserId());
|
||||
CheckUtils.throwIfNull(userDO, "用户不存在");
|
||||
//修改用户名
|
||||
if (!req.getUsername().equals(userDO.getUsername())) {
|
||||
userService.update(Wrappers.lambdaUpdate(UserDO.class)
|
||||
.set(UserDO::getUsername, req.getUsername())
|
||||
.eq(BaseIdDO::getId, userDO.getId()));
|
||||
}
|
||||
//修改密码
|
||||
if (StrUtil.isNotEmpty(req.getPassword())) {
|
||||
String password = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword()));
|
||||
ValidationUtils.throwIfNull(password, "新密码解密失败");
|
||||
UserPasswordResetReq passwordResetReq = new UserPasswordResetReq();
|
||||
passwordResetReq.setNewPassword(password);
|
||||
userService.resetPassword(passwordResetReq, userDO.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有租户套餐
|
||||
*/
|
||||
@GetMapping("/all/package")
|
||||
@Operation(summary = "查询所有租户套餐", description = "查询所有租户套餐")
|
||||
@SaCheckPermission(value = {"tenant:user:add", "tenant:user:update"}, mode = SaMode.OR)
|
||||
public List<TenantPackageResp> packageList() {
|
||||
return packageService.list(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有数据库连接
|
||||
*/
|
||||
@GetMapping("/all/dbConnect")
|
||||
@Operation(summary = "获取租户数据连接列表", description = "获取租户数据连接列表")
|
||||
@SaCheckPermission(value = {"tenant:user:add", "tenant:user:update"}, mode = SaMode.OR)
|
||||
public List<TenantDbConnectResp> dbConnectList() {
|
||||
return dbConnectService.list(null, null);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.tenant.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.continew.admin.common.base.controller.BaseController;
|
||||
import top.continew.admin.tenant.model.query.TenantDbConnectQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantDbConnectReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectResp;
|
||||
import top.continew.admin.tenant.service.TenantDbConnectService;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
|
||||
/**
|
||||
* 租户数据连接管理 API
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Tag(name = "租户数据连接管理 API")
|
||||
@RestController
|
||||
@CrudRequestMapping(value = "/tenant/dbConnect", api = {Api.PAGE, Api.GET, Api.CREATE, Api.UPDATE, Api.DELETE})
|
||||
public class TenantDbConnectController extends BaseController<TenantDbConnectService, TenantDbConnectResp, TenantDbConnectDetailResp, TenantDbConnectQuery, TenantDbConnectReq> {}
|
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.tenant.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.continew.admin.common.base.controller.BaseController;
|
||||
import top.continew.admin.common.config.properties.TenantProperties;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.system.model.entity.MenuDO;
|
||||
import top.continew.admin.system.model.query.MenuQuery;
|
||||
import top.continew.admin.system.service.MenuService;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.query.TenantPackageQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantPackageReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantPackageDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantPackageResp;
|
||||
import top.continew.admin.tenant.service.TenantPackageService;
|
||||
import top.continew.admin.tenant.service.TenantService;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.tenant.TenantHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户套餐管理 API
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Tag(name = "租户套餐管理 API")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@CrudRequestMapping(value = "/tenant/package", api = {Api.LIST, Api.PAGE, Api.GET, Api.CREATE, Api.UPDATE, Api.DELETE})
|
||||
public class TenantPackageController extends BaseController<TenantPackageService, TenantPackageResp, TenantPackageDetailResp, TenantPackageQuery, TenantPackageReq> {
|
||||
|
||||
private final MenuService menuService;
|
||||
private final TenantProperties tenantProperties;
|
||||
private final TenantService tenantService;
|
||||
|
||||
@GetMapping("/menuTree")
|
||||
@SaCheckPermission("tenant:package:get")
|
||||
@Operation(summary = "获取租户套餐菜单", description = "获取租户套餐菜单")
|
||||
public List<Tree<Long>> menuTree() {
|
||||
MenuQuery query = new MenuQuery();
|
||||
//必须是启用状态的菜单
|
||||
query.setStatus(DisEnableStatusEnum.ENABLE);
|
||||
//过滤掉租户不能使用的菜单
|
||||
query.setExcludeMenuIdList(tenantProperties.getIgnoreMenus());
|
||||
return menuService.tree(query, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DSTransactional
|
||||
public void update(TenantPackageReq req, Long id) {
|
||||
//查询套餐对应的租户
|
||||
List<TenantDO> tenantDOList = tenantService.list(Wrappers.lambdaQuery(TenantDO.class)
|
||||
.eq(TenantDO::getPackageId, id));
|
||||
if (!tenantDOList.isEmpty()) {
|
||||
TenantPackageDetailResp detail = baseService.get(id);
|
||||
List<Long> oldMenuIds = detail.getMenuIds();
|
||||
List<Long> newMenuIds = Arrays.stream(req.getMenuIds()).toList();
|
||||
//删除的菜单
|
||||
List<Long> deleteMenuIds = new ArrayList<>(oldMenuIds);
|
||||
deleteMenuIds.removeAll(newMenuIds);
|
||||
//如果有删除的菜单则绑定了套餐的租户对应的菜单也会删除
|
||||
if (!deleteMenuIds.isEmpty()) {
|
||||
List<MenuDO> deleteMenus = menuService.listByIds(deleteMenuIds);
|
||||
tenantDOList.forEach(tenantDO -> SpringUtil.getBean(TenantHandler.class)
|
||||
.execute(tenantDO.getId(), () -> menuService.deleteTenantMenus(deleteMenus)));
|
||||
}
|
||||
//新增的菜单
|
||||
List<Long> addMenuIds = new ArrayList<>(newMenuIds);
|
||||
addMenuIds.removeAll(oldMenuIds);
|
||||
//如果有新增的菜单则绑定了套餐的租户对应的菜单也会新增
|
||||
if (!addMenuIds.isEmpty()) {
|
||||
List<MenuDO> addMenus = menuService.listByIds(addMenuIds);
|
||||
for (MenuDO addMenu : addMenus) {
|
||||
MenuDO pMenu = addMenu.getParentId() != 0 ? menuService.getById(addMenu.getParentId()) : null;
|
||||
tenantDOList.forEach(tenantDO -> SpringUtil.getBean(TenantHandler.class)
|
||||
.execute(tenantDO.getId(), () -> menuService.addTenantMenu(addMenu, pMenu)));
|
||||
}
|
||||
RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
|
||||
}
|
||||
}
|
||||
super.update(req, id);
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.tenant.mapper;
|
||||
|
||||
import top.continew.admin.tenant.model.entity.TenantDbConnectDO;
|
||||
import top.continew.starter.data.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 租户数据连接 Mapper
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
public interface TenantDbConnectMapper extends BaseMapper<TenantDbConnectDO> {}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.tenant.mapper;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.resp.TenantResp;
|
||||
import top.continew.starter.data.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 租户 Mapper
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@DS(SysConstants.DEFAULT_DATASOURCE)
|
||||
@Mapper
|
||||
public interface TenantMapper extends BaseMapper<TenantDO> {
|
||||
|
||||
@Select("SELECT sys_tenant.*,sys_tenant_package.`name` as package_name FROM sys_tenant\n" + "LEFT JOIN sys_tenant_package ON sys_tenant.package_id = sys_tenant_package.id\n" + "${ew.getCustomSqlSegment}")
|
||||
IPage<TenantResp> listTenant(IPage page, @Param(Constants.WRAPPER) Wrapper wrapper);
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.tenant.mapper;
|
||||
|
||||
import top.continew.starter.data.mapper.BaseMapper;
|
||||
import top.continew.admin.tenant.model.entity.TenantPackageDO;
|
||||
|
||||
/**
|
||||
* 租户套餐 Mapper
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
public interface TenantPackageMapper extends BaseMapper<TenantPackageDO> {
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.tenant.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.base.model.entity.BaseDO;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 租户实体
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_tenant")
|
||||
public class TenantDO extends BaseDO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 绑定的域名
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 租户套餐编号
|
||||
*/
|
||||
private Long packageId;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 租户过期时间
|
||||
*/
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private String tenantSn;
|
||||
|
||||
/**
|
||||
* 隔离级别
|
||||
*/
|
||||
private Integer isolationLevel;
|
||||
|
||||
/**
|
||||
* 数据连接ID
|
||||
*/
|
||||
private Long dbConnectId;
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.tenant.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.base.model.entity.BaseDO;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 租户数据连接实体
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_tenant_db_connect")
|
||||
public class TenantDbConnectDO extends BaseDO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 连接名称
|
||||
*/
|
||||
private String connectName;
|
||||
|
||||
/**
|
||||
* 连接类型
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 连接主机地址
|
||||
*/
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* 连接端口
|
||||
*/
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 连接用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 连接密码
|
||||
*/
|
||||
private String password;
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.tenant.model.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import top.continew.admin.common.base.model.entity.BaseDO;
|
||||
|
||||
/**
|
||||
* 租户套餐实体
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_tenant_package")
|
||||
public class TenantPackageDO extends BaseDO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 套餐名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 关联的菜单ids
|
||||
*/
|
||||
private String menuIds;
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.tenant.model.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import top.continew.starter.core.exception.BusinessException;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TenantConnectTypeEnum {
|
||||
|
||||
MYSQL;
|
||||
|
||||
public static TenantConnectTypeEnum getByOrdinal(Integer ordinal) {
|
||||
for (TenantConnectTypeEnum item : TenantConnectTypeEnum.values()) {
|
||||
if (item.ordinal() == ordinal) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
throw new BusinessException("未知的连接类型");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.tenant.model.query;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.starter.data.annotation.Query;
|
||||
import top.continew.starter.data.enums.QueryType;
|
||||
|
||||
/**
|
||||
* 租户数据连接查询条件
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "租户数据连接查询条件")
|
||||
public class TenantDbConnectQuery implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 连接名称
|
||||
*/
|
||||
@Schema(description = "连接名称")
|
||||
@Query(type = QueryType.EQ)
|
||||
private String connectName;
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.tenant.model.query;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import top.continew.starter.data.annotation.Query;
|
||||
import top.continew.starter.data.enums.QueryType;
|
||||
|
||||
/**
|
||||
* 租户套餐查询条件
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "租户套餐查询条件")
|
||||
public class TenantPackageQuery implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 套餐名称
|
||||
*/
|
||||
@Schema(description = "套餐名称")
|
||||
@Query(type = QueryType.EQ)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1:启用;2:禁用)")
|
||||
@Query(type = QueryType.EQ)
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.tenant.model.query;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.starter.data.annotation.Query;
|
||||
import top.continew.starter.data.enums.QueryType;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 租户查询条件
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "租户查询条件")
|
||||
public class TenantQuery implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户名称
|
||||
*/
|
||||
@Schema(description = "租户名称")
|
||||
@Query(type = QueryType.LIKE)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 租户套餐编号
|
||||
*/
|
||||
@Schema(description = "租户套餐编号")
|
||||
@Query(type = QueryType.EQ)
|
||||
private Long packageId;
|
||||
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.tenant.model.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 创建或修改租户数据连接参数
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建或修改租户数据连接参数")
|
||||
public class TenantDbConnectReq implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 连接名称
|
||||
*/
|
||||
@Schema(description = "连接名称")
|
||||
@NotBlank(message = "连接名称不能为空")
|
||||
@Length(max = 128, message = "连接名称长度不能超过 {max} 个字符")
|
||||
private String connectName;
|
||||
|
||||
/**
|
||||
* 连接类型
|
||||
*/
|
||||
@Schema(description = "连接类型")
|
||||
@NotNull(message = "连接类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 连接主机地址
|
||||
*/
|
||||
@Schema(description = "连接主机地址")
|
||||
@NotBlank(message = "连接主机地址不能为空")
|
||||
@Length(max = 128, message = "连接主机地址长度不能超过 {max} 个字符")
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* 连接端口
|
||||
*/
|
||||
@Schema(description = "连接端口")
|
||||
@NotNull(message = "连接端口不能为空")
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 连接用户名
|
||||
*/
|
||||
@Schema(description = "连接用户名")
|
||||
@NotBlank(message = "连接用户名不能为空")
|
||||
@Length(max = 128, message = "连接用户名长度不能超过 {max} 个字符")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 连接密码
|
||||
*/
|
||||
@Schema(description = "连接密码")
|
||||
@NotBlank(message = "连接密码不能为空")
|
||||
@Length(max = 128, message = "连接密码长度不能超过 {max} 个字符")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private Long id;
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.tenant.model.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @description: 租户登录用户信息
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-02 20:41
|
||||
*/
|
||||
@Data
|
||||
public class TenantLoginUserInfoReq implements Serializable {
|
||||
|
||||
/**
|
||||
* 租户id
|
||||
*/
|
||||
@NotNull(message = "租户ID不能为空")
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 登录用户名
|
||||
*/
|
||||
@NotEmpty(message = "登录用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 登录密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private Long id;
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.tenant.model.req;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.*;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 创建或修改租户套餐参数
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建或修改租户套餐参数")
|
||||
public class TenantPackageReq implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 套餐名称
|
||||
*/
|
||||
@Schema(description = "套餐名称")
|
||||
@NotBlank(message = "套餐名称不能为空")
|
||||
@Length(max = 64, message = "套餐名称长度不能超过 {max} 个字符")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 关联的菜单ids
|
||||
*/
|
||||
@Schema(description = "关联的菜单ids")
|
||||
private Long[] menuIds;
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "菜单选择是否父子节点关联")
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "状态")
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private Long id;
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.tenant.model.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Future;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import top.continew.admin.common.constant.RegexConstants;
|
||||
import top.continew.starter.extension.crud.validation.CrudValidationGroup;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 创建或修改租户参数
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建或修改租户参数")
|
||||
public class TenantReq implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户名称
|
||||
*/
|
||||
@Schema(description = "租户名称")
|
||||
@NotBlank(message = "租户名称不能为空")
|
||||
@Length(max = 64, message = "租户名称长度不能超过 {max} 个字符")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 绑定的域名
|
||||
*/
|
||||
@Schema(description = "绑定的域名")
|
||||
@Length(max = 128, message = "绑定的域名长度不能超过 {max} 个字符")
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 租户套餐编号
|
||||
*/
|
||||
@Schema(description = "租户套餐编号")
|
||||
@NotNull(message = "租户套餐编号不能为空")
|
||||
private Long packageId;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
@Schema(description = "状态")
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 租户过期时间
|
||||
*/
|
||||
@Schema(description = "租户过期时间")
|
||||
@Future(message = "过期时间必须是未来时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@Schema(description = "用户名", example = "zhangsan")
|
||||
@NotBlank(message = "用户名不能为空", groups = CrudValidationGroup.Create.class)
|
||||
@Pattern(regexp = RegexConstants.USERNAME, message = "用户名长度为 4-64 个字符,支持大小写字母、数字、下划线,以字母开头")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码(加密)
|
||||
*/
|
||||
@Schema(description = "密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==")
|
||||
@NotBlank(message = "密码不能为空", groups = CrudValidationGroup.Create.class)
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private String tenantSn;
|
||||
|
||||
/**
|
||||
* 隔离级别
|
||||
*/
|
||||
@Schema(description = "隔离级别")
|
||||
@NotNull(message = "隔离级别不能为空", groups = CrudValidationGroup.Create.class)
|
||||
private Integer isolationLevel;
|
||||
|
||||
/**
|
||||
* 数据连接ID
|
||||
*/
|
||||
@Schema(description = "数据连接ID")
|
||||
private Long dbConnectId;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private Long id;
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TenantAvailableResp {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String domain;
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 租户通用信息返回
|
||||
* @author: 小熊
|
||||
* @create: 2024-11-28 09:53
|
||||
*/
|
||||
@Data
|
||||
public class TenantCommonResp {
|
||||
|
||||
/**
|
||||
* 是否开启了多租户
|
||||
*/
|
||||
private Boolean isEnabled;
|
||||
|
||||
/**
|
||||
* 可用租户列表
|
||||
*/
|
||||
private List<TenantAvailableResp> availableList;
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.base.model.resp.BaseDetailResp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 租户数据连接详情信息
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@Schema(description = "租户数据连接详情信息")
|
||||
public class TenantDbConnectDetailResp extends BaseDetailResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 连接名称
|
||||
*/
|
||||
@Schema(description = "连接名称")
|
||||
@ExcelProperty(value = "连接名称")
|
||||
private String connectName;
|
||||
|
||||
/**
|
||||
* 连接类型
|
||||
*/
|
||||
@Schema(description = "连接类型")
|
||||
@ExcelProperty(value = "连接类型")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 连接主机地址
|
||||
*/
|
||||
@Schema(description = "连接主机地址")
|
||||
@ExcelProperty(value = "连接主机地址")
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* 连接端口
|
||||
*/
|
||||
@Schema(description = "连接端口")
|
||||
@ExcelProperty(value = "连接端口")
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 连接用户名
|
||||
*/
|
||||
@Schema(description = "连接用户名")
|
||||
@ExcelProperty(value = "连接用户名")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 连接密码
|
||||
*/
|
||||
@Schema(description = "连接密码")
|
||||
@ExcelProperty(value = "连接密码")
|
||||
private String password;
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.base.model.resp.BaseResp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 租户数据连接信息
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "租户数据连接信息")
|
||||
public class TenantDbConnectResp extends BaseResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 连接名称
|
||||
*/
|
||||
@Schema(description = "连接名称")
|
||||
private String connectName;
|
||||
|
||||
/**
|
||||
* 连接类型
|
||||
*/
|
||||
@Schema(description = "连接类型")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 连接主机地址
|
||||
*/
|
||||
@Schema(description = "连接主机地址")
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* 连接端口
|
||||
*/
|
||||
@Schema(description = "连接端口")
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 连接用户名
|
||||
*/
|
||||
@Schema(description = "连接用户名")
|
||||
private String username;
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.base.model.resp.BaseDetailResp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户详情信息
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@Schema(description = "租户详情信息")
|
||||
public class TenantDetailResp extends BaseDetailResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户名称
|
||||
*/
|
||||
@Schema(description = "租户名称")
|
||||
@ExcelProperty(value = "租户名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 绑定的域名
|
||||
*/
|
||||
@Schema(description = "绑定的域名")
|
||||
@ExcelProperty(value = "绑定的域名")
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 租户套餐编号
|
||||
*/
|
||||
@Schema(description = "租户套餐编号")
|
||||
@ExcelProperty(value = "租户套餐编号")
|
||||
private Long packageId;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1:启用;2:禁用)")
|
||||
@ExcelProperty(value = "状态(1:启用;2:禁用)")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 租户过期时间
|
||||
*/
|
||||
@Schema(description = "租户过期时间")
|
||||
@ExcelProperty(value = "租户过期时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 绑定的套餐名称
|
||||
*/
|
||||
@Schema(description = "绑定的套餐名称")
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 套餐关联的菜单
|
||||
*/
|
||||
@Schema(description = "关联的菜单ids")
|
||||
private List<Long> menuIds;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private String tenantSn;
|
||||
|
||||
/**
|
||||
* 租户绑定的管理用户id
|
||||
*/
|
||||
private Long userId;
|
||||
}
|
@@ -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.tenant.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.*;
|
||||
import java.util.List;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.admin.common.base.model.resp.BaseDetailResp;
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
|
||||
/**
|
||||
* 租户套餐详情信息
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@Schema(description = "租户套餐详情信息")
|
||||
public class TenantPackageDetailResp extends BaseDetailResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 套餐名称
|
||||
*/
|
||||
@Schema(description = "套餐名称")
|
||||
@ExcelProperty(value = "套餐名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 关联的菜单ids
|
||||
*/
|
||||
@Schema(description = "关联的菜单ids")
|
||||
@ExcelProperty(value = "关联的菜单ids")
|
||||
private List<Long> menuIds;
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "菜单选择是否父子节点关联")
|
||||
@ExcelProperty(value = "菜单选择是否父子节点关联")
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1:启用;2:禁用)")
|
||||
@ExcelProperty(value = "状态(1:启用;2:禁用)")
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.*;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.admin.common.base.model.resp.BaseResp;
|
||||
|
||||
/**
|
||||
* 租户套餐信息
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "租户套餐信息")
|
||||
public class TenantPackageResp extends BaseResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 套餐名称
|
||||
*/
|
||||
@Schema(description = "套餐名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 关联的菜单ids
|
||||
*/
|
||||
@Schema(description = "关联的菜单ids")
|
||||
private List<Long> menuIds;
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "菜单选择是否父子节点关联")
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1:启用;2:禁用)")
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.tenant.model.resp;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.base.model.resp.BaseResp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 租户信息
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "租户信息")
|
||||
public class TenantResp extends BaseResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户名称
|
||||
*/
|
||||
@Schema(description = "租户名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 绑定的域名
|
||||
*/
|
||||
@Schema(description = "绑定的域名")
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 租户套餐编号
|
||||
*/
|
||||
@Schema(description = "租户套餐编号")
|
||||
private Long packageId;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;2:禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1:启用;2:禁用)")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 租户过期时间
|
||||
*/
|
||||
@Schema(description = "租户过期时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 绑定的套餐名称
|
||||
*/
|
||||
@Schema(description = "绑定的套餐名称")
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private String tenantSn;
|
||||
|
||||
/**
|
||||
* 隔离级别
|
||||
*/
|
||||
@Schema(description = "隔离级别")
|
||||
private Integer isolationLevel;
|
||||
|
||||
/**
|
||||
* 数据连接ID
|
||||
*/
|
||||
@Schema(description = "数据连接ID")
|
||||
private Long dbConnectId;
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.tenant.service;
|
||||
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import top.continew.admin.common.base.service.BaseService;
|
||||
import top.continew.admin.tenant.model.query.TenantDbConnectQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantDbConnectReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectResp;
|
||||
|
||||
/**
|
||||
* 租户数据连接业务接口
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
public interface TenantDbConnectService extends BaseService<TenantDbConnectResp, TenantDbConnectDetailResp, TenantDbConnectQuery, TenantDbConnectReq> {
|
||||
|
||||
JdbcTemplate getConnectJdbcTemplateById(Long id);
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.tenant.service;
|
||||
|
||||
import top.continew.admin.common.base.service.BaseService;
|
||||
import top.continew.admin.tenant.model.query.TenantPackageQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantPackageReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantPackageDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantPackageResp;
|
||||
|
||||
/**
|
||||
* 租户套餐业务接口
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
public interface TenantPackageService extends BaseService<TenantPackageResp, TenantPackageDetailResp, TenantPackageQuery, TenantPackageReq> {}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.tenant.service;
|
||||
|
||||
import top.continew.admin.common.base.service.BaseService;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.query.TenantQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantAvailableResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantResp;
|
||||
import top.continew.starter.data.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户业务接口
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
public interface TenantService extends BaseService<TenantResp, TenantDetailResp, TenantQuery, TenantReq>, IService<TenantDO> {
|
||||
|
||||
/**
|
||||
* 获取所有可用的租户列表
|
||||
*/
|
||||
List<TenantAvailableResp> getAvailableList();
|
||||
|
||||
/**
|
||||
* 租户绑定用户
|
||||
*/
|
||||
void bindUser(Long tenantId, Long userId);
|
||||
|
||||
/**
|
||||
* 检查租户状态
|
||||
*/
|
||||
void checkStatus();
|
||||
|
||||
/**
|
||||
* 根据id获取租户DO
|
||||
*/
|
||||
TenantDO getTenantById(Long id);
|
||||
|
||||
/**
|
||||
* 根据用户id获取租户信息
|
||||
*/
|
||||
TenantDO getTenantByUserId(Long userId);
|
||||
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.tenant.service.impl;
|
||||
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.continew.admin.common.base.service.BaseServiceImpl;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.tenant.mapper.TenantDbConnectMapper;
|
||||
import top.continew.admin.tenant.mapper.TenantMapper;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.entity.TenantDbConnectDO;
|
||||
import top.continew.admin.tenant.model.enums.TenantConnectTypeEnum;
|
||||
import top.continew.admin.tenant.model.query.TenantDbConnectQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantDbConnectReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectResp;
|
||||
import top.continew.admin.tenant.service.TenantDbConnectService;
|
||||
import top.continew.admin.tenant.util.DbConnectUtil;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.util.validation.CheckUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户数据连接业务实现
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/12/12 19:13
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TenantDbConnectServiceImpl extends BaseServiceImpl<TenantDbConnectMapper, TenantDbConnectDO, TenantDbConnectResp, TenantDbConnectDetailResp, TenantDbConnectQuery, TenantDbConnectReq> implements TenantDbConnectService {
|
||||
|
||||
private final TenantMapper tenantMapper;
|
||||
|
||||
@Override
|
||||
@Cached(name = CacheConstants.DB_CONNECT_KEY_PREFIX, key = "#id")
|
||||
public TenantDbConnectDetailResp get(Long id) {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeCreate(TenantDbConnectReq req) {
|
||||
TenantConnectTypeEnum connectTypeEnum = TenantConnectTypeEnum.getByOrdinal(req.getType());
|
||||
if (TenantConnectTypeEnum.MYSQL.equals(connectTypeEnum)) {
|
||||
DbConnectUtil.getMysqlDataSource(req.getHost(), req.getPort(), req.getUsername(), req
|
||||
.getPassword(), null, null);
|
||||
checkRepeat(req, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证重复数据
|
||||
*/
|
||||
private void checkRepeat(TenantDbConnectReq req, Long id) {
|
||||
CheckUtils.throwIf(baseMapper.exists(Wrappers.lambdaQuery(TenantDbConnectDO.class)
|
||||
.eq(TenantDbConnectDO::getHost, req.getHost())
|
||||
.eq(TenantDbConnectDO::getPort, req.getPort())
|
||||
.eq(TenantDbConnectDO::getUsername, req.getUsername())
|
||||
.ne(id != null, TenantDbConnectDO::getId, id)), "数据库连接已存在");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeUpdate(TenantDbConnectReq req, Long id) {
|
||||
TenantConnectTypeEnum connectTypeEnum = TenantConnectTypeEnum.getByOrdinal(req.getType());
|
||||
if (TenantConnectTypeEnum.MYSQL.equals(connectTypeEnum)) {
|
||||
DbConnectUtil.getMysqlDataSource(req.getHost(), req.getPort(), req.getUsername(), req
|
||||
.getPassword(), null, null);
|
||||
checkRepeat(req, id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeDelete(List<Long> ids) {
|
||||
CheckUtils.throwIf(tenantMapper.selectCount(Wrappers.lambdaQuery(TenantDO.class)
|
||||
.in(TenantDO::getDbConnectId, ids)) > 0, "存在关联租户无法删除");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterUpdate(TenantDbConnectReq req, TenantDbConnectDO entity) {
|
||||
RedisUtils.delete(CacheConstants.DB_CONNECT_KEY_PREFIX + entity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterDelete(List<Long> ids) {
|
||||
ids.forEach(id -> RedisUtils.delete(CacheConstants.DB_CONNECT_KEY_PREFIX + id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcTemplate getConnectJdbcTemplateById(Long id) {
|
||||
TenantDbConnectDetailResp dbConnectReq = get(id);
|
||||
TenantConnectTypeEnum connectTypeEnum = TenantConnectTypeEnum.getByOrdinal(dbConnectReq.getType());
|
||||
if (TenantConnectTypeEnum.MYSQL.equals(connectTypeEnum)) {
|
||||
DataSource dataSource = DbConnectUtil.getMysqlDataSource(dbConnectReq.getHost(), dbConnectReq
|
||||
.getPort(), dbConnectReq.getUsername(), dbConnectReq.getPassword(), null, null);
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.tenant.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.continew.admin.common.base.service.BaseServiceImpl;
|
||||
import top.continew.admin.tenant.mapper.TenantMapper;
|
||||
import top.continew.admin.tenant.mapper.TenantPackageMapper;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.entity.TenantPackageDO;
|
||||
import top.continew.admin.tenant.model.query.TenantPackageQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantPackageReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantPackageDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantPackageResp;
|
||||
import top.continew.admin.tenant.service.TenantPackageService;
|
||||
import top.continew.starter.core.util.validation.CheckUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户套餐业务实现
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 11:25
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TenantPackageServiceImpl extends BaseServiceImpl<TenantPackageMapper, TenantPackageDO, TenantPackageResp, TenantPackageDetailResp, TenantPackageQuery, TenantPackageReq> implements TenantPackageService {
|
||||
|
||||
private final TenantMapper tenantMapper;
|
||||
|
||||
@Override
|
||||
public TenantPackageDetailResp get(Long id) {
|
||||
TenantPackageDO tenantPackageDO = getById(id);
|
||||
TenantPackageDetailResp packageDetailResp = BeanUtil
|
||||
.copyProperties(tenantPackageDO, TenantPackageDetailResp.class);
|
||||
packageDetailResp.setMenuIds(new JSONArray(tenantPackageDO.getMenuIds()).toList(Long.class));
|
||||
fill(packageDetailResp);
|
||||
return packageDetailResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeCreate(TenantPackageReq req) {
|
||||
CheckUtils.throwIf(baseMapper.selectCount(Wrappers.lambdaQuery(TenantPackageDO.class)
|
||||
.eq(TenantPackageDO::getName, req.getName())) > 0, "租户套餐名称不能重复");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeDelete(List<Long> ids) {
|
||||
CheckUtils.throwIf(tenantMapper.selectCount(Wrappers.lambdaQuery(TenantDO.class)
|
||||
.in(TenantDO::getPackageId, ids)) > 0, "存在关联租户无法删除");
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.tenant.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.resp.TenantDbConnectDetailResp;
|
||||
import top.continew.admin.tenant.service.TenantDbConnectService;
|
||||
import top.continew.admin.tenant.service.TenantService;
|
||||
import top.continew.admin.tenant.util.DbConnectUtil;
|
||||
import top.continew.starter.core.util.validation.CheckUtils;
|
||||
import top.continew.starter.extension.tenant.config.TenantDataSource;
|
||||
import top.continew.starter.extension.tenant.config.TenantProvider;
|
||||
import top.continew.starter.extension.tenant.context.TenantContext;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
|
||||
/**
|
||||
* @description: 租户数据源提供者实现
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-12 15:35
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TenantProviderImpl implements TenantProvider {
|
||||
|
||||
private final TenantService tenantService;
|
||||
private final TenantDbConnectService tenantDbConnectService;
|
||||
|
||||
@Override
|
||||
public TenantContext getByTenantId(String tenantId, boolean verify) {
|
||||
TenantContext context = new TenantContext();
|
||||
if (StrUtil.isNotEmpty(tenantId) && !SysConstants.DEFAULT_TENANT.equals(tenantId)) {
|
||||
Long longTenantId = Long.valueOf(tenantId);
|
||||
TenantDO tenantDO = tenantService.getTenantById(longTenantId);
|
||||
CheckUtils.throwIfNull(tenantDO, "租户[{}]不存在", tenantId);
|
||||
CheckUtils.throwIf(verify && DisEnableStatusEnum.DISABLE.getValue()
|
||||
.equals(tenantDO.getStatus()), "租户[{}]已被禁用", tenantId);
|
||||
context.setTenantId(longTenantId);
|
||||
TenantIsolationLevel isolationLevel = TenantIsolationLevel.DATASOURCE.ordinal() == tenantDO
|
||||
.getIsolationLevel() ? TenantIsolationLevel.DATASOURCE : TenantIsolationLevel.LINE;
|
||||
context.setIsolationLevel(isolationLevel);
|
||||
if (isolationLevel.equals(TenantIsolationLevel.DATASOURCE)) {
|
||||
TenantDbConnectDetailResp dbConnectReq = tenantDbConnectService.get(tenantDO.getDbConnectId());
|
||||
String dbName = SysConstants.TENANT_DB_PREFIX + tenantDO.getTenantSn();
|
||||
HikariConfig hikariConfig = DbConnectUtil.formatHikariConfig(dbConnectReq.getHost(), dbConnectReq
|
||||
.getPort(), dbConnectReq.getUsername(), dbConnectReq.getPassword(), dbName, DbConnectUtil
|
||||
.getDefaultMysqlConnectParameter());
|
||||
TenantDataSource source = new TenantDataSource();
|
||||
source.setPoolName(tenantId);
|
||||
source.setDriverClassName(hikariConfig.getDriverClassName());
|
||||
source.setUrl(hikariConfig.getJdbcUrl());
|
||||
source.setUsername(hikariConfig.getUsername());
|
||||
source.setPassword(hikariConfig.getPassword());
|
||||
context.setDataSource(source);
|
||||
}
|
||||
} else {
|
||||
context.setTenantId(Long.valueOf(SysConstants.DEFAULT_TENANT));
|
||||
context.setIsolationLevel(TenantIsolationLevel.LINE);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.tenant.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.continew.admin.common.base.service.BaseServiceImpl;
|
||||
import top.continew.admin.common.config.properties.TenantProperties;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.tenant.mapper.TenantMapper;
|
||||
import top.continew.admin.tenant.mapper.TenantPackageMapper;
|
||||
import top.continew.admin.tenant.model.entity.TenantDO;
|
||||
import top.continew.admin.tenant.model.entity.TenantPackageDO;
|
||||
import top.continew.admin.tenant.model.query.TenantQuery;
|
||||
import top.continew.admin.tenant.model.req.TenantReq;
|
||||
import top.continew.admin.tenant.model.resp.TenantAvailableResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantDetailResp;
|
||||
import top.continew.admin.tenant.model.resp.TenantResp;
|
||||
import top.continew.admin.tenant.service.TenantDbConnectService;
|
||||
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.core.util.validation.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.model.entity.BaseIdDO;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.tenant.context.TenantContextHolder;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户业务实现
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2024/11/26 17:20
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, TenantResp, TenantDetailResp, TenantQuery, TenantReq> implements TenantService {
|
||||
|
||||
private final TenantPackageMapper packageMapper;
|
||||
private final TenantProperties tenantProperties;
|
||||
private final TenantDbConnectService dbConnectService;
|
||||
|
||||
@Override
|
||||
protected void beforeCreate(TenantReq req) {
|
||||
//租户名称不能重复
|
||||
ValidationUtils.throwIf(baseMapper.exists(Wrappers.lambdaQuery(TenantDO.class)
|
||||
.eq(TenantDO::getName, req.getName())), "重复的租户名称");
|
||||
//录入随机的六位租户编号
|
||||
req.setTenantSn(generateTenantSn());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成六位随机不重复的编号
|
||||
*/
|
||||
private String generateTenantSn() {
|
||||
String tenantSn;
|
||||
do {
|
||||
tenantSn = RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER_LOWER, 6);
|
||||
} while (baseMapper.exists(Wrappers.lambdaQuery(TenantDO.class).eq(TenantDO::getTenantSn, tenantSn)));
|
||||
return tenantSn;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterCreate(TenantReq req, TenantDO entity) {
|
||||
//数据源级别的租户需要创建数据库
|
||||
if (entity.getIsolationLevel().equals(TenantIsolationLevel.DATASOURCE.ordinal())) {
|
||||
JdbcTemplate jdbcTemplate = dbConnectService.getConnectJdbcTemplateById(entity.getDbConnectId());
|
||||
String dbName = SysConstants.TENANT_DB_PREFIX + entity.getTenantSn();
|
||||
//建库
|
||||
jdbcTemplate.execute(StrUtil
|
||||
.format("CREATE DATABASE {} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;", dbName));
|
||||
jdbcTemplate.execute(StrUtil.format("USE {};", dbName));
|
||||
//建表
|
||||
Resource resource = new ClassPathResource("db/changelog/mysql/tenant_table.sql");
|
||||
String tableSql = resource.readUtf8Str();
|
||||
Arrays.stream(tableSql.split(";"))
|
||||
.map(String::trim)
|
||||
.filter(sql -> !sql.isEmpty())
|
||||
.forEach(jdbcTemplate::execute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TenantAvailableResp> getAvailableList() {
|
||||
List<TenantDO> tenantDOS = baseMapper.selectList(Wrappers.lambdaQuery(TenantDO.class)
|
||||
.select(TenantDO::getName, BaseIdDO::getId, TenantDO::getDomain)
|
||||
.eq(TenantDO::getStatus, DisEnableStatusEnum.ENABLE.getValue())
|
||||
.and(t -> t.isNull(TenantDO::getExpireTime).or().ge(TenantDO::getExpireTime, DateUtil.date())));
|
||||
return BeanUtil.copyToList(tenantDOS, TenantAvailableResp.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResp<TenantResp> page(TenantQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper queryWrapper = Wrappers.query(TenantQuery.class)
|
||||
.eq(query.getPackageId() != null, "package_id", query.getPackageId())
|
||||
.like(StrUtil.isNotEmpty(query.getName()), "sys_tenant.name", query.getName());
|
||||
this.sort(queryWrapper, pageQuery);
|
||||
IPage<TenantResp> list = baseMapper.listTenant(new Page<>(pageQuery.getPage(), pageQuery
|
||||
.getSize()), queryWrapper);
|
||||
PageResp<TenantResp> pageResp = PageResp.build(list, TenantResp.class);
|
||||
return pageResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantDetailResp get(Long id) {
|
||||
TenantDetailResp detailResp = new TenantDetailResp();
|
||||
TenantDO tenantDO = getById(id);
|
||||
if (tenantDO != null) {
|
||||
BeanUtil.copyProperties(tenantDO, detailResp);
|
||||
TenantPackageDO packageDO = packageMapper.selectById(tenantDO.getPackageId());
|
||||
if (packageDO != null) {
|
||||
detailResp.setPackageName(packageDO.getName());
|
||||
detailResp.setMenuIds(new JSONArray(packageDO.getMenuIds()).toList(Long.class));
|
||||
}
|
||||
}
|
||||
fill(detailResp);
|
||||
return detailResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindUser(Long tenantId, Long userId) {
|
||||
update(Wrappers.lambdaUpdate(TenantDO.class).set(TenantDO::getUserId, userId).eq(BaseIdDO::getId, tenantId));
|
||||
TenantDO entity = getById(tenantId);
|
||||
RedisUtils.set(CacheConstants.TENANT_KEY + tenantId, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkStatus() {
|
||||
if (tenantProperties.isEnabled()) {
|
||||
Long tenantId = TenantContextHolder.getTenantId();
|
||||
CheckUtils.throwIfNull(tenantId, "未选择租户");
|
||||
if (tenantId != 0) {
|
||||
TenantDO tenantDO = baseMapper.selectById(tenantId);
|
||||
CheckUtils.throwIfNull(tenantDO, "租户不存在");
|
||||
CheckUtils.throwIfNotEqual(DisEnableStatusEnum.ENABLE.getValue(), tenantDO.getStatus(), "此租户已被禁用");
|
||||
//租户过期
|
||||
CheckUtils.throwIf(tenantDO.getExpireTime() != null && tenantDO.getExpireTime()
|
||||
.isBefore(DateUtil.date().toLocalDateTime()), "租户已过期");
|
||||
//套餐状态
|
||||
TenantPackageDO packageDO = packageMapper.selectById(tenantDO.getPackageId());
|
||||
CheckUtils.throwIfNull(tenantDO, "套餐不存在");
|
||||
CheckUtils.throwIfNotEqual(DisEnableStatusEnum.ENABLE.getValue(), packageDO.getStatus(), "此租户套餐已被禁用");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cached(name = CacheConstants.TENANT_KEY, key = "#id")
|
||||
public TenantDO getTenantById(Long id) {
|
||||
return baseMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterUpdate(TenantReq req, TenantDO entity) {
|
||||
RedisUtils.set(CacheConstants.TENANT_KEY + entity.getId(), entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterDelete(List<Long> ids) {
|
||||
ids.forEach(id -> RedisUtils.delete(CacheConstants.TENANT_KEY + id));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantDO getTenantByUserId(Long userId) {
|
||||
return baseMapper.selectOne(Wrappers.lambdaQuery(TenantDO.class).eq(TenantDO::getUserId, userId));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.tenant.util;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import top.continew.starter.core.exception.BusinessException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description: 数据连接工具类
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-15 18:54
|
||||
*/
|
||||
public class DbConnectUtil {
|
||||
|
||||
/**
|
||||
* 格式化HikariConfig
|
||||
*/
|
||||
public static HikariConfig formatHikariConfig(String host,
|
||||
Integer port,
|
||||
String username,
|
||||
String password,
|
||||
String dbName,
|
||||
Map<String, String> parameter) {
|
||||
String activeProfile = SpringUtil.getActiveProfile();
|
||||
String jdbcUrl = StrUtil.format("jdbc:mysql://{}:{}", host, port);
|
||||
String driverClassName = "com.mysql.cj.jdbc.Driver";
|
||||
if ("dev".equals(activeProfile)) {
|
||||
jdbcUrl = StrUtil.format("jdbc:p6spy:mysql://{}:{}", host, port);
|
||||
driverClassName = "com.p6spy.engine.spy.P6SpyDriver";
|
||||
}
|
||||
if (StrUtil.isNotEmpty(dbName)) {
|
||||
jdbcUrl = StrUtil.format("{}/{}", jdbcUrl, dbName);
|
||||
}
|
||||
if (parameter != null) {
|
||||
jdbcUrl = StrUtil.format("{}?{}", jdbcUrl, MapUtil.join(parameter, "&", "="));
|
||||
}
|
||||
HikariConfig configuration = new HikariConfig();
|
||||
configuration.setJdbcUrl(jdbcUrl);
|
||||
configuration.setDriverClassName(driverClassName);
|
||||
configuration.setUsername(username);
|
||||
configuration.setPassword(password);
|
||||
configuration.setConnectionTimeout(3000L);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证mysql连接有效性并返回数据源
|
||||
*/
|
||||
public static DataSource getMysqlDataSource(String host,
|
||||
Integer port,
|
||||
String username,
|
||||
String password,
|
||||
String dbName,
|
||||
Map<String, String> parameter) {
|
||||
try {
|
||||
DataSource dataSource = new HikariDataSource(formatHikariConfig(host, port, username, password, dbName, parameter));
|
||||
Connection connection = dataSource.getConnection();
|
||||
connection.close();
|
||||
return dataSource;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("数据库连接失败,请检查基础配置信息");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的mysql连接参数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> getDefaultMysqlConnectParameter() {
|
||||
Map<String, String> parameter = new HashMap<>();
|
||||
parameter.put("serverTimezone", "Asia/Shanghai");
|
||||
parameter.put("useUnicode", "true");
|
||||
parameter.put("characterEncoding", "utf8");
|
||||
parameter.put("useSSL", "false");
|
||||
parameter.put("allowMultiQueries", "true");
|
||||
parameter.put("autoReconnect", "true");
|
||||
parameter.put("maxReconnects", "10");
|
||||
parameter.put("failOverReadOnly", "false");
|
||||
return parameter;
|
||||
}
|
||||
|
||||
}
|
@@ -19,6 +19,7 @@
|
||||
<module>continew-plugin-schedule</module>
|
||||
<module>continew-plugin-open</module>
|
||||
<module>continew-plugin-generator</module>
|
||||
<module>continew-plugin-tenant</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -64,6 +64,12 @@
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 多租户插件 -->
|
||||
<dependency>
|
||||
<groupId>top.continew.admin</groupId>
|
||||
<artifactId>continew-plugin-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
@@ -23,6 +23,7 @@ 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;
|
||||
@@ -41,6 +42,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.log.dao.LogDao;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
import top.continew.starter.log.model.LogRequest;
|
||||
@@ -85,7 +88,10 @@ public class LogDaoLocalImpl implements LogDao {
|
||||
logDO.setCreateTime(LocalDateTime.ofInstant(logRecord.getTimestamp(), ZoneId.systemDefault()));
|
||||
// 设置操作人
|
||||
this.setCreateUser(logDO, logRequest, logResponse);
|
||||
Long tenantId = TenantContextHolder.getTenantId();
|
||||
SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> {
|
||||
logMapper.insert(logDO);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.config.tenant;
|
||||
|
||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||
import org.aspectj.lang.annotation.After;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.extension.tenant.context.TenantContextHolder;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
|
||||
/**
|
||||
* @description: 主数据源切面
|
||||
* @author: 小熊
|
||||
* @create: 2025-01-15 16:02
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.TENANT, name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||
public class DataSourceSwitchAspect {
|
||||
|
||||
@Pointcut("execution(* top.continew.admin.tenant.mapper..*(..)) || " + "execution(* top.continew.admin.tenant.service..*(..)) || " + "execution(* top.continew.admin.system.mapper.ClientMapper.*(..)) || " + "execution(* top.continew.admin.system.service.ClientService.*(..)) || " + "execution(* top.continew.admin.system.mapper.DictMapper.*(..)) || " + "execution(* top.continew.admin.system.service.DictService.*(..)) || " + "execution(* top.continew.admin.system.mapper.DictItemMapper.*(..)) || " + "execution(* top.continew.admin.system.service.DictItemService.*(..)) || " + "execution(* top.continew.admin.system.mapper.OptionMapper.*(..)) || " + "execution(* top.continew.admin.system.service.OptionService.*(..)) || " + "execution(* top.continew.admin.system.mapper.StorageMapper.*(..)) || " + "execution(* top.continew.admin.system.service.StorageService.*(..))")
|
||||
public void MasterDataSourceMethods() {
|
||||
}
|
||||
|
||||
@Before("MasterDataSourceMethods()")
|
||||
public void switchToMasterDataSource() {
|
||||
if (TenantContextHolder.getIsolationLevel() == TenantIsolationLevel.DATASOURCE) {
|
||||
DynamicDataSourceContextHolder.push(SysConstants.DEFAULT_DATASOURCE);
|
||||
}
|
||||
}
|
||||
|
||||
@After("MasterDataSourceMethods()")
|
||||
public void clearDataSourceContext() {
|
||||
if (TenantContextHolder.getIsolationLevel() == TenantIsolationLevel.DATASOURCE) {
|
||||
DynamicDataSourceContextHolder.poll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -11,17 +11,23 @@ server:
|
||||
--- ### 数据源配置
|
||||
spring.datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
# 请务必提前创建好名为 continew_admin 的数据库,如果使用其他数据库名请注意同步修改 DB_NAME 配置
|
||||
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: true
|
||||
# 设置默认的数据源或者数据源组(默认:master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
# # PostgreSQL 配置
|
||||
# url: jdbc:p6spy:postgresql://${DB_HOST:127.0.0.1}:${DB_PORT:5432}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&stringtype=unspecified
|
||||
# username: ${DB_USER:postgres}
|
||||
# password: ${DB_PWD:123456}
|
||||
# driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
# Hikari 连接池配置
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
@@ -34,6 +40,8 @@ spring.datasource:
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
|
||||
|
||||
## Liquibase 配置
|
||||
spring.liquibase:
|
||||
# 是否启用
|
||||
@@ -168,7 +176,7 @@ captcha:
|
||||
expirationInMinutes: 5
|
||||
|
||||
--- ### 短信配置
|
||||
## 提示:配置文件方式和 [系统管理/系统配置/短信配置] 功能可任选其一方式使用,也可共同使用,但实际开发时建议选择一种,注释或删除另一方
|
||||
## 提示:配置文件方式和 [短信配置] 功能可任选其一方式使用,也可共同使用,但实际开发时建议选择一种,注释或删除另一方
|
||||
sms:
|
||||
http-log: true
|
||||
is-print: true
|
||||
@@ -184,7 +192,7 @@ sms:
|
||||
# sdk-app-id: 你的应用ID
|
||||
|
||||
--- ### 邮件配置
|
||||
## 提示:配置文件方式和 [系统管理/系统配置/邮件配置] 功能可任选其一方式使用,实际开发时请注释或删除另一方
|
||||
## 提示:配置文件方式和 [邮件配置] 功能可任选其一方式使用,实际开发时请注释或删除另一方
|
||||
#spring.mail:
|
||||
# # 根据需要更换
|
||||
# host: smtp.126.com
|
||||
|
@@ -13,17 +13,23 @@ server:
|
||||
--- ### 数据源配置
|
||||
spring.datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
# 请务必提前创建好名为 continew_admin 的数据库,如果使用其他数据库名请注意同步修改 DB_NAME 配置
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: false
|
||||
# 设置默认的数据源或者数据源组(默认:master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# # PostgreSQL 配置
|
||||
# url: jdbc:postgresql://${DB_HOST:127.0.0.1}:${DB_PORT:5432}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&stringtype=unspecified
|
||||
# username: ${DB_USER:postgres}
|
||||
# password: ${DB_PWD:123456}
|
||||
# driver-class-name: org.postgresql.Driver
|
||||
# Hikari 连接池配置
|
||||
type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
@@ -36,6 +42,7 @@ spring.datasource:
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
|
||||
## Liquibase 配置
|
||||
spring.liquibase:
|
||||
# 是否启用
|
||||
|
@@ -261,7 +261,6 @@ mybatis-plus:
|
||||
# 分页插件配置
|
||||
pagination:
|
||||
enabled: true
|
||||
db-type: MYSQL
|
||||
|
||||
--- ### CosId 配置
|
||||
cosid:
|
||||
@@ -299,3 +298,32 @@ auth:
|
||||
- /auth/logout
|
||||
- /system/user/password
|
||||
|
||||
#多租户配置
|
||||
continew-starter.tenant:
|
||||
enabled: true
|
||||
# 多租户忽略的表
|
||||
ignore-tables:
|
||||
- gen_config # 代码生成
|
||||
- gen_field_config
|
||||
- sys_dict # 字典表
|
||||
- sys_dict_item
|
||||
- sys_option #参数
|
||||
- sys_storage # 存储配置
|
||||
- sys_tenant # 租户
|
||||
- sys_tenant_package
|
||||
- sys_tenant_db_connect
|
||||
- sys_app #应用
|
||||
- sys_client #客户端管理
|
||||
- sys_sms_config
|
||||
- sys_sms_log
|
||||
#租户不能使用的菜单
|
||||
ignore-menus:
|
||||
- 1130 #字典管理
|
||||
- 1140
|
||||
- 1150 #系统配置
|
||||
- 2050 #短信日志
|
||||
- 3000 #任务调度
|
||||
- 9000 #代码生成
|
||||
- 7000 #能力开放
|
||||
- 7010 #应用管理
|
||||
- 10010 #租户管理
|
@@ -11,6 +11,8 @@ databaseChangeLog:
|
||||
file: db/changelog/mysql/plugin/plugin_open.sql
|
||||
- include:
|
||||
file: db/changelog/mysql/plugin/plugin_generator.sql
|
||||
- include:
|
||||
file: db/changelog/mysql/plugin/plugin_tenant.sql
|
||||
# PostgreSQL
|
||||
# - include:
|
||||
# file: db/changelog/postgresql/main_table.sql
|
||||
|
@@ -0,0 +1,145 @@
|
||||
-- liquibase formatted sql
|
||||
|
||||
-- changeset 小熊:1
|
||||
-- comment 初始化表结构
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_tenant
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_tenant`;
|
||||
CREATE TABLE `sys_tenant` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '租户名称',
|
||||
`tenant_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '租户编号',
|
||||
`user_id` bigint DEFAULT NULL COMMENT '租户对应的用户',
|
||||
`domain` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '绑定的域名',
|
||||
`package_id` bigint NOT NULL COMMENT '租户套餐编号',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态(1:启用;2:禁用)',
|
||||
`expire_time` datetime DEFAULT NULL COMMENT '租户过期时间',
|
||||
`create_user` bigint NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`isolation_level` tinyint unsigned NOT NULL COMMENT '隔离级别',
|
||||
`db_connect_id` bigint DEFAULT NULL COMMENT '数据连接ID',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='租户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_tenant_package
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_tenant_package`;
|
||||
CREATE TABLE `sys_tenant_package` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '套餐名称',
|
||||
`menu_ids` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '关联的菜单ids',
|
||||
`menu_check_strictly` bit(1) DEFAULT b'0' COMMENT '菜单选择是否父子节点关联',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态(1:启用;2:禁用)',
|
||||
`create_user` bigint NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='租户套餐表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_tenant_db_connect
|
||||
-- ----------------------------
|
||||
CREATE TABLE `sys_tenant_db_connect` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`connect_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '连接名称',
|
||||
`type` tinyint(1) NOT NULL COMMENT '连接类型',
|
||||
`host` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '连接主机地址',
|
||||
`port` smallint NOT NULL COMMENT '连接端口',
|
||||
`username` varchar(128) NOT NULL COMMENT '连接用户名',
|
||||
`password` varchar(128) NOT NULL COMMENT '连接密码',
|
||||
`create_user` bigint NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='租户数据库连接表';
|
||||
|
||||
|
||||
-- changeset 小熊:2
|
||||
-- comment 添加租户列和索引
|
||||
ALTER TABLE sys_app ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_app_tenant_id ON sys_app(tenant_id);
|
||||
ALTER TABLE sys_dept ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_dept_tenant_id ON sys_dept(tenant_id);
|
||||
ALTER TABLE sys_file ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_file_tenant_id ON sys_file(tenant_id);
|
||||
ALTER TABLE sys_log ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_log_tenant_id ON sys_log(tenant_id);
|
||||
ALTER TABLE sys_menu ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_menu_tenant_id ON sys_menu(tenant_id);
|
||||
ALTER TABLE sys_message ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_message_tenant_id ON sys_message(tenant_id);
|
||||
ALTER TABLE sys_message_log ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_message_log_tenant_id ON sys_message_log(tenant_id);
|
||||
ALTER TABLE sys_notice ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_notice_tenant_id ON sys_notice(tenant_id);
|
||||
ALTER TABLE sys_notice_log ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_notice_log_tenant_id ON sys_notice_log(tenant_id);
|
||||
ALTER TABLE sys_role ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_role_tenant_id ON sys_role(tenant_id);
|
||||
ALTER TABLE sys_user ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_user_tenant_id ON sys_user(tenant_id);
|
||||
ALTER TABLE sys_role_dept ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_role_dept_tenant_id ON sys_role_dept(tenant_id);
|
||||
ALTER TABLE sys_role_menu ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_role_menu_tenant_id ON sys_role_menu(tenant_id);
|
||||
ALTER TABLE sys_user_password_history ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_user_password_history_tenant_id ON sys_user_password_history(tenant_id);
|
||||
ALTER TABLE sys_user_role ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_user_role_tenant_id ON sys_user_role(tenant_id);
|
||||
ALTER TABLE sys_user_social ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_sys_user_social_tenant_id ON sys_user_social(tenant_id);
|
||||
|
||||
-- changeset 小熊:3
|
||||
-- comment 唯一索引变更
|
||||
ALTER TABLE sys_app DROP INDEX `uk_access_key`;
|
||||
ALTER TABLE sys_app ADD UNIQUE INDEX `uk_access_key` (`access_key`, `tenant_id`);
|
||||
ALTER TABLE sys_dept DROP INDEX `uk_name_parent_id`;
|
||||
ALTER TABLE sys_dept ADD UNIQUE INDEX `uk_name_parent_id` (`name`, `parent_id`, `tenant_id`);
|
||||
ALTER TABLE sys_menu DROP INDEX `uk_title_parent_id`;
|
||||
ALTER TABLE sys_menu ADD UNIQUE INDEX `uk_title_parent_id` (`title`, `parent_id`, `tenant_id`);
|
||||
ALTER TABLE sys_role DROP INDEX `uk_name`;
|
||||
ALTER TABLE sys_role ADD UNIQUE INDEX `uk_name` (`name`, `tenant_id`);
|
||||
ALTER TABLE sys_role DROP INDEX `uk_code`;
|
||||
ALTER TABLE sys_role ADD UNIQUE INDEX `uk_code` (`code`, `tenant_id`);
|
||||
ALTER TABLE sys_user DROP INDEX `uk_username`;
|
||||
ALTER TABLE sys_user ADD UNIQUE INDEX `uk_username` (`username`, `tenant_id`);
|
||||
ALTER TABLE sys_user DROP INDEX `uk_email`;
|
||||
ALTER TABLE sys_user ADD UNIQUE INDEX `uk_email` (`email`, `tenant_id`);
|
||||
ALTER TABLE sys_user DROP INDEX `uk_phone`;
|
||||
ALTER TABLE sys_user ADD UNIQUE INDEX `uk_phone` (`phone`, `tenant_id`);
|
||||
|
||||
-- changeset 小熊:4
|
||||
-- comment 菜单录入
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `title`, `parent_id`, `type`, `path`, `name`, `component`, `redirect`, `icon`, `is_external`, `is_cache`, `is_hidden`, `permission`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`, `tenant_id`)
|
||||
VALUES
|
||||
(10010, '租户管理', 0, 1, '/tenant', 'Tenant', 'Layout', '/tenant/user', 'user-group', b'0', b'0', b'0', NULL, 6, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10015, '租户套餐', 10010, 2, '/tenant/package', 'TenantPackage', 'tenant/package/index', NULL, 'menu', b'0', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10016, '列表', 10015, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:package:list', 1, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10017, '详情', 10015, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:package:detail', 2, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10018, '新增', 10015, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:package:add', 3, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10019, '修改', 10015, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:package:update', 4, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10020, '删除', 10015, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:package:delete', 5, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10022, '租户管理', 10010, 2, '/tenant/user', 'TenantUser', 'tenant/user/index', NULL, 'user-group', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10023, '列表', 10022, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:user:list', 1, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10024, '详情', 10022, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:user:detail', 2, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10025, '新增', 10022, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:user:add', 3, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10026, '修改', 10022, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:user:update', 4, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10027, '删除', 10022, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tenant:user:delete', 5, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10028, '账号更新', 10022, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'tenant:user:editLoginUserInfo', 6, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10040, '数据连接', 10010, 2, '/tenant/dbConnect', 'TenantDbConnect', 'tenant/tenantDbConnect/index', NULL, 'storage', b'0', b'0', b'0', NULL, 3, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10041, '列表', 10040, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'tenant:dbConnect:list', 1, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10042, '详情', 10040, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'tenant:dbConnect:detail', 2, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10043, '新增', 10040, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'tenant:dbConnect:add', 3, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10044, '修改', 10040, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'tenant:dbConnect:update', 4, 1, 1, NOW(), NULL, NULL, 0),
|
||||
(10045, '删除', 10040, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'tenant:dbConnect:delete', 5, 1, 1, NOW(), NULL, NULL, 0);
|
||||
|
||||
|
||||
|
@@ -0,0 +1,241 @@
|
||||
-- 数据源级别租户表结构
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_menu` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`title` varchar(30) NOT NULL COMMENT '标题',
|
||||
`parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '上级菜单ID',
|
||||
`type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:目录;2:菜单;3:按钮)',
|
||||
`path` varchar(255) DEFAULT NULL COMMENT '路由地址',
|
||||
`name` varchar(50) DEFAULT NULL COMMENT '组件名称',
|
||||
`component` varchar(255) DEFAULT NULL COMMENT '组件路径',
|
||||
`redirect` varchar(255) DEFAULT NULL COMMENT '重定向地址',
|
||||
`icon` varchar(50) DEFAULT NULL COMMENT '图标',
|
||||
`is_external` bit(1) DEFAULT b'0' COMMENT '是否外链',
|
||||
`is_cache` bit(1) DEFAULT b'0' COMMENT '是否缓存',
|
||||
`is_hidden` bit(1) DEFAULT b'0' COMMENT '是否隐藏',
|
||||
`permission` varchar(100) DEFAULT NULL COMMENT '权限标识',
|
||||
`sort` int NOT NULL DEFAULT 999 COMMENT '排序',
|
||||
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_title_parent_id`(`title`, `parent_id`),
|
||||
INDEX `idx_parent_id`(`parent_id`),
|
||||
INDEX `idx_create_user`(`create_user`),
|
||||
INDEX `idx_update_user`(`update_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_dept` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(30) NOT NULL COMMENT '名称',
|
||||
`parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '上级部门ID',
|
||||
`ancestors` varchar(512) NOT NULL DEFAULT '' COMMENT '祖级列表',
|
||||
`description` varchar(200) DEFAULT NULL COMMENT '描述',
|
||||
`sort` int NOT NULL DEFAULT 999 COMMENT '排序',
|
||||
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)',
|
||||
`is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_name_parent_id`(`name`, `parent_id`),
|
||||
INDEX `idx_parent_id`(`parent_id`),
|
||||
INDEX `idx_create_user`(`create_user`),
|
||||
INDEX `idx_update_user`(`update_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_role` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(30) NOT NULL COMMENT '名称',
|
||||
`code` varchar(30) NOT NULL COMMENT '编码',
|
||||
`data_scope` tinyint(1) NOT NULL DEFAULT 4 COMMENT '数据权限(1:全部数据权限;2:本部门及以下数据权限;3:本部门数据权限;4:仅本人数据权限;5:自定义数据权限)',
|
||||
`description` varchar(200) DEFAULT NULL COMMENT '描述',
|
||||
`sort` int NOT NULL DEFAULT 999 COMMENT '排序',
|
||||
`is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据',
|
||||
`menu_check_strictly` bit(1) DEFAULT b'0' COMMENT '菜单选择是否父子节点关联',
|
||||
`dept_check_strictly` bit(1) DEFAULT b'0' COMMENT '部门选择是否父子节点关联',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_name`(`name`),
|
||||
UNIQUE INDEX `uk_code`(`code`),
|
||||
INDEX `idx_create_user`(`create_user`),
|
||||
INDEX `idx_update_user`(`update_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_user` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(64) NOT NULL COMMENT '用户名',
|
||||
`nickname` varchar(30) NOT NULL COMMENT '昵称',
|
||||
`password` varchar(255) DEFAULT NULL COMMENT '密码',
|
||||
`gender` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '性别(0:未知;1:男;2:女)',
|
||||
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
|
||||
`phone` varchar(255) DEFAULT NULL COMMENT '手机号码',
|
||||
`avatar` longtext DEFAULT NULL COMMENT '头像',
|
||||
`description` varchar(200) DEFAULT NULL COMMENT '描述',
|
||||
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)',
|
||||
`is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据',
|
||||
`pwd_reset_time` datetime DEFAULT NULL COMMENT '最后一次修改密码时间',
|
||||
`dept_id` bigint(20) NOT NULL COMMENT '部门ID',
|
||||
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_username`(`username`),
|
||||
UNIQUE INDEX `uk_email`(`email`),
|
||||
UNIQUE INDEX `uk_phone`(`phone`),
|
||||
INDEX `idx_dept_id`(`dept_id`),
|
||||
INDEX `idx_create_user`(`create_user`),
|
||||
INDEX `idx_update_user`(`update_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_user_password_history` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
|
||||
`password` varchar(255) NOT NULL COMMENT '密码',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_user_id`(`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户历史密码表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_user_social` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`source` varchar(255) NOT NULL COMMENT '来源',
|
||||
`open_id` varchar(255) NOT NULL COMMENT '开放ID',
|
||||
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
|
||||
`meta_json` text DEFAULT NULL COMMENT '附加信息',
|
||||
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_source_open_id`(`source`, `open_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户社会化关联表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_user_role` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
|
||||
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_user_id_role_id`(`user_id`, `role_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和角色关联表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_role_menu` (
|
||||
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
|
||||
`menu_id` bigint(20) NOT NULL COMMENT '菜单ID',
|
||||
PRIMARY KEY (`role_id`, `menu_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和菜单关联表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_role_dept` (
|
||||
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
|
||||
`dept_id` bigint(20) NOT NULL COMMENT '部门ID',
|
||||
PRIMARY KEY (`role_id`, `dept_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和部门关联表';
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_log` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`trace_id` varchar(255) DEFAULT NULL COMMENT '链路ID',
|
||||
`description` varchar(255) NOT NULL COMMENT '日志描述',
|
||||
`module` varchar(100) NOT NULL COMMENT '所属模块',
|
||||
`request_url` varchar(512) NOT NULL COMMENT '请求URL',
|
||||
`request_method` varchar(10) NOT NULL COMMENT '请求方式',
|
||||
`request_headers` text DEFAULT NULL COMMENT '请求头',
|
||||
`request_body` text DEFAULT NULL COMMENT '请求体',
|
||||
`status_code` int NOT NULL COMMENT '状态码',
|
||||
`response_headers` text DEFAULT NULL COMMENT '响应头',
|
||||
`response_body` mediumtext DEFAULT NULL COMMENT '响应体',
|
||||
`time_taken` bigint(20) NOT NULL COMMENT '耗时(ms)',
|
||||
`ip` varchar(100) DEFAULT NULL COMMENT 'IP',
|
||||
`address` varchar(255) DEFAULT NULL COMMENT 'IP归属地',
|
||||
`browser` varchar(100) DEFAULT NULL COMMENT '浏览器',
|
||||
`os` varchar(100) DEFAULT NULL COMMENT '操作系统',
|
||||
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:成功;2:失败)',
|
||||
`error_msg` text DEFAULT NULL COMMENT '错误信息',
|
||||
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_module`(`module`),
|
||||
INDEX `idx_ip`(`ip`),
|
||||
INDEX `idx_address`(`address`),
|
||||
INDEX `idx_create_time`(`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统日志表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_message` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`title` varchar(50) NOT NULL COMMENT '标题',
|
||||
`content` text DEFAULT NULL COMMENT '内容',
|
||||
`type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:系统消息;2:安全消息)',
|
||||
`path` varchar(255) DEFAULT NULL COMMENT '跳转路径',
|
||||
`scope` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '通知范围(1:所有人;2:指定用户)',
|
||||
`users` json DEFAULT NULL COMMENT '通知用户',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_message_log` (
|
||||
`message_id` bigint(20) NOT NULL COMMENT '消息ID',
|
||||
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
|
||||
`read_time` datetime DEFAULT NULL COMMENT '读取时间',
|
||||
PRIMARY KEY (`message_id`, `user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息日志表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_notice` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`title` varchar(150) NOT NULL COMMENT '标题',
|
||||
`content` mediumtext NOT NULL COMMENT '内容',
|
||||
`type` varchar(30) NOT NULL COMMENT '分类',
|
||||
`notice_scope` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '通知范围(1:所有人;2:指定用户)',
|
||||
`notice_users` json DEFAULT NULL COMMENT '通知用户',
|
||||
`notice_methods` json DEFAULT NULL COMMENT '通知方式(1:系统消息;2:登录弹窗)',
|
||||
`is_timing` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否定时',
|
||||
`publish_time` datetime DEFAULT NULL COMMENT '发布时间',
|
||||
`is_top` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否置顶',
|
||||
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:草稿;2:待发布;3:已发布)',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_create_user`(`create_user`),
|
||||
INDEX `idx_update_user`(`update_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_notice_log` (
|
||||
`notice_id` bigint(20) NOT NULL COMMENT '公告ID',
|
||||
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
|
||||
`read_time` datetime DEFAULT NULL COMMENT '读取时间',
|
||||
PRIMARY KEY (`notice_id`, `user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告日志表';
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_file` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(255) NOT NULL COMMENT '名称',
|
||||
`original_name` varchar(255) NOT NULL COMMENT '原始名称',
|
||||
`size` bigint(20) DEFAULT NULL COMMENT '大小(字节)',
|
||||
`parent_path` varchar(512) NOT NULL DEFAULT '/' COMMENT '上级目录',
|
||||
`path` varchar(512) NOT NULL COMMENT '路径',
|
||||
`extension` varchar(32) DEFAULT NULL COMMENT '扩展名',
|
||||
`content_type` varchar(255) DEFAULT NULL COMMENT '内容类型',
|
||||
`type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)',
|
||||
`sha256` varchar(256) DEFAULT NULL COMMENT 'SHA256值',
|
||||
`metadata` text DEFAULT NULL COMMENT '元数据',
|
||||
`thumbnail_name` varchar(255) DEFAULT NULL COMMENT '缩略图名称',
|
||||
`thumbnail_size` bigint(20) DEFAULT NULL COMMENT '缩略图大小(字节)',
|
||||
`thumbnail_metadata` text DEFAULT NULL COMMENT '缩略图元数据',
|
||||
`storage_id` bigint(20) NOT NULL COMMENT '存储ID',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_type`(`type`),
|
||||
INDEX `idx_sha256`(`sha256`),
|
||||
INDEX `idx_storage_id`(`storage_id`),
|
||||
INDEX `idx_create_user`(`create_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件表';
|
@@ -19,6 +19,7 @@ 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;
|
||||
@@ -36,10 +37,13 @@ import top.continew.admin.system.service.DeptService;
|
||||
import top.continew.admin.system.service.OptionService;
|
||||
import top.continew.admin.system.service.RoleService;
|
||||
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.core.util.ServletUtils;
|
||||
import top.continew.starter.extension.tenant.TenantHandler;
|
||||
import top.continew.starter.extension.tenant.context.TenantContextHolder;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -90,10 +94,21 @@ public abstract class AbstractLoginHandler<T extends LoginReq> implements LoginH
|
||||
protected String authenticate(UserDO user, ClientResp client) {
|
||||
// 获取权限、角色、密码过期天数
|
||||
Long userId = user.getId();
|
||||
CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> roleService
|
||||
.listPermissionByUserId(userId), threadPoolTaskExecutor);
|
||||
CompletableFuture<Set<RoleContext>> roleFuture = CompletableFuture.supplyAsync(() -> roleService
|
||||
.listByUserId(userId), threadPoolTaskExecutor);
|
||||
Long tenantId = TenantContextHolder.getTenantId();
|
||||
CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
SpringUtil.getBean(TenantHandler.class).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, () -> {
|
||||
roles.addAll(roleService.listByUserId(userId));
|
||||
});
|
||||
return roles;
|
||||
}, threadPoolTaskExecutor);
|
||||
CompletableFuture<Integer> passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService
|
||||
.getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name()));
|
||||
CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture);
|
||||
@@ -108,6 +123,7 @@ public abstract class AbstractLoginHandler<T extends LoginReq> implements LoginH
|
||||
userContext.setClientType(client.getClientType());
|
||||
loginParameter.setExtra(CLIENT_ID, client.getClientId());
|
||||
userContext.setClientId(client.getClientId());
|
||||
userContext.setTenantId(tenantId);
|
||||
// 登录并缓存用户信息
|
||||
StpUtil.login(userContext.getId(), loginParameter.setExtraData(BeanUtil
|
||||
.beanToMap(new UserExtraContext(ServletUtils.getRequest()))));
|
||||
|
@@ -123,4 +123,5 @@ public class AuthServiceImpl implements AuthService {
|
||||
});
|
||||
return BeanUtil.copyToList(treeList, RouteResp.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -29,12 +29,14 @@ import org.springframework.stereotype.Service;
|
||||
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.admin.common.config.properties.TenantProperties;
|
||||
import top.continew.admin.common.context.UserContext;
|
||||
import top.continew.admin.common.context.UserContextHolder;
|
||||
import top.continew.admin.common.context.UserExtraContext;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.tenant.context.TenantContextHolder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
@@ -50,6 +52,8 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor
|
||||
public class OnlineUserServiceImpl implements OnlineUserService {
|
||||
|
||||
private final TenantProperties tenantProperties;
|
||||
|
||||
@Override
|
||||
@AutoOperate(type = OnlineUserResp.class, on = "list")
|
||||
public PageResp<OnlineUserResp> page(OnlineUserQuery query, PageQuery pageQuery) {
|
||||
@@ -88,6 +92,12 @@ public class OnlineUserServiceImpl implements OnlineUserService {
|
||||
.isMatchClientId(query.getClientId(), userContext)) {
|
||||
continue;
|
||||
}
|
||||
//租户数据过滤
|
||||
if (tenantProperties.isEnabled()) {
|
||||
if (!TenantContextHolder.getTenantId().equals(userContext.getTenantId())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
List<LocalDateTime> loginTimeList = query.getLoginTime();
|
||||
entry.getValue().parallelStream().forEach(token -> {
|
||||
UserExtraContext extraContext = UserContextHolder.getExtraContext(token);
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import top.continew.admin.system.model.entity.StorageDO;
|
||||
import top.continew.starter.data.mapper.BaseMapper;
|
||||
@@ -26,6 +28,7 @@ import top.continew.starter.data.mapper.BaseMapper;
|
||||
* @author Charles7c
|
||||
* @since 2023/12/26 22:09
|
||||
*/
|
||||
@DS(SysConstants.DEFAULT_DATASOURCE)
|
||||
@Mapper
|
||||
public interface StorageMapper extends BaseMapper<StorageDO> {
|
||||
}
|
@@ -108,4 +108,10 @@ public class UserDO extends BaseDO {
|
||||
* 部门 ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 租户 ID
|
||||
*/
|
||||
@TableField(select = false)
|
||||
private Long tenantId;
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ import top.continew.starter.data.enums.QueryType;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单查询条件
|
||||
@@ -56,4 +57,12 @@ public class MenuQuery implements Serializable {
|
||||
public MenuQuery(DisEnableStatusEnum status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除的菜单
|
||||
*/
|
||||
@Schema(description = "排除的菜单")
|
||||
@Query(columns = "id", type = QueryType.NOT_IN)
|
||||
private List<Long> excludeMenuIdList;
|
||||
|
||||
}
|
||||
|
@@ -56,4 +56,12 @@ public interface DeptService extends BaseService<DeptResp, DeptResp, DeptQuery,
|
||||
* @return 部门数量
|
||||
*/
|
||||
int countByNames(List<String> deptNames);
|
||||
|
||||
/**
|
||||
* 初始化租户部门
|
||||
*
|
||||
* @param deptName
|
||||
* @return 部门ID
|
||||
*/
|
||||
Long initTenantDept(String deptName);
|
||||
}
|
||||
|
@@ -34,6 +34,13 @@ import java.util.Set;
|
||||
*/
|
||||
public interface MenuService extends BaseService<MenuResp, MenuResp, MenuQuery, MenuReq>, IService<MenuDO> {
|
||||
|
||||
/**
|
||||
* 查询全部菜单
|
||||
*
|
||||
* @return 菜单列表
|
||||
*/
|
||||
List<MenuResp> listAll(Long tenantId);
|
||||
|
||||
/**
|
||||
* 根据用户 ID 查询
|
||||
*
|
||||
@@ -43,10 +50,35 @@ public interface MenuService extends BaseService<MenuResp, MenuResp, MenuQuery,
|
||||
Set<String> listPermissionByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 查询
|
||||
* 根据角色id查询
|
||||
*
|
||||
* @param roleId 角色 ID
|
||||
* @param roleId 角色id
|
||||
* @return 菜单列表
|
||||
*/
|
||||
List<MenuResp> listByRoleId(Long roleId);
|
||||
|
||||
/**
|
||||
* 递归初始化菜单
|
||||
*
|
||||
* @param menuList 需要初始化的菜单ID
|
||||
* @param oldParentId 原来的父级ID
|
||||
* @param newParentId 新的父级ID
|
||||
*/
|
||||
void menuInit(List<MenuDO> menuList, Long oldParentId, Long newParentId);
|
||||
|
||||
/**
|
||||
* 删除租户菜单
|
||||
*
|
||||
* @param menuList
|
||||
*/
|
||||
void deleteTenantMenus(List<MenuDO> menuList);
|
||||
|
||||
/**
|
||||
* 新增租户菜单
|
||||
*
|
||||
* @param menu 新增的菜单
|
||||
* @param pMenu 新增菜单的父级别
|
||||
*/
|
||||
void addTenantMenu(MenuDO menu, MenuDO pMenu);
|
||||
|
||||
}
|
||||
|
@@ -16,6 +16,9 @@
|
||||
|
||||
package top.continew.admin.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.continew.admin.system.model.entity.RoleMenuDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -24,7 +27,7 @@ import java.util.List;
|
||||
* @author Charles7c
|
||||
* @since 2023/2/19 10:40
|
||||
*/
|
||||
public interface RoleMenuService {
|
||||
public interface RoleMenuService extends IService<RoleMenuDO> {
|
||||
|
||||
/**
|
||||
* 新增
|
||||
|
@@ -100,4 +100,12 @@ public interface RoleService extends BaseService<RoleResp, RoleDetailResp, RoleQ
|
||||
* @return 角色数量
|
||||
*/
|
||||
int countByNames(List<String> roleNames);
|
||||
|
||||
/**
|
||||
* 初始化租户角色
|
||||
*
|
||||
* @return 角色ID
|
||||
*/
|
||||
Long initTenantRole();
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.system.service;
|
||||
|
||||
/**
|
||||
* @description: 多租户系统数据接口
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-02 20:08
|
||||
*/
|
||||
public interface TenantSysDataService {
|
||||
|
||||
/**
|
||||
* 清除所有系统数据
|
||||
*/
|
||||
void clear();
|
||||
|
||||
}
|
@@ -155,4 +155,13 @@ public interface UserService extends BaseService<UserResp, UserDetailResp, UserQ
|
||||
* @return 用户数量
|
||||
*/
|
||||
Long countByDeptIds(List<Long> deptIds);
|
||||
|
||||
/**
|
||||
* 初始化租户管理员
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
* @return 管理员id
|
||||
*/
|
||||
Long initTenantUser(String username, String password, Long deptId);
|
||||
}
|
||||
|
@@ -214,4 +214,25 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
}
|
||||
baseMapper.updateById(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化租户部门
|
||||
*
|
||||
* @param deptName
|
||||
* @return 部门ID
|
||||
*/
|
||||
@Override
|
||||
public Long initTenantDept(String deptName) {
|
||||
//部门添加
|
||||
DeptDO deptDO = new DeptDO();
|
||||
deptDO.setName(deptName);
|
||||
deptDO.setParentId(0l);
|
||||
deptDO.setAncestors("0");
|
||||
deptDO.setDescription("系统初始部门");
|
||||
deptDO.setSort(1);
|
||||
deptDO.setStatus(DisEnableStatusEnum.ENABLE);
|
||||
baseMapper.insert(deptDO);
|
||||
return deptDO.getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ package top.continew.admin.system.service.impl;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -28,15 +29,20 @@ import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.system.enums.MenuTypeEnum;
|
||||
import top.continew.admin.system.mapper.MenuMapper;
|
||||
import top.continew.admin.system.mapper.RoleMapper;
|
||||
import top.continew.admin.system.model.entity.MenuDO;
|
||||
import top.continew.admin.system.model.entity.RoleDO;
|
||||
import top.continew.admin.system.model.entity.RoleMenuDO;
|
||||
import top.continew.admin.system.model.query.MenuQuery;
|
||||
import top.continew.admin.system.model.req.MenuReq;
|
||||
import top.continew.admin.system.model.resp.MenuResp;
|
||||
import top.continew.admin.system.service.MenuService;
|
||||
import top.continew.admin.system.service.RoleMenuService;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.validation.CheckUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -50,6 +56,9 @@ import java.util.Set;
|
||||
@RequiredArgsConstructor
|
||||
public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuResp, MenuResp, MenuQuery, MenuReq> implements MenuService {
|
||||
|
||||
private final RoleMenuService roleMenuService;
|
||||
private final RoleMapper roleMapper;
|
||||
|
||||
@Override
|
||||
public Long create(MenuReq req) {
|
||||
String title = req.getTitle();
|
||||
@@ -90,12 +99,17 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
|
||||
RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cached(key = "'ALL' + #tenantId", name = CacheConstants.ROLE_MENU_KEY_PREFIX)
|
||||
public List<MenuResp> listAll(Long tenantId) {
|
||||
return super.list(new MenuQuery(DisEnableStatusEnum.ENABLE), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> listPermissionByUserId(Long userId) {
|
||||
return baseMapper.selectPermissionByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cached(key = "#roleId", name = CacheConstants.ROLE_MENU_KEY_PREFIX)
|
||||
public List<MenuResp> listByRoleId(Long roleId) {
|
||||
if (SysConstants.SUPER_ROLE_ID.equals(roleId)) {
|
||||
@@ -107,6 +121,63 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void menuInit(List<MenuDO> menuList, Long oldParentId, Long newParentId) {
|
||||
List<MenuDO> children = menuList.stream().filter(menuDO -> menuDO.getParentId().equals(oldParentId)).toList();
|
||||
for (MenuDO menuDO : children) {
|
||||
Long oldId = menuDO.getId();
|
||||
menuDO.setId(null);
|
||||
menuDO.setParentId(newParentId);
|
||||
save(menuDO);
|
||||
menuInit(menuList, oldId, menuDO.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTenantMenus(List<MenuDO> menuList) {
|
||||
if (!menuList.isEmpty()) {
|
||||
List<Long> delIds = new ArrayList<>();
|
||||
for (MenuDO menuDO : menuList) {
|
||||
MenuDO tMenu = getOne(Wrappers.query(MenuDO.class)
|
||||
.eq(menuDO.getType().equals(MenuTypeEnum.BUTTON.getValue()), "CONCAT(title,permission)", menuDO
|
||||
.getTitle() + menuDO.getPermission())
|
||||
.eq(!menuDO.getType().equals(MenuTypeEnum.BUTTON.getValue()), "name", menuDO.getName()));
|
||||
if (tMenu != null) {
|
||||
delIds.add(tMenu.getId());
|
||||
}
|
||||
}
|
||||
if (!delIds.isEmpty()) {
|
||||
//菜单删除
|
||||
delete(delIds);
|
||||
//绑定关系删除
|
||||
roleMenuService.remove(Wrappers.lambdaQuery(RoleMenuDO.class).in(RoleMenuDO::getMenuId, delIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTenantMenu(MenuDO menu, MenuDO pMenu) {
|
||||
Long pId = 0l;
|
||||
if (pMenu != null) {
|
||||
MenuDO tPMenu = getOne(Wrappers.query(MenuDO.class)
|
||||
.eq(pMenu.getType().equals(MenuTypeEnum.BUTTON.getValue()), "CONCAT(title,permission)", pMenu
|
||||
.getTitle() + pMenu.getPermission())
|
||||
.eq(!pMenu.getType().equals(MenuTypeEnum.BUTTON.getValue()), "name", pMenu.getName()));
|
||||
pId = tPMenu.getId();
|
||||
}
|
||||
menu.setId(null);
|
||||
menu.setParentId(pId);
|
||||
//菜单新增
|
||||
save(menu);
|
||||
//管理员绑定菜单
|
||||
RoleDO roleDO = roleMapper.selectOne(Wrappers.lambdaQuery(RoleDO.class)
|
||||
.eq(RoleDO::getCode, SysConstants.TENANT_ADMIN_CODE));
|
||||
RoleMenuDO roleMenuDO = new RoleMenuDO();
|
||||
roleMenuDO.setRoleId(roleDO.getId());
|
||||
roleMenuDO.setMenuId(menu.getId());
|
||||
roleMenuService.save(roleMenuDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标题是否存在
|
||||
*
|
||||
|
@@ -23,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.system.mapper.RoleMenuMapper;
|
||||
import top.continew.admin.system.model.entity.RoleMenuDO;
|
||||
import top.continew.admin.system.service.RoleMenuService;
|
||||
import top.continew.starter.data.service.impl.ServiceImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -36,7 +37,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleMenuServiceImpl implements RoleMenuService {
|
||||
public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenuDO> implements RoleMenuService {
|
||||
|
||||
private final RoleMenuMapper baseMapper;
|
||||
|
||||
|
@@ -69,6 +69,8 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
|
||||
CheckUtils.throwIf(this.isNameExists(name, null), "新增失败,[{}] 已存在", name);
|
||||
String code = req.getCode();
|
||||
CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
|
||||
// 防止租户添加超管
|
||||
CheckUtils.throwIf(SysConstants.SUPER_ROLE_CODE.equals(code), "新增失败,[{}] 禁止使用", code);
|
||||
// 新增信息
|
||||
Long roleId = super.create(req);
|
||||
// 保存角色和部门关联
|
||||
@@ -247,4 +249,24 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化租户角色
|
||||
*
|
||||
* @return 角色ID
|
||||
*/
|
||||
@Override
|
||||
public Long initTenantRole() {
|
||||
RoleDO roleDO = new RoleDO();
|
||||
roleDO.setName("系统管理员");
|
||||
roleDO.setCode(SysConstants.TENANT_ADMIN_CODE);
|
||||
roleDO.setDataScope(DataScopeEnum.ALL);
|
||||
roleDO.setDescription("系统初始角色");
|
||||
roleDO.setSort(1);
|
||||
roleDO.setIsSystem(true);
|
||||
roleDO.setMenuCheckStrictly(false);
|
||||
roleDO.setDeptCheckStrictly(false);
|
||||
baseMapper.insert(roleDO);
|
||||
return roleDO.getId();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.system.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.system.mapper.*;
|
||||
import top.continew.admin.system.mapper.user.UserMapper;
|
||||
import top.continew.admin.system.mapper.user.UserPasswordHistoryMapper;
|
||||
import top.continew.admin.system.mapper.user.UserSocialMapper;
|
||||
import top.continew.admin.system.model.entity.user.UserDO;
|
||||
import top.continew.admin.system.service.FileService;
|
||||
import top.continew.admin.system.service.TenantSysDataService;
|
||||
import top.continew.starter.extension.crud.model.entity.BaseIdDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 多租户系统数据接口
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-02 20:12
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class TenantSysDataServiceImpl implements TenantSysDataService {
|
||||
|
||||
private final DeptMapper deptMapper;
|
||||
private final FileService fileService;
|
||||
private final LogMapper logMapper;
|
||||
private final MenuMapper menuMapper;
|
||||
private final MessageMapper messageMapper;
|
||||
private final MessageMapper messageUserMapper;
|
||||
private final NoticeMapper noticeMapper;
|
||||
private final RoleMapper roleMapper;
|
||||
private final RoleDeptMapper roleDeptMapper;
|
||||
private final RoleMenuMapper roleMenuMapper;
|
||||
private final UserMapper userMapper;
|
||||
private final UserPasswordHistoryMapper userPasswordHistoryMapper;
|
||||
private final UserRoleMapper userRoleMapper;
|
||||
private final UserSocialMapper userSocialMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void clear() {
|
||||
//所有用户退出
|
||||
List<UserDO> userDOS = userMapper.selectList(null);
|
||||
for (UserDO userDO : userDOS) {
|
||||
StpUtil.logout(userDO.getId());
|
||||
}
|
||||
Wrapper dw = Wrappers.query().eq("1", 1);
|
||||
//部门清除
|
||||
deptMapper.delete(dw);
|
||||
//文件清除
|
||||
List<Long> fileIds = fileService.list().stream().map(BaseIdDO::getId).toList();
|
||||
if (!fileIds.isEmpty()) {
|
||||
fileService.delete(fileIds);
|
||||
}
|
||||
//日志清除
|
||||
logMapper.delete(dw);
|
||||
//菜单清除
|
||||
menuMapper.delete(dw);
|
||||
//消息清除
|
||||
messageMapper.delete(dw);
|
||||
messageUserMapper.delete(dw);
|
||||
//通知清除
|
||||
noticeMapper.delete(dw);
|
||||
//角色相关数据清除
|
||||
roleMapper.delete(dw);
|
||||
roleDeptMapper.delete(dw);
|
||||
roleMenuMapper.delete(dw);
|
||||
//用户数据清除
|
||||
userMapper.delete(dw);
|
||||
userPasswordHistoryMapper.delete(dw);
|
||||
userRoleMapper.delete(dw);
|
||||
userSocialMapper.delete(dw);
|
||||
}
|
||||
|
||||
}
|
@@ -23,10 +23,7 @@ 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.CharsetUtil;
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.extra.validation.ValidationUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@@ -57,6 +54,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import top.continew.admin.auth.service.OnlineUserService;
|
||||
import top.continew.admin.common.base.service.BaseServiceImpl;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.common.constant.RegexConstants;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.context.UserContext;
|
||||
import top.continew.admin.common.context.UserContextHolder;
|
||||
@@ -80,8 +78,10 @@ import top.continew.admin.system.service.*;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.exception.BusinessException;
|
||||
import top.continew.starter.core.util.ExceptionUtils;
|
||||
import top.continew.starter.core.util.FileUploadUtils;
|
||||
import top.continew.starter.core.util.validation.CheckUtils;
|
||||
import top.continew.starter.core.util.validation.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
@@ -734,6 +734,26 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long initTenantUser(String username, String password, Long deptId) {
|
||||
//密码验证
|
||||
String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(password));
|
||||
ValidationUtils.throwIfNull(rawPassword, "密码解密失败");
|
||||
ValidationUtils.throwIf(!ReUtil
|
||||
.isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字");
|
||||
UserDO userDO = new UserDO();
|
||||
userDO.setUsername(username);
|
||||
userDO.setNickname("系统管理员");
|
||||
userDO.setPassword(rawPassword);
|
||||
userDO.setGender(GenderEnum.UNKNOWN);
|
||||
userDO.setDescription("系统初始用户");
|
||||
userDO.setStatus(DisEnableStatusEnum.ENABLE);
|
||||
userDO.setIsSystem(true);
|
||||
userDO.setDeptId(deptId);
|
||||
baseMapper.insert(userDO);
|
||||
return userDO.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 获取用户信息(数据权限)
|
||||
*
|
||||
|
7
pom.xml
7
pom.xml
@@ -76,6 +76,13 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 多租户插件 -->
|
||||
<dependency>
|
||||
<groupId>top.continew.admin</groupId>
|
||||
<artifactId>continew-plugin-tenant</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 代码生成器插件(后续会改造为独立插件) -->
|
||||
<dependency>
|
||||
<groupId>top.continew.admin</groupId>
|
||||
|
Reference in New Issue
Block a user