houzhongyi
2024-07-11 e7c1260db32209a078a962aaa0ad5492c35774fb
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.system.service.tenant;
H 2
3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.ObjectUtil;
6 import cn.hutool.core.util.StrUtil;
7 import com.iailab.framework.common.enums.CommonStatusEnum;
8 import com.iailab.framework.common.pojo.PageResult;
9 import com.iailab.framework.common.util.collection.CollectionUtils;
10 import com.iailab.framework.common.util.date.DateUtils;
11 import com.iailab.framework.common.util.object.BeanUtils;
12 import com.iailab.framework.tenant.config.TenantProperties;
13 import com.iailab.framework.tenant.core.context.TenantContextHolder;
14 import com.iailab.framework.tenant.core.util.TenantUtils;
15 import com.iailab.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
16 import com.iailab.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
17 import com.iailab.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
18 import com.iailab.module.system.convert.tenant.TenantConvert;
19 import com.iailab.module.system.dal.dataobject.permission.MenuDO;
20 import com.iailab.module.system.dal.dataobject.permission.RoleDO;
21 import com.iailab.module.system.dal.dataobject.tenant.TenantDO;
22 import com.iailab.module.system.dal.dataobject.tenant.TenantPackageDO;
23 import com.iailab.module.system.dal.mysql.tenant.TenantMapper;
24 import com.iailab.module.system.enums.permission.RoleCodeEnum;
25 import com.iailab.module.system.enums.permission.RoleTypeEnum;
26 import com.iailab.module.system.service.permission.MenuService;
27 import com.iailab.module.system.service.permission.PermissionService;
28 import com.iailab.module.system.service.permission.RoleService;
29 import com.iailab.module.system.service.tenant.handler.TenantInfoHandler;
30 import com.iailab.module.system.service.tenant.handler.TenantMenuHandler;
31 import com.iailab.module.system.service.user.AdminUserService;
32 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
33 import lombok.extern.slf4j.Slf4j;
34 import org.springframework.beans.factory.annotation.Autowired;
35 import org.springframework.context.annotation.Lazy;
36 import org.springframework.stereotype.Service;
37 import org.springframework.validation.annotation.Validated;
38
39 import javax.annotation.Resource;
40 import java.util.List;
41 import java.util.Objects;
42 import java.util.Set;
43
44 import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
45 import static com.iailab.module.system.enums.ErrorCodeConstants.*;
46 import static java.util.Collections.singleton;
47
48 /**
49  * 租户 Service 实现类
50  *
51  * @author iailab
52  */
53 @Service
54 @Validated
55 @Slf4j
56 public class TenantServiceImpl implements TenantService {
57
58     @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
59     @Autowired(required = false) // 由于 iailab.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入
60     private TenantProperties tenantProperties;
61
62     @Resource
63     private TenantMapper tenantMapper;
64
65     @Resource
66     private TenantPackageService tenantPackageService;
67     @Resource
68     @Lazy // 延迟,避免循环依赖报错
69     private AdminUserService userService;
70     @Resource
71     private RoleService roleService;
72     @Resource
73     private MenuService menuService;
74     @Resource
75     private PermissionService permissionService;
76
77     @Override
78     public List<Long> getTenantIdList() {
79         List<TenantDO> tenants = tenantMapper.selectList();
80         return CollectionUtils.convertList(tenants, TenantDO::getId);
81     }
82
83     @Override
84     public void validTenant(Long id) {
85         TenantDO tenant = getTenant(id);
86         if (tenant == null) {
87             throw exception(TENANT_NOT_EXISTS);
88         }
89         if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) {
90             throw exception(TENANT_DISABLE, tenant.getName());
91         }
92         if (DateUtils.isExpired(tenant.getExpireTime())) {
93             throw exception(TENANT_EXPIRE, tenant.getName());
94         }
95     }
96
97     @Override
98     @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
99     public Long createTenant(TenantSaveReqVO createReqVO) {
100         // 校验租户名称是否重复
101         validTenantNameDuplicate(createReqVO.getName(), null);
102         // 校验租户域名是否重复
103         validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
104         // 校验套餐被禁用
105         TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
106
107         // 创建租户
108         TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class);
109         tenantMapper.insert(tenant);
110         // 创建租户的管理员
111         TenantUtils.execute(tenant.getId(), () -> {
112             // 创建角色
113             Long roleId = createRole(tenantPackage);
114             // 创建用户,并分配角色
115             Long userId = createUser(roleId, createReqVO);
116             // 修改租户的管理员
117             tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
118         });
119         return tenant.getId();
120     }
121
122     private Long createUser(Long roleId, TenantSaveReqVO createReqVO) {
123         // 创建用户
124         Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO));
125         // 分配角色
126         permissionService.assignUserRole(userId, singleton(roleId));
127         return userId;
128     }
129
130     private Long createRole(TenantPackageDO tenantPackage) {
131         // 创建角色
132         RoleSaveReqVO reqVO = new RoleSaveReqVO();
133         reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())
134                 .setSort(0).setRemark("系统自动生成");
135         Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
136         // 分配权限
137         permissionService.assignRoleMenu(roleId, tenantPackage.getMenuIds());
138         return roleId;
139     }
140
141     @Override
142     @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
143     public void updateTenant(TenantSaveReqVO updateReqVO) {
144         // 校验存在
145         TenantDO tenant = validateUpdateTenant(updateReqVO.getId());
146         // 校验租户名称是否重复
147         validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId());
148         // 校验租户域名是否重复
149         validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId());
150         // 校验套餐被禁用
151         TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
152
153         // 更新租户
154         TenantDO updateObj = BeanUtils.toBean(updateReqVO, TenantDO.class);
155         tenantMapper.updateById(updateObj);
156         // 如果套餐发生变化,则修改其角色的权限
157         if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) {
158             updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds());
159         }
160     }
161
162     private void validTenantNameDuplicate(String name, Long id) {
163         TenantDO tenant = tenantMapper.selectByName(name);
164         if (tenant == null) {
165             return;
166         }
167         // 如果 id 为空,说明不用比较是否为相同名字的租户
168         if (id == null) {
169             throw exception(TENANT_NAME_DUPLICATE, name);
170         }
171         if (!tenant.getId().equals(id)) {
172             throw exception(TENANT_NAME_DUPLICATE, name);
173         }
174     }
175
176     private void validTenantWebsiteDuplicate(String website, Long id) {
177         if (StrUtil.isEmpty(website)) {
178             return;
179         }
180         TenantDO tenant = tenantMapper.selectByWebsite(website);
181         if (tenant == null) {
182             return;
183         }
184         // 如果 id 为空,说明不用比较是否为相同名字的租户
185         if (id == null) {
186             throw exception(TENANT_WEBSITE_DUPLICATE, website);
187         }
188         if (!tenant.getId().equals(id)) {
189             throw exception(TENANT_WEBSITE_DUPLICATE, website);
190         }
191     }
192
193     @Override
194     @DSTransactional
195     public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
196         TenantUtils.execute(tenantId, () -> {
197             // 获得所有角色
198             List<RoleDO> roles = roleService.getRoleList();
199             roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配",
200                     role.getId(), role.getTenantId(), tenantId)); // 兜底校验
201             // 重新分配每个角色的权限
202             roles.forEach(role -> {
203                 // 如果是租户管理员,重新分配其权限为租户套餐的权限
204                 if (Objects.equals(role.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
205                     permissionService.assignRoleMenu(role.getId(), menuIds);
206                     log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), menuIds);
207                     return;
208                 }
209                 // 如果是其他角色,则去掉超过套餐的权限
210                 Set<Long> roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId());
211                 roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds);
212                 permissionService.assignRoleMenu(role.getId(), roleMenuIds);
213                 log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds);
214             });
215         });
216     }
217
218     @Override
219     public void deleteTenant(Long id) {
220         // 校验存在
221         validateUpdateTenant(id);
222         // 删除
223         tenantMapper.deleteById(id);
224     }
225
226     private TenantDO validateUpdateTenant(Long id) {
227         TenantDO tenant = tenantMapper.selectById(id);
228         if (tenant == null) {
229             throw exception(TENANT_NOT_EXISTS);
230         }
231         // 内置租户,不允许删除
232         if (isSystemTenant(tenant)) {
233             throw exception(TENANT_CAN_NOT_UPDATE_SYSTEM);
234         }
235         return tenant;
236     }
237
238     @Override
239     public TenantDO getTenant(Long id) {
240         return tenantMapper.selectById(id);
241     }
242
243     @Override
244     public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
245         return tenantMapper.selectPage(pageReqVO);
246     }
247
248     @Override
249     public TenantDO getTenantByName(String name) {
250         return tenantMapper.selectByName(name);
251     }
252
253     @Override
254     public TenantDO getTenantByWebsite(String website) {
255         return tenantMapper.selectByWebsite(website);
256     }
257
258     @Override
259     public Long getTenantCountByPackageId(Long packageId) {
260         return tenantMapper.selectCountByPackageId(packageId);
261     }
262
263     @Override
264     public List<TenantDO> getTenantListByPackageId(Long packageId) {
265         return tenantMapper.selectListByPackageId(packageId);
266     }
267
268     @Override
269     public void handleTenantInfo(TenantInfoHandler handler) {
270         // 如果禁用,则不执行逻辑
271         if (isTenantDisable()) {
272             return;
273         }
274         // 获得租户
275         TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
276         // 执行处理器
277         handler.handle(tenant);
278     }
279
280     @Override
281     public void handleTenantMenu(TenantMenuHandler handler) {
282         // 如果禁用,则不执行逻辑
283         if (isTenantDisable()) {
284             return;
285         }
286         // 获得租户,然后获得菜单
287         TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
288         Set<Long> menuIds;
289         if (isSystemTenant(tenant)) { // 系统租户,菜单是全量的
290             menuIds = CollectionUtils.convertSet(menuService.getMenuList(), MenuDO::getId);
291         } else {
292             menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds();
293         }
294         // 执行处理器
295         handler.handle(menuIds);
296     }
297
298     private static boolean isSystemTenant(TenantDO tenant) {
299         return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM);
300     }
301
302     private boolean isTenantDisable() {
303         return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
304     }
305
306 }