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