From d88a9ca74ae366f7ff82ba4b362d3ec037dd37d7 Mon Sep 17 00:00:00 2001 From: zzy Date: Tue, 21 Apr 2026 14:58:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=9A=E5=91=98=E7=AE=A1=E7=90=86=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/user/MemberUserDO.java | 20 +-- .../user/MemberUserTenantRelDO.java | 44 +++++++ .../mysql/user/MemberUserTenantRelMapper.java | 64 ++++++++++ .../service/auth/MemberAuthServiceImpl.java | 82 ++++++++++-- .../user/MemberUserTenantRelService.java | 55 ++++++++ .../user/MemberUserTenantRelServiceImpl.java | 119 ++++++++++++++++++ 6 files changed, 364 insertions(+), 20 deletions(-) create mode 100644 fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserTenantRelDO.java create mode 100644 fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/mysql/user/MemberUserTenantRelMapper.java create mode 100644 fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelService.java create mode 100644 fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelServiceImpl.java diff --git a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserDO.java b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserDO.java index 47dc6a1..0d4bda5 100644 --- a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserDO.java +++ b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserDO.java @@ -1,17 +1,18 @@ package com.fjrcloud.community.module.member.dal.dataobject.user; -import com.fjrcloud.community.framework.common.enums.CommonStatusEnum; -import com.fjrcloud.community.framework.common.enums.TerminalEnum; -import com.fjrcloud.community.framework.ip.core.Area; -import com.fjrcloud.community.framework.mybatis.core.type.LongListTypeHandler; -import com.fjrcloud.community.framework.tenant.core.db.TenantBaseDO; -import com.fjrcloud.community.module.member.dal.dataobject.group.MemberGroupDO; -import com.fjrcloud.community.module.member.dal.dataobject.level.MemberLevelDO; -import com.fjrcloud.community.module.system.enums.common.SexEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.fjrcloud.community.framework.common.enums.CommonStatusEnum; +import com.fjrcloud.community.framework.common.enums.TerminalEnum; +import com.fjrcloud.community.framework.ip.core.Area; +import com.fjrcloud.community.framework.mybatis.core.type.LongListTypeHandler; +import com.fjrcloud.community.framework.tenant.core.aop.TenantIgnore; +import com.fjrcloud.community.framework.tenant.core.db.TenantBaseDO; +import com.fjrcloud.community.module.member.dal.dataobject.group.MemberGroupDO; +import com.fjrcloud.community.module.member.dal.dataobject.level.MemberLevelDO; +import com.fjrcloud.community.module.system.enums.common.SexEnum; import lombok.*; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -26,12 +27,13 @@ import java.util.List; * @author 芋道源码 */ @TableName(value = "member_user", autoResultMap = true) -@KeySequence("member_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@KeySequence("member_user_seq") @Data @EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor +@TenantIgnore public class MemberUserDO extends TenantBaseDO { // ========== 账号信息 ========== diff --git a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserTenantRelDO.java b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserTenantRelDO.java new file mode 100644 index 0000000..56b20a4 --- /dev/null +++ b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/dataobject/user/MemberUserTenantRelDO.java @@ -0,0 +1,44 @@ +package com.fjrcloud.community.module.member.dal.dataobject.user; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fjrcloud.community.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +/** + * 会员用户与租户关联表 + * + * @author 福建融云 + */ +@TableName("member_user_tenant_rel") +@KeySequence("member_user_tenant_rel_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberUserTenantRelDO extends BaseDO { + + /** + * 主键ID + */ + @TableId + private Long id; + + /** + * 会员用户ID + */ + private Long userId; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 是否为默认租户 + */ + private Boolean defaultTenant; +} diff --git a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/mysql/user/MemberUserTenantRelMapper.java b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/mysql/user/MemberUserTenantRelMapper.java new file mode 100644 index 0000000..580f373 --- /dev/null +++ b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/dal/mysql/user/MemberUserTenantRelMapper.java @@ -0,0 +1,64 @@ +package com.fjrcloud.community.module.member.dal.mysql.user; + +import com.fjrcloud.community.framework.mybatis.core.mapper.BaseMapperX; +import com.fjrcloud.community.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.fjrcloud.community.module.member.dal.dataobject.user.MemberUserTenantRelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 会员用户与租户关联 Mapper + * + * @author 福建融云 + */ +@Mapper +public interface MemberUserTenantRelMapper extends BaseMapperX { + + /** + * 根据用户ID查询关联列表 + * + * @param userId 用户ID + * @return 关联列表 + */ + default List selectListByUserId(Long userId) { + return selectList(MemberUserTenantRelDO::getUserId, userId); + } + + /** + * 根据用户ID和租户ID查询关联关系 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @return 关联关系 + */ + default MemberUserTenantRelDO selectByUserIdAndTenantId(Long userId, Long tenantId) { + return selectOne(new LambdaQueryWrapperX() + .eq(MemberUserTenantRelDO::getUserId, userId) + .eq(MemberUserTenantRelDO::getTenantId, tenantId)); + } + + /** + * 根据用户ID删除所有关联关系 + * + * @param userId 用户ID + * @return 删除数量 + */ + default int deleteByUserId(Long userId) { + return delete(new LambdaQueryWrapperX() + .eq(MemberUserTenantRelDO::getUserId, userId)); + } + + /** + * 根据用户ID和租户ID删除关联关系 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @return 删除数量 + */ + default int deleteByUserIdAndTenantId(Long userId, Long tenantId) { + return delete(new LambdaQueryWrapperX() + .eq(MemberUserTenantRelDO::getUserId, userId) + .eq(MemberUserTenantRelDO::getTenantId, tenantId)); + } +} diff --git a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/auth/MemberAuthServiceImpl.java b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/auth/MemberAuthServiceImpl.java index cadd82b..35567d1 100644 --- a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/auth/MemberAuthServiceImpl.java +++ b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/auth/MemberAuthServiceImpl.java @@ -1,20 +1,23 @@ package com.fjrcloud.community.module.member.service.auth; import cn.hutool.core.lang.Assert; +import com.fjrcloud.community.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; +import com.fjrcloud.community.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO; +import com.fjrcloud.community.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO; import com.fjrcloud.community.framework.common.enums.CommonStatusEnum; import com.fjrcloud.community.framework.common.enums.TerminalEnum; import com.fjrcloud.community.framework.common.enums.UserTypeEnum; import com.fjrcloud.community.framework.common.util.monitor.TracerUtils; import com.fjrcloud.community.framework.common.util.servlet.ServletUtils; +import com.fjrcloud.community.framework.tenant.core.context.TenantContextHolder; import com.fjrcloud.community.module.member.controller.app.auth.vo.*; import com.fjrcloud.community.module.member.convert.auth.AuthConvert; import com.fjrcloud.community.module.member.dal.dataobject.user.MemberUserDO; +import com.fjrcloud.community.module.member.dal.dataobject.user.MemberUserTenantRelDO; import com.fjrcloud.community.module.member.service.user.MemberUserService; +import com.fjrcloud.community.module.member.service.user.MemberUserTenantRelService; import com.fjrcloud.community.module.system.api.logger.LoginLogApi; import com.fjrcloud.community.module.system.api.logger.dto.LoginLogCreateReqDTO; -import com.fjrcloud.community.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; -import com.fjrcloud.community.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO; -import com.fjrcloud.community.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO; import com.fjrcloud.community.module.system.api.sms.SmsCodeApi; import com.fjrcloud.community.module.system.api.social.SocialClientApi; import com.fjrcloud.community.module.system.api.social.SocialUserApi; @@ -39,7 +42,7 @@ import static com.fjrcloud.community.framework.web.core.util.WebFrameworkUtils.g import static com.fjrcloud.community.module.member.enums.ErrorCodeConstants.*; /** - * 会员的认证 Service 接口 + * 会员的认证 Service 实现类 * * @author 芋道源码 */ @@ -49,22 +52,33 @@ public class MemberAuthServiceImpl implements MemberAuthService { @Resource private MemberUserService userService; + + @Resource + private MemberUserTenantRelService userTenantRelService; + @Resource private SmsCodeApi smsCodeApi; + @Resource private LoginLogApi loginLogApi; + @Resource private SocialUserApi socialUserApi; + @Resource private SocialClientApi socialClientApi; + @Resource private OAuth2TokenCommonApi oauth2TokenApi; @Override public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) { - // 使用手机 + 密码,进行登录。 + // 使用手机 + 密码进行登录 MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword()); + // 确保用户与当前租户有关联关系 + ensureUserTenantRelation(user.getId()); + // 如果 socialType 非空,说明需要绑定社交用户 String openid = null; if (reqVO.getSocialType() != null) { @@ -79,7 +93,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { @Override @Transactional public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) { - // 校验验证码 + // 获得或创建注册用户 String userIp = getClientIP(); // smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); @@ -93,6 +107,9 @@ public class MemberAuthServiceImpl implements MemberAuthService { throw exception(AUTH_LOGIN_USER_DISABLED); } + // 确保用户与当前租户有关联关系 + ensureUserTenantRelation(user.getId()); + // 如果 socialType 非空,说明需要绑定社交用户 String openid = null; if (reqVO.getSocialType() != null) { @@ -107,7 +124,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { @Override @Transactional public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) { - // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 + // 使用 code 授权码进行登录,获得绑定的用户编号 SocialUserRespDTO socialUser = socialUserApi.getSocialUserByCode(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), reqVO.getCode(), reqVO.getState()); if (socialUser == null) { @@ -115,10 +132,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { } // 情况一:已绑定,直接读取用户信息 + // 情况二:未绑定,注册用户 + 绑定用户 MemberUserDO user; if (socialUser.getUserId() != null) { user = userService.getUser(socialUser.getUserId()); - // 情况二:未绑定,注册用户 + 绑定用户 } else { user = userService.createUser(socialUser.getNickname(), socialUser.getAvatar(), getClientIP(), getTerminal()); socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), @@ -128,6 +145,9 @@ public class MemberAuthServiceImpl implements MemberAuthService { throw exception(USER_NOT_EXISTS); } + // 确保用户与当前租户有关联关系 + ensureUserTenantRelation(user.getId()); + // 创建 Token 令牌,记录登录日志 return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid()); } @@ -139,11 +159,14 @@ public class MemberAuthServiceImpl implements MemberAuthService { UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode()); Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空"); - // 获得获得注册用户 + // 获得或创建注册用户 MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(), getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal()); Assert.notNull(user, "获取用户失败,结果为空"); + // 确保用户与当前租户有关联关系 + ensureUserTenantRelation(user.getId()); + // 绑定社交用户 String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(), reqVO.getLoginCode(), reqVO.getState())); @@ -152,6 +175,29 @@ public class MemberAuthServiceImpl implements MemberAuthService { return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid); } + /** + * 确保用户与当前租户存在关联关系 + * 如果会员已在系统中注册但尚未在当前租户下关联,则自动创建关联 + * + * @param userId 用户ID + */ + private void ensureUserTenantRelation(Long userId) { + // 获取当前租户ID + Long currentTenantId = TenantContextHolder.getTenantId(); + if (currentTenantId == null) { + return; + } + + // 检查用户是否已在当前租户下有关联 + MemberUserTenantRelDO rel = userTenantRelService.getUserTenantRel(userId, currentTenantId); + + // 如果没有关联,自动创建 + if (rel == null) { + userTenantRelService.assignUserToTenant(userId, currentTenantId, false); + log.info("[ensureUserTenantRelation][会员({}) 自动关联租户({})]", userId, currentTenantId); + } + } + private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType, String openid) { // 插入登陆日志 @@ -169,23 +215,35 @@ public class MemberAuthServiceImpl implements MemberAuthService { return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri); } + /** + * 使用手机 + 密码登录 + * + * @param mobile 手机号 + * @param password 密码 + * @return 用户对象 + */ private MemberUserDO login0(String mobile, String password) { final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE; + // 校验账号是否存在 MemberUserDO user = userService.getUserByMobile(mobile); if (user == null) { createLoginLog(null, mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); throw exception(AUTH_LOGIN_BAD_CREDENTIALS); } + + // 校验密码是否正确 if (!userService.isPasswordMatch(password, user.getPassword())) { createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); throw exception(AUTH_LOGIN_BAD_CREDENTIALS); } + // 校验是否禁用 if (CommonStatusEnum.isDisable(user.getStatus())) { createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED); throw exception(AUTH_LOGIN_USER_DISABLED); } + return user; } @@ -201,6 +259,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { reqDTO.setUserIp(getClientIP()); reqDTO.setResult(loginResult.getResult()); loginLogApi.createLoginLog(reqDTO); + // 更新最后登录时间 if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { userService.updateUserLogin(userId, getClientIP()); @@ -220,13 +279,14 @@ public class MemberAuthServiceImpl implements MemberAuthService { @Override public void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO) { - // 情况 1:如果是修改手机场景,需要校验新手机号是否已经注册,说明不能使用该手机了 + // 情况 1:如果是修改手机场景,需要校验新手机号是否已经注册 if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene())) { MemberUserDO user = userService.getUserByMobile(reqVO.getMobile()); if (user != null && !Objects.equals(user.getId(), userId)) { throw exception(AUTH_MOBILE_USED); } } + // 情况 2:如果是重置密码场景,需要校验手机号是存在的 if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene())) { MemberUserDO user = userService.getUserByMobile(reqVO.getMobile()); @@ -234,10 +294,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { throw exception(USER_MOBILE_NOT_EXISTS); } } + // 情况 3:如果是修改密码场景,需要查询手机号,无需前端传递 if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene())) { MemberUserDO user = userService.getUser(userId); - // TODO 芋艿:后续 member user 手机非强绑定,这块需要做下调整; reqVO.setMobile(user.getMobile()); } diff --git a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelService.java b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelService.java new file mode 100644 index 0000000..40b0f04 --- /dev/null +++ b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelService.java @@ -0,0 +1,55 @@ +package com.fjrcloud.community.module.member.service.user; + +import com.fjrcloud.community.module.member.dal.dataobject.user.MemberUserTenantRelDO; + +import java.util.List; + +/** + * 会员用户与租户关联 Service 接口 + * + * @author 福建融云 + */ +public interface MemberUserTenantRelService { + + /** + * 获取用户的租户关联列表 + * + * @param userId 用户ID + * @return 租户关联列表 + */ + List getUserTenantRels(Long userId); + + /** + * 获取用户在指定租户的关联关系 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @return 关联关系 + */ + MemberUserTenantRelDO getUserTenantRel(Long userId, Long tenantId); + + /** + * 将用户分配到租户 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @param isDefault 是否为默认租户 + */ + void assignUserToTenant(Long userId, Long tenantId, Boolean isDefault); + + /** + * 从租户中移除用户 + * + * @param userId 用户ID + * @param tenantId 租户ID + */ + void removeUserFromTenant(Long userId, Long tenantId); + + /** + * 批量分配用户到多个租户(先删除再新增) + * + * @param userId 用户ID + * @param tenantIds 租户ID列表 + */ + void batchAssignUserToTenants(Long userId, List tenantIds); +} diff --git a/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelServiceImpl.java b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelServiceImpl.java new file mode 100644 index 0000000..2e23ffc --- /dev/null +++ b/fjrcloud-module-member/src/main/java/com/fjrcloud/community/module/member/service/user/MemberUserTenantRelServiceImpl.java @@ -0,0 +1,119 @@ +package com.fjrcloud.community.module.member.service.user; + +import cn.hutool.core.collection.CollUtil; +import com.fjrcloud.community.module.member.dal.dataobject.user.MemberUserTenantRelDO; +import com.fjrcloud.community.module.member.dal.mysql.user.MemberUserTenantRelMapper; +import com.fjrcloud.community.module.system.dal.dataobject.tenant.TenantDO; +import com.fjrcloud.community.module.system.dal.mysql.tenant.TenantMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +import static com.fjrcloud.community.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.fjrcloud.community.module.system.enums.ErrorCodeConstants.TENANT_NOT_EXISTS; + +/** + * 会员用户与租户关联 Service 实现类 + * + * @author 福建融云 + */ +@Service +@Slf4j +public class MemberUserTenantRelServiceImpl implements MemberUserTenantRelService { + + @Resource + private MemberUserTenantRelMapper memberUserTenantRelMapper; + + @Resource + private TenantMapper tenantMapper; + + @Override + public List getUserTenantRels(Long userId) { + return memberUserTenantRelMapper.selectListByUserId(userId); + } + + @Override + public MemberUserTenantRelDO getUserTenantRel(Long userId, Long tenantId) { + return memberUserTenantRelMapper.selectByUserIdAndTenantId(userId, tenantId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void assignUserToTenant(Long userId, Long tenantId, Boolean isDefault) { + // 校验租户是否存在 + TenantDO tenant = tenantMapper.selectById(tenantId); + if (tenant == null) { + throw exception(TENANT_NOT_EXISTS); + } + + // 检查关联是否已存在,避免重复创建 + MemberUserTenantRelDO existRel = memberUserTenantRelMapper.selectByUserIdAndTenantId(userId, tenantId); + if (existRel != null) { + return; + } + + // 如果设置为默认租户,需要清除其他默认租户标记 + if (Boolean.TRUE.equals(isDefault)) { + clearDefaultTenant(userId); + } + + // 创建新的关联关系 + MemberUserTenantRelDO rel = new MemberUserTenantRelDO(); + rel.setUserId(userId); + rel.setTenantId(tenantId); + rel.setDefaultTenant(isDefault != null ? isDefault : false); + memberUserTenantRelMapper.insert(rel); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void removeUserFromTenant(Long userId, Long tenantId) { + memberUserTenantRelMapper.deleteByUserIdAndTenantId(userId, tenantId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void batchAssignUserToTenants(Long userId, List tenantIds) { + // 先删除该用户的所有租户关联 + memberUserTenantRelMapper.deleteByUserId(userId); + + // 如果租户列表为空,直接返回 + if (CollUtil.isEmpty(tenantIds)) { + return; + } + + // 批量创建新的关联关系 + for (Long tenantId : tenantIds) { + // 校验租户是否存在 + TenantDO tenant = tenantMapper.selectById(tenantId); + if (tenant == null) { + throw exception(TENANT_NOT_EXISTS); + } + + // 创建关联关系 + MemberUserTenantRelDO rel = new MemberUserTenantRelDO(); + rel.setUserId(userId); + rel.setTenantId(tenantId); + rel.setDefaultTenant(false); + memberUserTenantRelMapper.insert(rel); + } + } + + /** + * 清除用户的默认租户标记 + * + * @param userId 用户ID + */ + private void clearDefaultTenant(Long userId) { + List rels = memberUserTenantRelMapper.selectListByUserId(userId); + for (MemberUserTenantRelDO rel : rels) { + if (Boolean.TRUE.equals(rel.getDefaultTenant())) { + rel.setDefaultTenant(false); + memberUserTenantRelMapper.updateById(rel); + } + } + } +}