From ed6dd65a51a1c26af2c9d76407463b7f67c71fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=86=8A?= <50282105+xtanyu@users.noreply.github.com> Date: Thu, 10 Jul 2025 20:38:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(plugin/tenant):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=A4=9A=E7=A7=9F=E6=88=B7=E6=8F=92=E4=BB=B6=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- continew-common/pom.xml | 13 + .../config/properties/TenantProperties.java | 40 +++ .../admin/common/constant/CacheConstants.java | 10 + .../admin/common/constant/SysConstants.java | 25 ++ .../admin/common/context/UserContext.java | 8 +- .../common/context/UserContextHolder.java | 7 + .../admin/open/service/AppService.java | 1 + .../continew-plugin-tenant/pom.xml | 21 ++ .../tenant/controller/TenantController.java | 211 +++++++++++++++ .../controller/TenantDbConnectController.java | 39 +++ .../controller/TenantPackageController.java | 116 +++++++++ .../tenant/mapper/TenantDbConnectMapper.java | 28 ++ .../admin/tenant/mapper/TenantMapper.java | 44 ++++ .../tenant/mapper/TenantPackageMapper.java | 29 +++ .../admin/tenant/model/entity/TenantDO.java | 83 ++++++ .../model/entity/TenantDbConnectDO.java | 67 +++++ .../tenant/model/entity/TenantPackageDO.java | 58 +++++ .../model/enums/TenantConnectTypeEnum.java | 38 +++ .../model/query/TenantDbConnectQuery.java | 49 ++++ .../model/query/TenantPackageQuery.java | 55 ++++ .../admin/tenant/model/query/TenantQuery.java | 54 ++++ .../tenant/model/req/TenantDbConnectReq.java | 92 +++++++ .../model/req/TenantLoginUserInfoReq.java | 56 ++++ .../tenant/model/req/TenantPackageReq.java | 76 ++++++ .../admin/tenant/model/req/TenantReq.java | 120 +++++++++ .../model/resp/TenantAvailableResp.java | 30 +++ .../tenant/model/resp/TenantCommonResp.java | 41 +++ .../model/resp/TenantDbConnectDetailResp.java | 82 ++++++ .../model/resp/TenantDbConnectResp.java | 68 +++++ .../tenant/model/resp/TenantDetailResp.java | 99 +++++++ .../model/resp/TenantPackageDetailResp.java | 72 ++++++ .../tenant/model/resp/TenantPackageResp.java | 65 +++++ .../admin/tenant/model/resp/TenantResp.java | 91 +++++++ .../service/TenantDbConnectService.java | 36 +++ .../tenant/service/TenantPackageService.java | 31 +++ .../admin/tenant/service/TenantService.java | 63 +++++ .../impl/TenantDbConnectServiceImpl.java | 120 +++++++++ .../impl/TenantPackageServiceImpl.java | 71 ++++++ .../service/impl/TenantProviderImpl.java | 82 ++++++ .../service/impl/TenantServiceImpl.java | 202 +++++++++++++++ .../admin/tenant/util/DbConnectUtil.java | 106 ++++++++ continew-plugin/pom.xml | 1 + continew-server/pom.xml | 6 + .../admin/config/log/LogDaoLocalImpl.java | 8 +- .../config/tenant/DataSourceSwitchAspect.java | 59 +++++ .../main/resources/config/application-dev.yml | 58 +++-- .../resources/config/application-prod.yml | 53 ++-- .../src/main/resources/config/application.yml | 30 ++- .../db/changelog/db.changelog-master.yaml | 2 + .../changelog/mysql/plugin/plugin_tenant.sql | 145 +++++++++++ .../db/changelog/mysql/tenant_table.sql | 241 ++++++++++++++++++ .../admin/auth/AbstractLoginHandler.java | 26 +- .../auth/service/impl/AuthServiceImpl.java | 1 + .../service/impl/OnlineUserServiceImpl.java | 10 + .../admin/system/mapper/StorageMapper.java | 3 + .../system/model/entity/user/UserDO.java | 6 + .../admin/system/model/query/MenuQuery.java | 9 + .../admin/system/service/DeptService.java | 8 + .../admin/system/service/MenuService.java | 36 ++- .../admin/system/service/RoleMenuService.java | 5 +- .../admin/system/service/RoleService.java | 8 + .../system/service/TenantSysDataService.java | 31 +++ .../admin/system/service/UserService.java | 9 + .../system/service/impl/DeptServiceImpl.java | 21 ++ .../system/service/impl/MenuServiceImpl.java | 73 +++++- .../service/impl/RoleMenuServiceImpl.java | 3 +- .../system/service/impl/RoleServiceImpl.java | 22 ++ .../impl/TenantSysDataServiceImpl.java | 96 +++++++ .../system/service/impl/UserServiceImpl.java | 28 +- pom.xml | 7 + 70 files changed, 3539 insertions(+), 65 deletions(-) create mode 100644 continew-common/src/main/java/top/continew/admin/common/config/properties/TenantProperties.java create mode 100644 continew-plugin/continew-plugin-tenant/pom.xml create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantController.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantDbConnectController.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantPackageController.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantDbConnectMapper.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantMapper.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantPackageMapper.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDO.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDbConnectDO.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantPackageDO.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/enums/TenantConnectTypeEnum.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantDbConnectQuery.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantPackageQuery.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantQuery.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantDbConnectReq.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantLoginUserInfoReq.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantPackageReq.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantReq.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantAvailableResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantCommonResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectDetailResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDetailResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageDetailResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantResp.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantDbConnectService.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantPackageService.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantService.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantDbConnectServiceImpl.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantPackageServiceImpl.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantProviderImpl.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantServiceImpl.java create mode 100644 continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/util/DbConnectUtil.java create mode 100644 continew-server/src/main/java/top/continew/admin/config/tenant/DataSourceSwitchAspect.java create mode 100644 continew-server/src/main/resources/db/changelog/mysql/plugin/plugin_tenant.sql create mode 100644 continew-server/src/main/resources/db/changelog/mysql/tenant_table.sql create mode 100644 continew-system/src/main/java/top/continew/admin/system/service/TenantSysDataService.java create mode 100644 continew-system/src/main/java/top/continew/admin/system/service/impl/TenantSysDataServiceImpl.java diff --git a/continew-common/pom.xml b/continew-common/pom.xml index 2db3f20b..f029b85f 100644 --- a/continew-common/pom.xml +++ b/continew-common/pom.xml @@ -167,5 +167,18 @@ top.continew.starter continew-starter-extension-crud-mp + + + + top.continew.starter + continew-starter-extension-tenant-mp + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + + \ No newline at end of file diff --git a/continew-common/src/main/java/top/continew/admin/common/config/properties/TenantProperties.java b/continew-common/src/main/java/top/continew/admin/common/config/properties/TenantProperties.java new file mode 100644 index 00000000..a3d09e40 --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/config/properties/TenantProperties.java @@ -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 ignoreMenus; + +} diff --git a/continew-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java b/continew-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java index 66cd0533..020d19d0 100644 --- a/continew-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java +++ b/continew-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java @@ -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() { } } diff --git a/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java b/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java index 1e08d9c5..7f9bfdb2 100644 --- a/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java +++ b/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java @@ -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() { } } diff --git a/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java b/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java index 35223f4d..91280b9a 100644 --- a/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java +++ b/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java @@ -80,7 +80,7 @@ public class UserContext implements Serializable { */ private Set roles; - /** + /* * 客户端类型 */ private String clientType; @@ -90,6 +90,11 @@ public class UserContext implements Serializable { */ private String clientId; + /** + * 租户 ID + */ + private Long tenantId; + public UserContext(Set permissions, Set 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()); } + } diff --git a/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java b/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java index e727865c..019974b2 100644 --- a/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java +++ b/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java @@ -180,4 +180,11 @@ public class UserContextHolder { StpUtil.checkLogin(); return getContext().isAdmin(); } + + /** + * 获取租户ID + */ + public static Long getTenantId() { + return ExceptionUtils.exToNull(() -> getContext().getTenantId()); + } } diff --git a/continew-plugin/continew-plugin-open/src/main/java/top/continew/admin/open/service/AppService.java b/continew-plugin/continew-plugin-open/src/main/java/top/continew/admin/open/service/AppService.java index 469f10ef..59e0dcbc 100644 --- a/continew-plugin/continew-plugin-open/src/main/java/top/continew/admin/open/service/AppService.java +++ b/continew-plugin/continew-plugin-open/src/main/java/top/continew/admin/open/service/AppService.java @@ -55,4 +55,5 @@ public interface AppService extends BaseService + + 4.0.0 + + top.continew.admin + continew-plugin + ${revision} + + + continew-plugin-tenant + 多租户插件 + + + top.continew.admin + continew-system + + + + \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantController.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantController.java new file mode 100644 index 00000000..e116c277 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantController.java @@ -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 { + + 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 create(TenantReq req) { + //套餐菜单 + TenantPackageDetailResp detailResp = packageService.get(req.getPackageId()); + CheckUtils.throwIf(detailResp.getMenuIds().isEmpty(), "该套餐无可用菜单"); + List menuRespList = menuService.listByIds(detailResp.getMenuIds()); + //租户添加 + IdResp 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 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 dbConnectList() { + return dbConnectService.list(null, null); + } + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantDbConnectController.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantDbConnectController.java new file mode 100644 index 00000000..8caf9359 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantDbConnectController.java @@ -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 {} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantPackageController.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantPackageController.java new file mode 100644 index 00000000..2b1cdf0b --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/controller/TenantPackageController.java @@ -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 { + + private final MenuService menuService; + private final TenantProperties tenantProperties; + private final TenantService tenantService; + + @GetMapping("/menuTree") + @SaCheckPermission("tenant:package:get") + @Operation(summary = "获取租户套餐菜单", description = "获取租户套餐菜单") + public List> 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 tenantDOList = tenantService.list(Wrappers.lambdaQuery(TenantDO.class) + .eq(TenantDO::getPackageId, id)); + if (!tenantDOList.isEmpty()) { + TenantPackageDetailResp detail = baseService.get(id); + List oldMenuIds = detail.getMenuIds(); + List newMenuIds = Arrays.stream(req.getMenuIds()).toList(); + //删除的菜单 + List deleteMenuIds = new ArrayList<>(oldMenuIds); + deleteMenuIds.removeAll(newMenuIds); + //如果有删除的菜单则绑定了套餐的租户对应的菜单也会删除 + if (!deleteMenuIds.isEmpty()) { + List deleteMenus = menuService.listByIds(deleteMenuIds); + tenantDOList.forEach(tenantDO -> SpringUtil.getBean(TenantHandler.class) + .execute(tenantDO.getId(), () -> menuService.deleteTenantMenus(deleteMenus))); + } + //新增的菜单 + List addMenuIds = new ArrayList<>(newMenuIds); + addMenuIds.removeAll(oldMenuIds); + //如果有新增的菜单则绑定了套餐的租户对应的菜单也会新增 + if (!addMenuIds.isEmpty()) { + List 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); + } +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantDbConnectMapper.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantDbConnectMapper.java new file mode 100644 index 00000000..d7bd685e --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantDbConnectMapper.java @@ -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 {} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantMapper.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantMapper.java new file mode 100644 index 00000000..cc422f96 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantMapper.java @@ -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 { + + @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 listTenant(IPage page, @Param(Constants.WRAPPER) Wrapper wrapper); + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantPackageMapper.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantPackageMapper.java new file mode 100644 index 00000000..b42e6f50 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/mapper/TenantPackageMapper.java @@ -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 { +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDO.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDO.java new file mode 100644 index 00000000..f7576a17 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDO.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDbConnectDO.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDbConnectDO.java new file mode 100644 index 00000000..d5fbab31 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantDbConnectDO.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantPackageDO.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantPackageDO.java new file mode 100644 index 00000000..aa5bea91 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/entity/TenantPackageDO.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/enums/TenantConnectTypeEnum.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/enums/TenantConnectTypeEnum.java new file mode 100644 index 00000000..6be37cc0 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/enums/TenantConnectTypeEnum.java @@ -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("未知的连接类型"); + } + +} diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantDbConnectQuery.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantDbConnectQuery.java new file mode 100644 index 00000000..b89d0651 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantDbConnectQuery.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantPackageQuery.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantPackageQuery.java new file mode 100644 index 00000000..0c5b5121 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantPackageQuery.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantQuery.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantQuery.java new file mode 100644 index 00000000..98d5a652 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/query/TenantQuery.java @@ -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; + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantDbConnectReq.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantDbConnectReq.java new file mode 100644 index 00000000..40120d6f --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantDbConnectReq.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantLoginUserInfoReq.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantLoginUserInfoReq.java new file mode 100644 index 00000000..f58377f4 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantLoginUserInfoReq.java @@ -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; +} diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantPackageReq.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantPackageReq.java new file mode 100644 index 00000000..02713d75 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantPackageReq.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantReq.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantReq.java new file mode 100644 index 00000000..3159698b --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/req/TenantReq.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantAvailableResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantAvailableResp.java new file mode 100644 index 00000000..82f9010f --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantAvailableResp.java @@ -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; + +} diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantCommonResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantCommonResp.java new file mode 100644 index 00000000..083a8d56 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantCommonResp.java @@ -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 availableList; + +} diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectDetailResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectDetailResp.java new file mode 100644 index 00000000..219e0aa2 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectDetailResp.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectResp.java new file mode 100644 index 00000000..a7efd70f --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDbConnectResp.java @@ -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; + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDetailResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDetailResp.java new file mode 100644 index 00000000..7ce09059 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantDetailResp.java @@ -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 menuIds; + + /** + * 租户编号 + */ + private String tenantSn; + + /** + * 租户绑定的管理用户id + */ + private Long userId; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageDetailResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageDetailResp.java new file mode 100644 index 00000000..d928a390 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageDetailResp.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.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 menuIds; + + /** + * 菜单选择是否父子节点关联 + */ + @Schema(description = "菜单选择是否父子节点关联") + @ExcelProperty(value = "菜单选择是否父子节点关联") + private Boolean menuCheckStrictly; + + /** + * 状态(1:启用;2:禁用) + */ + @Schema(description = "状态(1:启用;2:禁用)") + @ExcelProperty(value = "状态(1:启用;2:禁用)") + private Integer status; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageResp.java new file mode 100644 index 00000000..846cebe8 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantPackageResp.java @@ -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 menuIds; + + /** + * 菜单选择是否父子节点关联 + */ + @Schema(description = "菜单选择是否父子节点关联") + private Boolean menuCheckStrictly; + + /** + * 状态(1:启用;2:禁用) + */ + @Schema(description = "状态(1:启用;2:禁用)") + private Integer status; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantResp.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantResp.java new file mode 100644 index 00000000..e51ca067 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/model/resp/TenantResp.java @@ -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; +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantDbConnectService.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantDbConnectService.java new file mode 100644 index 00000000..fb121d5a --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantDbConnectService.java @@ -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 { + + JdbcTemplate getConnectJdbcTemplateById(Long id); + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantPackageService.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantPackageService.java new file mode 100644 index 00000000..99e29145 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantPackageService.java @@ -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 {} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantService.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantService.java new file mode 100644 index 00000000..7eb058cb --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/TenantService.java @@ -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, IService { + + /** + * 获取所有可用的租户列表 + */ + List getAvailableList(); + + /** + * 租户绑定用户 + */ + void bindUser(Long tenantId, Long userId); + + /** + * 检查租户状态 + */ + void checkStatus(); + + /** + * 根据id获取租户DO + */ + TenantDO getTenantById(Long id); + + /** + * 根据用户id获取租户信息 + */ + TenantDO getTenantByUserId(Long userId); + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantDbConnectServiceImpl.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantDbConnectServiceImpl.java new file mode 100644 index 00000000..2962c156 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantDbConnectServiceImpl.java @@ -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 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 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 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; + } + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantPackageServiceImpl.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantPackageServiceImpl.java new file mode 100644 index 00000000..2b90155e --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantPackageServiceImpl.java @@ -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 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 ids) { + CheckUtils.throwIf(tenantMapper.selectCount(Wrappers.lambdaQuery(TenantDO.class) + .in(TenantDO::getPackageId, ids)) > 0, "存在关联租户无法删除"); + } +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantProviderImpl.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantProviderImpl.java new file mode 100644 index 00000000..e5bccde9 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantProviderImpl.java @@ -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; + } + +} diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantServiceImpl.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantServiceImpl.java new file mode 100644 index 00000000..64cb4fa8 --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/service/impl/TenantServiceImpl.java @@ -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 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 getAvailableList() { + List 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 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 list = baseMapper.listTenant(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + PageResp 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 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)); + } + +} \ No newline at end of file diff --git a/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/util/DbConnectUtil.java b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/util/DbConnectUtil.java new file mode 100644 index 00000000..e4254b7f --- /dev/null +++ b/continew-plugin/continew-plugin-tenant/src/main/java/top/continew/admin/tenant/util/DbConnectUtil.java @@ -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 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 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 getDefaultMysqlConnectParameter() { + Map 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; + } + +} diff --git a/continew-plugin/pom.xml b/continew-plugin/pom.xml index 092cf258..2ecaebe7 100644 --- a/continew-plugin/pom.xml +++ b/continew-plugin/pom.xml @@ -19,6 +19,7 @@ continew-plugin-schedule continew-plugin-open continew-plugin-generator + continew-plugin-tenant diff --git a/continew-server/pom.xml b/continew-server/pom.xml index 8ff192d1..32bc2959 100644 --- a/continew-server/pom.xml +++ b/continew-server/pom.xml @@ -64,6 +64,12 @@ liquibase-core + + + top.continew.admin + continew-plugin-tenant + + org.springframework.boot spring-boot-starter-test diff --git a/continew-server/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java b/continew-server/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java index 37fbdbc2..986344c1 100644 --- a/continew-server/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java +++ b/continew-server/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java @@ -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); - logMapper.insert(logDO); + Long tenantId = TenantContextHolder.getTenantId(); + SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> { + logMapper.insert(logDO); + }); } /** diff --git a/continew-server/src/main/java/top/continew/admin/config/tenant/DataSourceSwitchAspect.java b/continew-server/src/main/java/top/continew/admin/config/tenant/DataSourceSwitchAspect.java new file mode 100644 index 00000000..deecf743 --- /dev/null +++ b/continew-server/src/main/java/top/continew/admin/config/tenant/DataSourceSwitchAspect.java @@ -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(); + } + } + +} diff --git a/continew-server/src/main/resources/config/application-dev.yml b/continew-server/src/main/resources/config/application-dev.yml index e837da3e..a7e88c3d 100644 --- a/continew-server/src/main/resources/config/application-dev.yml +++ b/continew-server/src/main/resources/config/application-dev.yml @@ -11,29 +11,37 @@ 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 - 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 连接池配置 - hikari: - # 最大连接数量(默认 10,根据实际环境调整) - # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 - maximum-pool-size: 20 - # 获取连接超时时间(默认 30000 毫秒,30 秒) - connection-timeout: 30000 - # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) - idle-timeout: 600000 - # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) - keepaliveTime: 30000 - # 连接最大生存时间(默认 1800000 毫秒,30 分钟) - max-lifetime: 1800000 + ## 动态数据源配置(可配多主多从: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.mysql.cj.jdbc.Driver + type: ${spring.datasource.type} + # Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP) + hikari: + # 最大连接数量(默认 10,根据实际环境调整) + # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 + maximum-pool-size: 20 + # 获取连接超时时间(默认 30000 毫秒,30 秒) + connection-timeout: 30000 + # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) + idle-timeout: 600000 + # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) + 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 diff --git a/continew-server/src/main/resources/config/application-prod.yml b/continew-server/src/main/resources/config/application-prod.yml index 389ba631..94815ecf 100644 --- a/continew-server/src/main/resources/config/application-prod.yml +++ b/continew-server/src/main/resources/config/application-prod.yml @@ -13,29 +13,36 @@ 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 - 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 连接池配置 - hikari: - # 最大连接数量(默认 10,根据实际环境调整) - # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 - maximum-pool-size: 20 - # 获取连接超时时间(默认 30000 毫秒,30 秒) - connection-timeout: 30000 - # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) - idle-timeout: 600000 - # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) - keepaliveTime: 30000 - # 连接最大生存时间(默认 1800000 毫秒,30 分钟) - max-lifetime: 1800000 + ## 动态数据源配置(可配多主多从: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 + type: ${spring.datasource.type} + # Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP) + hikari: + # 最大连接数量(默认 10,根据实际环境调整) + # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 + maximum-pool-size: 20 + # 获取连接超时时间(默认 30000 毫秒,30 秒) + connection-timeout: 30000 + # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) + idle-timeout: 600000 + # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) + keepaliveTime: 30000 + # 连接最大生存时间(默认 1800000 毫秒,30 分钟) + max-lifetime: 1800000 + ## Liquibase 配置 spring.liquibase: # 是否启用 diff --git a/continew-server/src/main/resources/config/application.yml b/continew-server/src/main/resources/config/application.yml index ebbaba9e..4856d5b8 100644 --- a/continew-server/src/main/resources/config/application.yml +++ b/continew-server/src/main/resources/config/application.yml @@ -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 #租户管理 \ No newline at end of file diff --git a/continew-server/src/main/resources/db/changelog/db.changelog-master.yaml b/continew-server/src/main/resources/db/changelog/db.changelog-master.yaml index feefa21e..fe77c111 100644 --- a/continew-server/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/continew-server/src/main/resources/db/changelog/db.changelog-master.yaml @@ -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 diff --git a/continew-server/src/main/resources/db/changelog/mysql/plugin/plugin_tenant.sql b/continew-server/src/main/resources/db/changelog/mysql/plugin/plugin_tenant.sql new file mode 100644 index 00000000..9744d9f1 --- /dev/null +++ b/continew-server/src/main/resources/db/changelog/mysql/plugin/plugin_tenant.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); + + + diff --git a/continew-server/src/main/resources/db/changelog/mysql/tenant_table.sql b/continew-server/src/main/resources/db/changelog/mysql/tenant_table.sql new file mode 100644 index 00000000..0087b12e --- /dev/null +++ b/continew-server/src/main/resources/db/changelog/mysql/tenant_table.sql @@ -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='文件表'; diff --git a/continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java b/continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java index 86dd31ee..1df4a16a 100644 --- a/continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java +++ b/continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java @@ -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 implements LoginH protected String authenticate(UserDO user, ClientResp client) { // 获取权限、角色、密码过期天数 Long userId = user.getId(); - CompletableFuture> permissionFuture = CompletableFuture.supplyAsync(() -> roleService - .listPermissionByUserId(userId), threadPoolTaskExecutor); - CompletableFuture> roleFuture = CompletableFuture.supplyAsync(() -> roleService - .listByUserId(userId), threadPoolTaskExecutor); + Long tenantId = TenantContextHolder.getTenantId(); + CompletableFuture> permissionFuture = CompletableFuture.supplyAsync(() -> { + Set permissions = new HashSet<>(); + SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> { + permissions.addAll(roleService.listPermissionByUserId(userId)); + }); + return permissions; + }, threadPoolTaskExecutor); + CompletableFuture> roleFuture = CompletableFuture.supplyAsync(() -> { + Set roles = new HashSet<>(); + SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> { + roles.addAll(roleService.listByUserId(userId)); + }); + return roles; + }, threadPoolTaskExecutor); CompletableFuture passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture); @@ -108,6 +123,7 @@ public abstract class AbstractLoginHandler 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())))); diff --git a/continew-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java b/continew-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java index 87bd8f9d..e7fee1ed 100644 --- a/continew-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java @@ -123,4 +123,5 @@ public class AuthServiceImpl implements AuthService { }); return BeanUtil.copyToList(treeList, RouteResp.class); } + } diff --git a/continew-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java b/continew-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java index f313c0c0..ad69e700 100644 --- a/continew-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java @@ -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 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 loginTimeList = query.getLoginTime(); entry.getValue().parallelStream().forEach(token -> { UserExtraContext extraContext = UserContextHolder.getExtraContext(token); diff --git a/continew-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java b/continew-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java index e77de252..75da4f86 100644 --- a/continew-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java +++ b/continew-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java @@ -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 { } \ No newline at end of file diff --git a/continew-system/src/main/java/top/continew/admin/system/model/entity/user/UserDO.java b/continew-system/src/main/java/top/continew/admin/system/model/entity/user/UserDO.java index 88c4d0c2..92ccb92f 100644 --- a/continew-system/src/main/java/top/continew/admin/system/model/entity/user/UserDO.java +++ b/continew-system/src/main/java/top/continew/admin/system/model/entity/user/UserDO.java @@ -108,4 +108,10 @@ public class UserDO extends BaseDO { * 部门 ID */ private Long deptId; + + /** + * 租户 ID + */ + @TableField(select = false) + private Long tenantId; } diff --git a/continew-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java b/continew-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java index ff5691b9..4a8607f3 100644 --- a/continew-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java +++ b/continew-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java @@ -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 excludeMenuIdList; + } diff --git a/continew-system/src/main/java/top/continew/admin/system/service/DeptService.java b/continew-system/src/main/java/top/continew/admin/system/service/DeptService.java index 12ef2d14..1f763d05 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/DeptService.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/DeptService.java @@ -56,4 +56,12 @@ public interface DeptService extends BaseService deptNames); + + /** + * 初始化租户部门 + * + * @param deptName + * @return 部门ID + */ + Long initTenantDept(String deptName); } diff --git a/continew-system/src/main/java/top/continew/admin/system/service/MenuService.java b/continew-system/src/main/java/top/continew/admin/system/service/MenuService.java index 73c46ebb..2322a1c2 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/MenuService.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/MenuService.java @@ -34,6 +34,13 @@ import java.util.Set; */ public interface MenuService extends BaseService, IService { + /** + * 查询全部菜单 + * + * @return 菜单列表 + */ + List listAll(Long tenantId); + /** * 根据用户 ID 查询 * @@ -43,10 +50,35 @@ public interface MenuService extends BaseService listPermissionByUserId(Long userId); /** - * 根据角色 ID 查询 + * 根据角色id查询 * - * @param roleId 角色 ID + * @param roleId 角色id * @return 菜单列表 */ List listByRoleId(Long roleId); + + /** + * 递归初始化菜单 + * + * @param menuList 需要初始化的菜单ID + * @param oldParentId 原来的父级ID + * @param newParentId 新的父级ID + */ + void menuInit(List menuList, Long oldParentId, Long newParentId); + + /** + * 删除租户菜单 + * + * @param menuList + */ + void deleteTenantMenus(List menuList); + + /** + * 新增租户菜单 + * + * @param menu 新增的菜单 + * @param pMenu 新增菜单的父级别 + */ + void addTenantMenu(MenuDO menu, MenuDO pMenu); + } diff --git a/continew-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java b/continew-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java index 3f541b79..083ba5d4 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java @@ -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 { /** * 新增 diff --git a/continew-system/src/main/java/top/continew/admin/system/service/RoleService.java b/continew-system/src/main/java/top/continew/admin/system/service/RoleService.java index 5b9c2212..ca566926 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/RoleService.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/RoleService.java @@ -100,4 +100,12 @@ public interface RoleService extends BaseService roleNames); + + /** + * 初始化租户角色 + * + * @return 角色ID + */ + Long initTenantRole(); + } diff --git a/continew-system/src/main/java/top/continew/admin/system/service/TenantSysDataService.java b/continew-system/src/main/java/top/continew/admin/system/service/TenantSysDataService.java new file mode 100644 index 00000000..48016d5c --- /dev/null +++ b/continew-system/src/main/java/top/continew/admin/system/service/TenantSysDataService.java @@ -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(); + +} diff --git a/continew-system/src/main/java/top/continew/admin/system/service/UserService.java b/continew-system/src/main/java/top/continew/admin/system/service/UserService.java index 252d5548..0f0914a8 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/UserService.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/UserService.java @@ -155,4 +155,13 @@ public interface UserService extends BaseService deptIds); + + /** + * 初始化租户管理员 + * + * @param username + * @param password + * @return 管理员id + */ + Long initTenantUser(String username, String password, Long deptId); } diff --git a/continew-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java b/continew-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java index 52a8b503..492c055e 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java @@ -214,4 +214,25 @@ public class DeptServiceImpl extends BaseServiceImpl 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 listAll(Long tenantId) { + return super.list(new MenuQuery(DisEnableStatusEnum.ENABLE), null); + } + @Override public Set listPermissionByUserId(Long userId) { return baseMapper.selectPermissionByUserId(userId); } - @Override @Cached(key = "#roleId", name = CacheConstants.ROLE_MENU_KEY_PREFIX) public List listByRoleId(Long roleId) { if (SysConstants.SUPER_ROLE_ID.equals(roleId)) { @@ -107,6 +121,63 @@ public class MenuServiceImpl extends BaseServiceImpl menuList, Long oldParentId, Long newParentId) { + List 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 menuList) { + if (!menuList.isEmpty()) { + List 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); + } + /** * 标题是否存在 * diff --git a/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java b/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java index 0154ac89..b593b0e7 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java @@ -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 implements RoleMenuService { private final RoleMenuMapper baseMapper; diff --git a/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java b/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java index c9e89456..2052c342 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java @@ -69,6 +69,8 @@ public class RoleServiceImpl extends BaseServiceImpl userDOS = userMapper.selectList(null); + for (UserDO userDO : userDOS) { + StpUtil.logout(userDO.getId()); + } + Wrapper dw = Wrappers.query().eq("1", 1); + //部门清除 + deptMapper.delete(dw); + //文件清除 + List 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); + } + +} diff --git a/continew-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java b/continew-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java index 9bd3dceb..f6dddd7a 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java @@ -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 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 获取用户信息(数据权限) * diff --git a/pom.xml b/pom.xml index 922e7413..c6dd7695 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,13 @@ ${revision} + + + top.continew.admin + continew-plugin-tenant + 4.0.0-SNAPSHOT + + top.continew.admin