清理多租户

This commit is contained in:
2026-03-12 19:45:27 +08:00
parent f0649cb888
commit 8cef3227f3
40 changed files with 123 additions and 753 deletions

View File

@@ -100,20 +100,6 @@ public interface ErrorCodeConstants {
ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量");
ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁");
// ========== 租户信息 1-002-015-000 ==========
ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在");
ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用");
ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期");
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!");
ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在");
ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在");
// ========== 租户套餐 1-002-016-000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在");
ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除");
ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用");
ErrorCode TENANT_PACKAGE_NAME_DUPLICATE = new ErrorCode(1_002_016_003, "已经存在该名字的租户套餐");
// ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在");

View File

@@ -4,25 +4,14 @@ import com.njcn.rdms.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 角色标识枚举
*/
@Getter
@AllArgsConstructor
public enum RoleCodeEnum {
SUPER_ADMIN("super_admin", "超级管理员"),
TENANT_ADMIN("tenant_admin", "租户管理员"),
CRM_ADMIN("crm_admin", "CRM 管理员"); // CRM 系统专用
;
CRM_ADMIN("crm_admin", "CRM 管理员");
/**
* 角色编码
*/
private final String code;
/**
* 名字
*/
private final String name;
public static boolean isSuperAdmin(String code) {

View File

@@ -1,12 +1,11 @@
package com.njcn.rdms.module.system.api.oauth2;
import com.njcn.rdms.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.njcn.rdms.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.njcn.rdms.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.njcn.rdms.module.system.service.oauth2.OAuth2TokenService;
import jakarta.annotation.Resource;
@@ -15,7 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@RestController
@Validated
public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi {
@@ -30,7 +29,6 @@ public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi {
}
@Override
// 访问令牌校验时,无需传递租户编号;主要解决上传文件的场景,前端不会传递 tenant-id
public CommonResult<OAuth2AccessTokenCheckRespDTO> checkAccessToken(String accessToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(accessToken);
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenCheckRespDTO.class));

View File

@@ -1,7 +1,6 @@
### 请求 /login 接口 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
tag: Yunai.local
{
@@ -14,7 +13,6 @@ tag: Yunai.local
### 请求 /login 接口【加密 AES】 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
tag: Yunai.local
X-API-ENCRYPT: true
@@ -23,7 +21,6 @@ WvSX9MOrenyGfBhEM0g1/hHgq8ocktMZ9OwAJ6MOG5FUrzYF/rG5JF1eMptQM1wT73VgDS05l/37WeRt
### 请求 /login 接口【加密 RSA】 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
tag: Yunai.local
X-API-ENCRYPT: true
@@ -32,7 +29,6 @@ e7QZTork9ZV5CmgZvSd+cHZk3xdUxKtowLM02kOha+gxHK2H/daU8nVBYS3+bwuDRy5abf+Pz1QJJGVA
### 请求 /login 接口 => 成功(无验证码)
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
{
"username": "admin",
@@ -42,10 +38,8 @@ tenant-id: {{adminTenantId}}
### 请求 /get-permission-info 接口 => 成功
GET {{baseUrl}}/system/auth/get-permission-info
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### 请求 /list-menus 接口 => 成功
GET {{baseUrl}}/system/list-menus
Authorization: Bearer {{token}}
#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a
tenant-id: {{adminTenantId}}

View File

@@ -1,4 +1,3 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/dict-data/list-all-simple
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -1,7 +1,6 @@
### 请求 /system/file-config/create 接口 => 成功
### 请求 /system/file-config/create 接口 => 成功
POST {{baseUrl}}/system/file-config/create
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
@@ -21,7 +20,6 @@ Authorization: Bearer {{token}}
### 请求 /system/file-config/update 接口 => 成功
PUT {{baseUrl}}/system/file-config/update
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
@@ -41,6 +39,4 @@ Authorization: Bearer {{token}}
### 请求 /system/file-config/test 接口 => 成功
GET {{baseUrl}}/system/file-config/test?id=2
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}

View File

@@ -1,5 +1,3 @@
### 获得地区树
GET {{baseUrl}}/system/area/tree
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -1,4 +1,3 @@
### 请求 /system/operate-log/page 接口 => 成功
GET {{baseUrl}}/system/operate-log/page
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -2,7 +2,6 @@
POST {{baseUrl}}/system/oauth2-client/create
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
{
"id": "1",

View File

@@ -1,13 +1,11 @@
### 请求 /system/oauth2/authorize 接口 => 成功
GET {{baseUrl}}/system/oauth2/authorize?clientId=default
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/authorize + token 接口 => 成功
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true
@@ -15,7 +13,6 @@ response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=htt
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false
@@ -23,7 +20,6 @@ response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=http
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a
@@ -31,7 +27,6 @@ grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=password&username=admin&password=admin123&scope=user.read
@@ -39,7 +34,6 @@ grant_type=password&username=admin&password=admin123&scope=user.read
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=client_credentials&scope=user.read
@@ -47,16 +41,13 @@ grant_type=client_credentials&scope=user.read
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588
### 请求 /system/oauth2/token + DELETE 接口 => 成功
DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/check-token 接口 => 成功
POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}

View File

@@ -1,13 +1,11 @@
### 请求 /system/oauth2/user/get 接口 => 成功
GET {{baseUrl}}/system/oauth2/user/get
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/user/update 接口 => 成功
PUT {{baseUrl}}/system/oauth2/user/update
Content-Type: application/json
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
tenant-id: {{adminTenantId}}
{
"nickname": "灿能源码"

View File

@@ -8,33 +8,32 @@ import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 【开放接口】校验令牌 Response VO")
@Schema(description = "Admin API - open check token response")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenCheckTokenRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@Schema(description = "User id", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@JsonProperty("user_id")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@Schema(description = "User type", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@JsonProperty("user_type")
private Integer userType;
@Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@JsonProperty("tenant_id")
private Long tenantId;
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "car")
@Schema(description = "Client id", requiredMode = Schema.RequiredMode.REQUIRED, example = "car")
@JsonProperty("client_id")
private String clientId;
@Schema(description = "授权范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_info")
@Schema(description = "Scopes", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_info")
private List<String> scopes;
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
@Schema(description = "Access token", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
@JsonProperty("access_token")
private String accessToken;
@Schema(description = "过期时间,时间戳 / 1000即单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "1593092157")
@Schema(description = "Expire timestamp in seconds", requiredMode = Schema.RequiredMode.REQUIRED, example = "1593092157")
private Long exp;
}

View File

@@ -1,4 +1,3 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/menu/list
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -16,7 +16,14 @@ import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Comparator;
import java.util.List;
@@ -50,7 +57,7 @@ public class MenuController {
@DeleteMapping("/delete")
@Operation(summary = "删除菜单")
@Parameter(name = "id", description = "菜单编号", required= true, example = "1024")
@Parameter(name = "id", description = "菜单编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:menu:delete')")
public CommonResult<Boolean> deleteMenu(@RequestParam("id") Long id) {
menuService.deleteMenu(id);
@@ -76,11 +83,9 @@ public class MenuController {
}
@GetMapping({"/list-all-simple", "simple-list"})
@Operation(summary = "获取菜单精简信息列表",
description = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。在多租户的场景下,会只返回租户所在套餐有的菜单")
@Operation(summary = "获取菜单精简信息列表", description = "只包含已启用的菜单,用于【角色分配菜单】功能的选项")
public CommonResult<List<MenuSimpleRespVO>> getSimpleMenuList() {
List<MenuDO> list = menuService.getMenuListByTenant(
new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
List<MenuDO> list = menuService.getMenuList(new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
list = menuService.filterDisableMenus(list);
list.sort(Comparator.comparing(MenuDO::getSort));
return success(BeanUtils.toBean(list, MenuSimpleRespVO.class));

View File

@@ -2,7 +2,6 @@
POST {{baseUrl}}/system/role/create
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenantId}}
{
"name": "测试角色",
@@ -14,7 +13,6 @@ tenant-id: {{adminTenantId}}
POST {{baseUrl}}/system/role/update
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenantId}}
{
"id": 100,
@@ -26,7 +24,6 @@ tenant-id: {{adminTenantId}}
POST {{baseUrl}}/system/role/delete
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
roleId=14
@@ -34,9 +31,7 @@ roleId=14
GET {{baseUrl}}/system/role/get?id=100
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### /role/page 成功
GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -1,5 +1,4 @@
### 请求 /system/redis/get-monitor-info 接口 => 成功
GET {{baseUrl}}/system/redis/get-monitor-info
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -2,10 +2,7 @@
GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
#Authorization: Bearer test100
tenant-id: {{adminTenantId}}
### 请求 /system/user/page 接口(测试访问别的租户)
GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
visit-tenant-id: 122

View File

@@ -1,4 +1,3 @@
### 请求 /system/user/profile/get 接口 => 没有权限
GET {{baseUrl}}/system/user/profile/get
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -7,96 +7,26 @@ import com.njcn.rdms.module.system.dal.dataobject.permission.MenuDO;
import java.util.Collection;
import java.util.List;
/**
* 菜单 Service 接口
*
* @author hongawen
*/
public interface MenuService {
/**
* 创建菜单
*
* @param createReqVO 菜单信息
* @return 创建出来的菜单编号
*/
Long createMenu(MenuSaveVO createReqVO);
/**
* 更新菜单
*
* @param updateReqVO 菜单信息
*/
void updateMenu(MenuSaveVO updateReqVO);
/**
* 删除菜单
*
* @param id 菜单编号
*/
void deleteMenu(Long id);
/**
* 批量删除菜单
*
* @param ids 菜单编号数组
*/
void deleteMenuList(List<Long> ids);
/**
* 获得所有菜单列表
*
* @return 菜单列表
*/
List<MenuDO> getMenuList();
/**
* 基于租户,筛选菜单列表
* 注意,如果是系统租户,返回的还是全菜单
*
* @param reqVO 筛选条件请求 VO
* @return 菜单列表
*/
List<MenuDO> getMenuListByTenant(MenuListReqVO reqVO);
/**
* 过滤掉关闭的菜单及其子菜单
*
* @param list 菜单列表
* @return 过滤后的菜单列表
*/
List<MenuDO> filterDisableMenus(List<MenuDO> list);
/**
* 筛选菜单列表
*
* @param reqVO 筛选条件请求 VO
* @return 菜单列表
*/
List<MenuDO> getMenuList(MenuListReqVO reqVO);
/**
* 获得权限对应的菜单编号数组
*
* @param permission 权限标识
* @return 数组
*/
List<Long> getMenuIdListByPermissionFromCache(String permission);
/**
* 获得菜单
*
* @param id 菜单编号
* @return 菜单
*/
MenuDO getMenu(Long id);
/**
* 获得菜单数组
*
* @param ids 菜单编号数组
* @return 菜单数组
*/
List<MenuDO> getMenuList(Collection<Long> ids);
}

View File

@@ -125,13 +125,6 @@ public class MenuServiceImpl implements MenuService {
return menuMapper.selectList();
}
@Override
public List<MenuDO> getMenuListByTenant(MenuListReqVO reqVO) {
// 查询所有菜单,并过滤掉关闭的节点
List<MenuDO> menus = getMenuList(reqVO);
return menus;
}
@Override
public List<MenuDO> filterDisableMenus(List<MenuDO> menuList) {
if (CollUtil.isEmpty(menuList)){