zhaopeiqing 7 månader sedan
förälder
incheckning
f5ba59ce5b
20 ändrade filer med 318 tillägg och 57 borttagningar
  1. 15 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java
  2. 1 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
  3. 22 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
  4. 6 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java
  5. 31 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java
  6. 9 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java
  7. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java
  8. 3 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSimpleRespVO.java
  9. 12 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java
  10. 9 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java
  11. 8 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
  12. 9 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
  13. 16 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java
  14. 23 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java
  15. 17 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java
  16. 63 22
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
  17. 2 17
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
  18. 4 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/UserTenantRelateServiceImpl.java
  19. 59 0
      yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/tenant/TenantMapper.xml
  20. 8 7
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java

+ 15 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java

@@ -41,4 +41,19 @@ public class AdminUserRespDTO {
      */
     private String mobile;
 
+    /**
+     * 多租户编号
+     */
+    private Long tenantId;
+
+    /**
+     * 多租户名称
+     */
+    private String tenantName;
+
+    /**
+     * 当前租户邀请码
+     */
+    private String corpId;
+
 }

+ 1 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java

@@ -47,6 +47,7 @@ public interface ErrorCodeConstants {
     ErrorCode USER_TENANT_RELATE_NOT_EXISTS = new ErrorCode(1_002_003_011, "用户租户关系不存在");
     ErrorCode USER_TENANT_EMPLOYEE_DUPLICATE = new ErrorCode(1_002_003_012, "该用户在名字为【{}】的租户下已存在员工");
     ErrorCode USER_NOT_IN_TENANT = new ErrorCode(1_002_003_013, "该用户不在该租户中");
+    ErrorCode USER_TENANT_EXISTS = new ErrorCode(1_002_003_014, "该用户在名字为【{}】的租户下已存在");
 
     // ========== 部门模块 1-002-004-000 ==========
     ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");

+ 22 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java

@@ -5,12 +5,15 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
 import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
@@ -18,6 +21,7 @@ import cn.iocoder.yudao.module.system.service.permission.MenuService;
 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
 import cn.iocoder.yudao.module.system.service.permission.RoleService;
 import cn.iocoder.yudao.module.system.service.social.SocialClientService;
+import cn.iocoder.yudao.module.system.service.tenant.TenantService;
 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -61,6 +65,8 @@ public class AuthController {
 
     @Resource
     private SecurityProperties securityProperties;
+    @Resource
+    private TenantService tenantService;
 
     @PostMapping("/login")
     @PermitAll
@@ -97,11 +103,17 @@ public class AuthController {
         if (user == null) {
             return success(null);
         }
+        AdminUserRespDTO dto = BeanUtils.toBean(user, AdminUserRespDTO.class);
+        TenantDO tenantDO = tenantService.getTenant(user.getTenantId()); // 获得租户信息,用于判断是否需要切换租户
+        if (tenantDO != null && tenantDO.getName() != null) {
+            dto.setTenantName(tenantDO.getName());
+            dto.setCorpId(tenantDO.getCorpId());
+        }
 
         // 1.2 获得角色列表
         Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
         if (CollUtil.isEmpty(roleIds)) {
-            return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList()));
+            return success(AuthConvert.INSTANCE.convert(dto, Collections.emptyList(), Collections.emptyList()));
         }
         List<RoleDO> roles = roleService.getRoleList(roleIds);
         roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
@@ -112,7 +124,7 @@ public class AuthController {
         menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
 
         // 2. 拼接结果返回
-        return success(AuthConvert.INSTANCE.convert(user, roles, menuList));
+        return success(AuthConvert.INSTANCE.convert(dto, roles, menuList));
     }
 
     // ========== 短信登录相关 ==========
@@ -168,4 +180,12 @@ public class AuthController {
         return success(authService.socialLogin(reqVO));
     }
 
+    @GetMapping("/getCurrentTenantIdByUsername")
+    @PermitAll
+    @Operation(summary = "通过用户账号获取租户列表")
+    @Parameter(name = "username", description = "用户账号", required = true, example = "1024")
+    public CommonResult<Long> getCurrentTenantIdByUsername(@RequestParam("username") String username) {
+        return success(authService.getCurrentTenantIdByUsername(username));
+    }
+
 }

+ 6 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java

@@ -50,6 +50,12 @@ public class AuthPermissionInfoRespVO {
         @Schema(description = "当前租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
         private Long tenantId;
 
+        @Schema(description = "当前租户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王道")
+        private String tenantName;
+
+        @Schema(description = "当前租户邀请码", requiredMode = Schema.RequiredMode.REQUIRED, example = "UUID")
+        private String corpId;
+
     }
 
     @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")

+ 31 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
+import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
@@ -27,7 +28,9 @@ import java.io.IOException;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
 
 @Tag(name = "管理后台 - 租户")
 @RestController
@@ -60,13 +63,13 @@ public class TenantController {
     @PostMapping("/create")
     @Operation(summary = "创建租户")
 //    @PreAuthorize("@ss.hasPermission('system:tenant:create')")
-    public CommonResult<Long> createTenant(@Valid @RequestBody TenantSaveReqVO createReqVO) {
+    public CommonResult<TenantSimpleRespVO> createTenant(@Valid @RequestBody TenantSaveReqVO createReqVO) {
         return success(tenantService.createTenant(createReqVO));
     }
 
     @PostMapping("/join")
     @Operation(summary = "加入租户")
-    public CommonResult<Long> joinTenant(@Valid @RequestBody TenantJoinReqVO createReqVO) {
+    public CommonResult<TenantSimpleRespVO> joinTenant(@Valid @RequestBody TenantJoinReqVO createReqVO) {
         return success(tenantService.joinTenant(createReqVO));
     }
 
@@ -106,6 +109,18 @@ public class TenantController {
         return success(BeanUtils.toBean(tenant, TenantRespVO.class));
     }
 
+    @GetMapping("/getTenant")
+    @Operation(summary = "获得租户")
+    public CommonResult<TenantRespVO> getTenant() {
+        // 获得用户信息
+        LoginUser user = SecurityFrameworkUtils.getLoginUser();
+        if (user == null || user.getTenantId() == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+        TenantDO tenant = tenantService.getTenant(user.getTenantId());
+        return success(BeanUtils.toBean(tenant, TenantRespVO.class));
+    }
+
     @GetMapping("/page")
     @Operation(summary = "获得租户分页")
     @PreAuthorize("@ss.hasPermission('system:tenant:query')")
@@ -114,6 +129,20 @@ public class TenantController {
         return success(BeanUtils.toBean(pageResult, TenantRespVO.class));
     }
 
+    @GetMapping("/getOwnCreateTenants")
+    @Operation(summary = "获得我创建的租户列表")
+    public CommonResult<List<TenantRespVO>> getOwnCreateTenants() {
+        List<TenantRespVO> list = tenantService.getOwnCreateTenants();
+        return success(BeanUtils.toBean(list, TenantRespVO.class));
+    }
+
+    @GetMapping("/getOwnJoinTenants")
+    @Operation(summary = "获得我加入的租户列表")
+    public CommonResult<List<TenantRespVO>> getOwnJoinTenants() {
+        List<TenantRespVO> list = tenantService.getOwnJoinTenants();
+        return success(BeanUtils.toBean(list, TenantRespVO.class));
+    }
+
     @GetMapping("/export-excel")
     @Operation(summary = "导出租户 Excel")
     @PreAuthorize("@ss.hasPermission('system:tenant:export')")

+ 9 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java

@@ -52,10 +52,19 @@ public class TenantRespVO {
     @ExcelProperty("创建时间")
     private LocalDateTime createTime;
 
+    @Schema(description = "创建者", example = "1024")
+    private String creator;
+
+    @Schema(description = "删除标识", example = "1")
+    private Boolean deleted;
+
     @Schema(description = "邀请码", example = "uuid")
     private String corpId;
 
     @Schema(description = "账号模式", example = "1")
     private Integer accountMode;
 
+    @Schema(description = "当前生效", example = "1")
+    private Boolean actived;
+
 }

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java

@@ -10,7 +10,7 @@ import java.time.LocalDateTime;
 @Data
 public class TenantSaveReqVO {
 
-//    @Schema(description = "租户编号", example = "1024")
+    @Schema(description = "租户编号", example = "1024")
     private Long id;
 
     @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")

+ 3 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSimpleRespVO.java

@@ -13,4 +13,7 @@ public class TenantSimpleRespVO {
     @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
     private String name;
 
+    @Schema(description = "邀请码", requiredMode = Schema.RequiredMode.REQUIRED, example = "UUID")
+    private String corpId;
+
 }

+ 12 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
@@ -39,6 +40,17 @@ public interface AuthConvert {
                 .build();
     }
 
+    default AuthPermissionInfoRespVO convert(AdminUserRespDTO user, List<RoleDO> roleList, List<MenuDO> menuList) {
+        return AuthPermissionInfoRespVO.builder()
+                .user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class))
+                .roles(convertSet(roleList, RoleDO::getCode))
+                // 权限标识信息
+                .permissions(convertSet(menuList, MenuDO::getPermission))
+                // 菜单树
+                .menus(buildMenuTree(menuList))
+                .build();
+    }
+
     AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
 
     /**

+ 9 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java

@@ -3,9 +3,12 @@ package cn.iocoder.yudao.module.system.dal.mysql.tenant;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -47,4 +50,10 @@ public interface TenantMapper extends BaseMapperX<TenantDO> {
         return selectOne(TenantDO::getCorpId, corpId);
     }
 
+    @TenantIgnore
+    List<TenantRespVO> getOwnCreateTenants(@Param("userId") Long userId);
+
+    @TenantIgnore
+    List<TenantRespVO> getOwnJoinTenants(@Param("userId") Long userId);
+
 }

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java

@@ -86,4 +86,12 @@ public interface AdminAuthService {
      */
     AuthLoginRespVO refreshToken(String refreshToken);
 
+    /**
+     * 账号登录 通过用户账号获取当前租户ID
+     *
+     * @param username
+     * @return 登录结果
+     */
+    Long getCurrentTenantIdByUsername(String username);
+
 }

+ 9 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@@ -243,6 +243,15 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         return AuthConvert.INSTANCE.convert(accessTokenDO);
     }
 
+    @Override
+    public Long getCurrentTenantIdByUsername(String username) {
+        AdminUserDO adminUserDO = userService.getUserByUsername(username);
+        if (adminUserDO == null || adminUserDO.getTenantId() == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+        return adminUserDO.getTenantId();
+    }
+
     @Override
     public void logout(String token, Integer logType) {
         // 删除访问令牌

+ 16 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java

@@ -29,6 +29,22 @@ public interface OAuth2TokenService {
      */
     OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes);
 
+    /**
+     * 创建访问令牌
+     * 注意:该流程中,会包含创建刷新令牌的创建
+     *
+     * 参考 DefaultTokenServices 的 createAccessToken 方法
+     *
+     * @param userId 用户编号
+     * @param userType 用户类型
+     * @param clientId 客户端编号
+     * @param scopes 授权范围
+     * @param tenantId 租户编号
+     * @return 访问令牌的信息
+     */
+    OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes, Long tenantId);
+
+
     /**
      * 刷新访问令牌
      *

+ 23 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java

@@ -65,6 +65,15 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         return createOAuth2AccessToken(refreshTokenDO, clientDO);
     }
 
+    @Override
+    public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes, Long tenantId) {
+        OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+        // 创建刷新令牌
+        OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes);
+        // 创建访问令牌
+        return createOAuth2AccessToken(refreshTokenDO, clientDO, tenantId);
+    }
+
     @Override
     public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId) {
         // 查询访问令牌
@@ -158,6 +167,20 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         return accessTokenDO;
     }
 
+    private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO, Long tenantId) {
+        OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+                .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())
+                .setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType()))
+                .setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes())
+                .setRefreshToken(refreshTokenDO.getRefreshToken())
+                .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds()));
+        accessTokenDO.setTenantId(tenantId); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
+        oauth2AccessTokenMapper.insert(accessTokenDO);
+        // 记录到 Redis 中
+        oauth2AccessTokenRedisDAO.set(accessTokenDO);
+        return accessTokenDO;
+    }
+
     private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO, List<String> scopes) {
         OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
                 .setUserId(userId).setUserType(userType)

+ 17 - 6
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java

@@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.system.service.tenant;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
-import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantJoinReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
@@ -27,7 +25,7 @@ public interface TenantService {
      * @param createReqVO 创建信息
      * @return 编号
      */
-    Long createTenant(@Valid TenantSaveReqVO createReqVO);
+    TenantSimpleRespVO createTenant(@Valid TenantSaveReqVO createReqVO);
 
     /**
      * 加入租户
@@ -35,7 +33,7 @@ public interface TenantService {
      * @param createReqVO 创建信息
      * @return 编号
      */
-    Long joinTenant(@Valid TenantJoinReqVO createReqVO);
+    TenantSimpleRespVO joinTenant(@Valid TenantJoinReqVO createReqVO);
 
     /**
      * 更新租户
@@ -75,7 +73,20 @@ public interface TenantService {
     TenantDO getTenant(Long id);
 
     /**
-     * 获得租户分页
+     * 获得我创建的租户列表
+     *
+     * @return
+     */
+    List<TenantRespVO> getOwnCreateTenants();
+
+    /**
+     * 获得我加入的租户列表
+     *
+     * @return
+     */
+    List<TenantRespVO> getOwnJoinTenants();
+    /**
+     * 获得我加入的租户列表
      *
      * @param pageReqVO 分页查询
      * @return 租户分页

+ 63 - 22
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java

@@ -25,9 +25,7 @@ import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantJoinReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
 import cn.iocoder.yudao.module.system.controller.admin.user.vo.tenant.UserTenantRelateSaveReqVO;
 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
 import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
@@ -61,6 +59,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -132,7 +131,7 @@ public class TenantServiceImpl implements TenantService {
 
     @Override
     @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
-    public Long createTenant(TenantSaveReqVO createReqVO) {
+    public TenantSimpleRespVO createTenant(TenantSaveReqVO createReqVO) {
         // 校验租户名称是否重复
         validTenantNameDuplicate(createReqVO.getName(), null);
         // 校验租户域名是否重复
@@ -142,21 +141,23 @@ public class TenantServiceImpl implements TenantService {
 
         // 创建租户
         TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class);
-        tenant.setCorpId(IdUtil.fastSimpleUUID());
+        String corpId = IdUtil.fastSimpleUUID();
+        tenant.setCorpId(corpId);
+        tenant.setExpireTime(LocalDateTime.now().plusYears(10));// 默认设置10年有效期
         tenantMapper.insert(tenant);
         // 获得用户信息
         LoginUser user = SecurityFrameworkUtils.getLoginUser();
-        // 更新用户信息当前租户
-        userService.updateUserTenantId(user.getId(), tenant.getId());
-        // 将这个用户所有租户关系设为不生效
-        userTenantRelateService.deactivateAllForUser(user.getId());
-        // 插入用户租户关系
-        userTenantRelateService.createUserTenantRelate(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()).setActived(true));
         // 自动根据账号创建对应人事信息,只保存姓名、头像、手机号
         AdminUserDO adminUserDO = userService.getUser(user.getId());
         if (adminUserDO == null) {
             throw exception(USER_NOT_EXISTS);
         }
+//        // 更新用户信息当前租户
+//        userService.updateUserTenantId(user.getId(), tenant.getId());
+//        // 将这个用户所有租户关系设为不生效
+//        userTenantRelateService.deactivateAllForUser(user.getId());
+        // 插入用户租户关系
+        userTenantRelateService.createUserTenantRelate(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()).setActived(false));
         // 先判断该租户下员工信息是否存在,如果不存在则创建
         EmployeeRespDTO employeeRespDTO = employeeApi.getEmployee(new EmployeeQueryReqDTO().setUserId(user.getId()).setTenantId(tenant.getId()));
         if (employeeRespDTO == null) {
@@ -174,12 +175,16 @@ public class TenantServiceImpl implements TenantService {
             // 修改租户的管理员
             tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(user.getId()));
         });
-        return tenant.getId();
+        TenantSimpleRespVO tenantSimpleRespVO = new TenantSimpleRespVO();
+        tenantSimpleRespVO.setId(tenant.getId());
+        tenantSimpleRespVO.setName(tenant.getName());
+        tenantSimpleRespVO.setCorpId(corpId);
+        return tenantSimpleRespVO;
     }
 
     @Override
     @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
-    public Long joinTenant(TenantJoinReqVO createReqVO) {
+    public TenantSimpleRespVO joinTenant(TenantJoinReqVO createReqVO) {
         // 根据租户邀请码查询租户信息
         TenantDO tenant = tenantMapper.selectByCorpId(createReqVO.getCorpId());
         if (tenant == null || tenant.getId() == null) {
@@ -187,12 +192,16 @@ public class TenantServiceImpl implements TenantService {
         }
         // 获得用户信息
         LoginUser user = SecurityFrameworkUtils.getLoginUser();
-        // 更新用户信息当前租户
-        userService.updateUserTenantId(user.getId(), tenant.getId());
-        // 将这个用户所有租户关系设为不生效
-        userTenantRelateService.deactivateAllForUser(user.getId());
+        // 校验该用户是否在该租户中
+        if (userTenantRelateService.checkUserHasTenant(user.getId(), tenant.getId())) {
+            throw exception(USER_TENANT_EXISTS, tenant.getName());
+        }
+//        // 更新用户信息当前租户
+//        userService.updateUserTenantId(user.getId(), tenant.getId());
+//        // 将这个用户所有租户关系设为不生效
+//        userTenantRelateService.deactivateAllForUser(user.getId());
         // 插入用户租户关系
-        userTenantRelateService.createUserTenantRelate(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()).setActived(true));
+        userTenantRelateService.createUserTenantRelate(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()).setActived(false));
         // 自动根据账号创建对应人事信息,只保存姓名、头像、手机号
         AdminUserDO adminUserDO = userService.getUser(user.getId());
         if (adminUserDO == null) {
@@ -206,7 +215,11 @@ public class TenantServiceImpl implements TenantService {
         } else {
             throw exception(USER_TENANT_EMPLOYEE_DUPLICATE, tenant.getName());
         }
-        return tenant.getId();
+        TenantSimpleRespVO tenantSimpleRespVO = new TenantSimpleRespVO();
+        tenantSimpleRespVO.setId(tenant.getId());
+        tenantSimpleRespVO.setName(tenant.getName());
+        tenantSimpleRespVO.setCorpId(tenant.getCorpId());
+        return tenantSimpleRespVO;
     }
 
     private Long createUser(Long roleId, TenantSaveReqVO createReqVO) {
@@ -249,6 +262,14 @@ public class TenantServiceImpl implements TenantService {
     @Override
     @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
     public void updateTenant(TenantSaveReqVO updateReqVO) {
+        // 1.1 获得用户信息
+        AdminUserDO user = userService.getUser(getLoginUserId());
+        if (user == null || user.getTenantId() == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+        if (updateReqVO.getId() == null) {
+            updateReqVO.setId(user.getTenantId());
+        }
         // 校验存在
         TenantDO tenant = validateUpdateTenant(updateReqVO.getId());
         // 校验租户名称是否重复
@@ -292,7 +313,7 @@ public class TenantServiceImpl implements TenantService {
         // 将这个用户的这个租户关系设为生效
         userTenantRelateService.activateForUser(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()));
         authService.logout(createReqVO.getToken(), LoginLogTypeEnum.LOGOUT_CHANGE_TENANT.getType());
-        return createTokenAfterLoginSuccess(adminUserDO.getId(), adminUserDO.getUsername(), LoginLogTypeEnum.LOGIN_CHANGE_TENANT);
+        return createTokenAfterLoginSuccess(adminUserDO.getId(), adminUserDO.getUsername(), LoginLogTypeEnum.LOGIN_CHANGE_TENANT, tenant.getId());
     }
 
     private void validTenantNameDuplicate(String name, Long id) {
@@ -376,6 +397,26 @@ public class TenantServiceImpl implements TenantService {
         return tenantMapper.selectById(id);
     }
 
+    @Override
+    public List<TenantRespVO> getOwnCreateTenants() {
+        // 获得用户信息
+        LoginUser user = SecurityFrameworkUtils.getLoginUser();
+        if (user == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+        return tenantMapper.getOwnCreateTenants(user.getId());
+    }
+
+    @Override
+    public List<TenantRespVO> getOwnJoinTenants() {
+        // 获得用户信息
+        LoginUser user = SecurityFrameworkUtils.getLoginUser();
+        if (user == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+        return tenantMapper.getOwnJoinTenants(user.getId());
+    }
+
     @Override
     public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
         return tenantMapper.selectPage(pageReqVO);
@@ -444,12 +485,12 @@ public class TenantServiceImpl implements TenantService {
         return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
     }
 
-    private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
+    private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType, Long tenantId) {
         // 插入登陆日志
         createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
         // 创建访问令牌
         OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
-                OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
+                OAuth2ClientConstants.CLIENT_ID_DEFAULT, null, tenantId);
         // 构建返回结果
         return AuthConvert.INSTANCE.convert(accessTokenDO);
     }

+ 2 - 17
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java

@@ -23,16 +23,12 @@ import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqV
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
-import cn.iocoder.yudao.module.system.dal.dataobject.user.UserTenantRelateDO;
 import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
-import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import cn.iocoder.yudao.module.system.service.dept.PostService;
 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.github.yulichang.wrapper.interfaces.SelectWrapper;
 import com.google.common.annotations.VisibleForTesting;
 import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
@@ -290,6 +286,7 @@ public class AdminUserServiceImpl implements AdminUserService {
     }
 
     @Override
+    @TenantIgnore
     public AdminUserDO getUser(Long id) {
         return userMapper.selectById(id);
     }
@@ -534,6 +531,7 @@ public class AdminUserServiceImpl implements AdminUserService {
     }
 
     @Override
+    @TenantIgnore
     public void updateUserTenantId(Long id, Long tenantId) {
         // 校验用户存在
         validateUserExists(id);
@@ -544,17 +542,4 @@ public class AdminUserServiceImpl implements AdminUserService {
         userMapper.updateById(updateObj);
     }
 
-    /**
-     * 将该用户所有关系设为失效
-     *
-     * @param userId 用户ID
-     * @return 受影响的行数
-     */
-//    @Override
-//    public int deactivateAllForUser(Long userId) {
-//        SelectWrapper<UserTenantRelateDO> updateWrapper = new UpdateWrapper<>();
-//        updateWrapper.eq("user_id", userId).set("actived", false); // 注意:这里使用false而不是b'0',因为MyBatis-Plus会处理类型转换
-//        return userMapper.update(null, updateWrapper); // 第一个参数是null,因为我们不需要传递实体对象,只使用Wrapper来指定更新条件
-//    }
-
 }

+ 4 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/UserTenantRelateServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.system.service.user;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.controller.admin.user.vo.tenant.UserTenantRelatePageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.user.vo.tenant.UserTenantRelateSaveReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.UserTenantRelateDO;
@@ -79,6 +80,7 @@ public class UserTenantRelateServiceImpl implements UserTenantRelateService {
      * @return 受影响的行数
      */
     @Override
+    @TenantIgnore
     public int deactivateAllForUser(Long userId) {
         // 创建一个LambdaUpdateWrapper实例
         LambdaUpdateWrapper<UserTenantRelateDO> updateWrapper = new LambdaUpdateWrapper<>();
@@ -98,6 +100,7 @@ public class UserTenantRelateServiceImpl implements UserTenantRelateService {
      * @return
      */
     @Override
+    @TenantIgnore
     public boolean checkUserHasTenant(Long userId, Long tenantId) {
         // 使用LambdaQueryWrapper来构建查询条件
         LambdaQueryWrapper<UserTenantRelateDO> queryWrapper = new LambdaQueryWrapper<>();
@@ -117,6 +120,7 @@ public class UserTenantRelateServiceImpl implements UserTenantRelateService {
      * @return 受影响的行数
      */
     @Override
+    @TenantIgnore
     public int activateForUser(UserTenantRelateSaveReqVO updateReqVO) {
         // 创建一个LambdaUpdateWrapper实例
         LambdaUpdateWrapper<UserTenantRelateDO> updateWrapper = new LambdaUpdateWrapper<>();

+ 59 - 0
yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/tenant/TenantMapper.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper">
+
+    <!-- 定义resultMap -->
+    <resultMap id="BaseResultMap" type="cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO">
+        <id column="id" property="id" jdbcType="BIGINT" />
+        <result column="name" property="name" jdbcType="VARCHAR" />
+        <result column="contact_name" property="contactName" jdbcType="VARCHAR" />
+        <result column="contact_mobile" property="contactMobile" jdbcType="VARCHAR" />
+        <result column="status" property="status" jdbcType="INTEGER" />
+        <result column="website" property="website" jdbcType="VARCHAR" />
+        <result column="package_id" property="packageId" jdbcType="BIGINT" />
+        <result column="expire_time" property="expireTime" jdbcType="TIMESTAMP" />
+        <result column="account_count" property="accountCount" jdbcType="INTEGER" />
+        <result column="corp_id" property="corpId" jdbcType="VARCHAR" />
+        <result column="account_mode" property="accountMode" jdbcType="INTEGER" />
+        <result column="creator" property="creator" jdbcType="VARCHAR" />
+        <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
+        <result column="deleted" property="deleted" jdbcType="BIT" />
+        <result column="actived" property="actived" jdbcType="BIT" />
+    </resultMap>
+
+    <!--
+        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
+     -->
+
+    <select id="getOwnCreateTenants" resultMap="BaseResultMap">
+        SELECT
+            st.*,
+            utr.actived
+        FROM
+            system_tenant st
+                LEFT JOIN
+            user_tenant_relate utr ON utr.tenant_id = st.id
+        WHERE
+            utr.user_id = #{userId}
+        AND st.creator = #{userId}
+        ORDER BY utr.actived DESC
+    </select>
+
+    <select id="getOwnJoinTenants" resultMap="BaseResultMap">
+        SELECT
+            st.*,
+            utr.actived
+        FROM
+            system_tenant st
+                LEFT JOIN
+            user_tenant_relate utr ON utr.tenant_id = st.id
+        WHERE
+            utr.user_id = #{userId}
+        AND st.creator != #{userId}
+        ORDER BY utr.actived DESC
+    </select>
+
+</mapper>

+ 8 - 7
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSimpleRespVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
@@ -153,19 +154,19 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
         TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> {
             o.setContactName("芋道");
             o.setContactMobile("15601691300");
-            o.setPackageId(100L);
+//            o.setPackageId(100L);
             o.setStatus(randomCommonStatus());
             o.setWebsite("https://www.iocoder.cn");
-            o.setUsername("yunai");
-            o.setPassword("yuanma");
+//            o.setUsername("yunai");
+//            o.setPassword("yuanma");
         }).setId(null); // 设置为 null,方便后面校验
 
         // 调用
-        Long tenantId = tenantService.createTenant(reqVO);
+        TenantSimpleRespVO resp = tenantService.createTenant(reqVO);
         // 断言
-        assertNotNull(tenantId);
+        assertNotNull(resp.getId());
         // 校验记录的属性是否正确
-        TenantDO tenant = tenantMapper.selectById(tenantId);
+        TenantDO tenant = tenantMapper.selectById(resp.getId());
         assertPojoEquals(reqVO, tenant, "id");
         assertEquals(300L, tenant.getContactUserId());
         // verify 分配权限
@@ -189,7 +190,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
         // mock 套餐
         TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class,
                 o -> o.setMenuIds(asSet(200L, 201L)));
-        when(tenantPackageService.validTenantPackage(eq(reqVO.getPackageId()))).thenReturn(tenantPackage);
+        when(tenantPackageService.validTenantPackage(eq(null))).thenReturn(tenantPackage);
         // mock 所有角色
         RoleDO role100 = randomPojo(RoleDO.class, o -> o.setId(100L).setCode(RoleCodeEnum.TENANT_ADMIN.getCode()));
         role100.setTenantId(dbTenant.getId());