用户租户体系调整,用户支持多租户数据

master
zzy 2026-04-20 23:53:52 +08:00
parent 4ce24a8b65
commit c01d68aec8
16 changed files with 542 additions and 116 deletions

View File

@ -0,0 +1,59 @@
package com.fjrcloud.community.module.system.controller.admin.auth;
import com.fjrcloud.community.framework.common.enums.UserTypeEnum;
import com.fjrcloud.community.framework.common.pojo.CommonResult;
import com.fjrcloud.community.framework.tenant.core.util.TenantUtils;
import com.fjrcloud.community.module.system.controller.admin.auth.vo.SwitchTenantReqVO;
import com.fjrcloud.community.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.fjrcloud.community.module.system.dal.dataobject.user.UserTenantRelDO;
import com.fjrcloud.community.module.system.enums.ErrorCodeConstants;
import com.fjrcloud.community.module.system.enums.oauth2.OAuth2ClientConstants;
import com.fjrcloud.community.module.system.service.oauth2.OAuth2TokenService;
import com.fjrcloud.community.module.system.service.usertenant.UserTenantRelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import static com.fjrcloud.community.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.fjrcloud.community.framework.common.pojo.CommonResult.success;
import static com.fjrcloud.community.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 认证 - 租户切换")
@RestController
@RequestMapping("/system/auth")
@Validated
public class AuthTenantSwitchController {
@Resource
private UserTenantRelService userTenantRelService;
@Resource
private OAuth2TokenService oauth2TokenService;
@PostMapping("/switch-tenant")
@Operation(summary = "切换租户")
public CommonResult<String> switchTenant(@Valid @RequestBody SwitchTenantReqVO reqVO) {
Long userId = getLoginUserId();
UserTenantRelDO rel = userTenantRelService.getUserTenantRel(userId, reqVO.getTenantId());
if (rel == null) {
throw exception(ErrorCodeConstants.USER_TENANT_REL_NOT_EXISTS);
}
String newToken = TenantUtils.execute(reqVO.getTenantId(), () -> {
OAuth2AccessTokenDO newAccessToken = oauth2TokenService.createAccessToken(
userId, UserTypeEnum.ADMIN.getValue(),
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
return newAccessToken.getAccessToken();
});
return success(newToken);
}
}

View File

@ -7,6 +7,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 登录 Response VO") @Schema(description = "管理后台 - 登录 Response VO")
@Data @Data
@ -27,4 +28,7 @@ public class AuthLoginRespVO {
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime; private LocalDateTime expiresTime;
@Schema(description = "用户可访问的租户列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<TenantInfoVO> tenants;
} }

View File

@ -0,0 +1,15 @@
package com.fjrcloud.community.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 切换租户 Request VO")
@Data
public class SwitchTenantReqVO {
@Schema(description = "租户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "租户ID不能为空")
private Long tenantId;
}

View File

@ -0,0 +1,24 @@
package com.fjrcloud.community.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 租户信息 VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TenantInfoVO {
@Schema(description = "租户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long tenantId;
@Schema(description = "租户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "XX小区")
private String tenantName;
@Schema(description = "是否默认租户", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean isDefault;
}

View File

@ -0,0 +1,83 @@
package com.fjrcloud.community.module.system.controller.admin.usertenant;
import com.fjrcloud.community.framework.common.pojo.CommonResult;
import com.fjrcloud.community.module.system.controller.admin.auth.vo.TenantInfoVO;
import com.fjrcloud.community.module.system.controller.admin.usertenant.vo.UserTenantAssignReqVO;
import com.fjrcloud.community.module.system.controller.admin.usertenant.vo.UserTenantBatchAssignReqVO;
import com.fjrcloud.community.module.system.controller.admin.usertenant.vo.UserTenantRespVO;
import com.fjrcloud.community.module.system.dal.dataobject.user.UserTenantRelDO;
import com.fjrcloud.community.module.system.dal.mysql.tenant.TenantMapper;
import com.fjrcloud.community.module.system.service.usertenant.UserTenantRelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;
import static com.fjrcloud.community.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 用户租户关联")
@RestController
@RequestMapping("/system/user-tenant")
@Validated
public class UserTenantRelController {
@Resource
private UserTenantRelService userTenantRelService;
@Resource
private TenantMapper tenantMapper;
@PostMapping("/assign")
@Operation(summary = "分配用户到租户")
@PreAuthorize("@ss.hasPermission('system:user-tenant:assign')")
public CommonResult<Boolean> assignUserToTenant(@Valid @RequestBody UserTenantAssignReqVO reqVO) {
userTenantRelService.assignUserToTenant(reqVO.getUserId(), reqVO.getTenantId(), reqVO.getIsDefault());
return success(true);
}
@DeleteMapping("/remove")
@Operation(summary = "移除用户租户关联")
@Parameter(name = "userId", description = "用户ID", required = true, example = "1")
@Parameter(name = "tenantId", description = "租户ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('system:user-tenant:remove')")
public CommonResult<Boolean> removeUserFromTenant(@RequestParam("userId") Long userId,
@RequestParam("tenantId") Long tenantId) {
userTenantRelService.removeUserFromTenant(userId, tenantId);
return success(true);
}
@PostMapping("/batch-assign")
@Operation(summary = "批量分配用户到多个租户")
@PreAuthorize("@ss.hasPermission('system:user-tenant:assign')")
public CommonResult<Boolean> batchAssignUserToTenants(@Valid @RequestBody UserTenantBatchAssignReqVO reqVO) {
userTenantRelService.batchAssignUserToTenants(reqVO.getUserId(), reqVO.getTenantIds());
return success(true);
}
@GetMapping("/list-by-user")
@Operation(summary = "获取用户的租户列表")
@Parameter(name = "userId", description = "用户ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('system:user-tenant:query')")
public CommonResult<List<UserTenantRespVO>> getUserTenants(@RequestParam("userId") Long userId) {
List<TenantInfoVO> tenantInfos = userTenantRelService.getUserTenants(userId);
List<UserTenantRespVO> result = tenantInfos.stream().map(info -> {
UserTenantRelDO rel = userTenantRelService.getUserTenantRel(userId, info.getTenantId());
return UserTenantRespVO.builder()
.id(rel != null ? rel.getId() : null)
.userId(userId)
.tenantId(info.getTenantId())
.tenantName(info.getTenantName())
.isDefault(info.getIsDefault())
.createTime(rel != null ? rel.getCreateTime() : null)
.build();
}).collect(Collectors.toList());
return success(result);
}
}

View File

@ -0,0 +1,22 @@
package com.fjrcloud.community.module.system.controller.admin.usertenant.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 分配用户到租户 Request VO")
@Data
public class UserTenantAssignReqVO {
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "用户ID不能为空")
private Long userId;
@Schema(description = "租户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "租户ID不能为空")
private Long tenantId;
@Schema(description = "是否默认租户", example = "true")
private Boolean isDefault;
}

View File

@ -0,0 +1,21 @@
package com.fjrcloud.community.module.system.controller.admin.usertenant.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 批量分配用户到租户 Request VO")
@Data
public class UserTenantBatchAssignReqVO {
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "用户ID不能为空")
private Long userId;
@Schema(description = "租户ID列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
@NotEmpty(message = "租户ID列表不能为空")
private List<Long> tenantIds;
}

View File

@ -0,0 +1,35 @@
package com.fjrcloud.community.module.system.controller.admin.usertenant.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 用户租户关联 Response VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserTenantRespVO {
@Schema(description = "关联ID", example = "1")
private Long id;
@Schema(description = "用户ID", example = "1")
private Long userId;
@Schema(description = "租户ID", example = "1")
private Long tenantId;
@Schema(description = "租户名称", example = "XX小区")
private String tenantName;
@Schema(description = "是否默认租户", example = "true")
private Boolean isDefault;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,27 @@
package com.fjrcloud.community.module.system.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.*;
@TableName("system_user_tenant_rel")
@KeySequence("system_user_tenant_rel_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserTenantRelDO extends BaseDO {
@TableId
private Long id;
private Long userId;
private Long tenantId;
private Boolean defaultTenant;
}

View File

@ -0,0 +1,28 @@
package com.fjrcloud.community.module.system.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.system.dal.dataobject.user.UserTenantRelDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserTenantRelMapper extends BaseMapperX<UserTenantRelDO> {
default List<UserTenantRelDO> selectListByUserId(Long userId) {
return selectList(UserTenantRelDO::getUserId, userId);
}
default UserTenantRelDO selectByUserIdAndTenantId(Long userId, Long tenantId) {
return selectOne(new LambdaQueryWrapperX<UserTenantRelDO>()
.eq(UserTenantRelDO::getUserId, userId)
.eq(UserTenantRelDO::getTenantId, tenantId));
}
default UserTenantRelDO selectDefaultByUserId(Long userId) {
return selectOne(new LambdaQueryWrapperX<UserTenantRelDO>()
.eq(UserTenantRelDO::getUserId, userId)
.eq(UserTenantRelDO::getDefaultTenant, true));
}
}

View File

@ -44,7 +44,11 @@ public interface ErrorCodeConstants {
ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用");
ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})"); ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})");
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_010, "手机号未注册用户");
ErrorCode USER_TENANT_REL_NOT_EXISTS = new ErrorCode(1_002_011, "用户没有该租户的访问权限");
ErrorCode USER_TENANT_REL_EXISTS = new ErrorCode(1_002_012, "用户已拥有该租户的访问权限");
ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
// ========== 部门模块 1-002-004-000 ========== // ========== 部门模块 1-002-004-000 ==========

View File

@ -3,24 +3,19 @@ package com.fjrcloud.community.module.system.enums.logger;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/**
*
*/
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum LoginResultEnum { public enum LoginResultEnum {
SUCCESS(0), // 成功 SUCCESS(0),
BAD_CREDENTIALS(10), // 账号或密码不正确 BAD_CREDENTIALS(10),
USER_DISABLED(20), // 用户被禁用 USER_DISABLED(20),
CAPTCHA_NOT_FOUND(30), // 图片验证码不存在 CAPTCHA_NOT_FOUND(30),
CAPTCHA_CODE_ERROR(31), // 图片验证码不正确 CAPTCHA_CODE_ERROR(31),
TENANT_PERMISSION_DENIED(40),
; ;
/**
*
*/
private final Integer result; private final Integer result;
} }

View File

@ -11,6 +11,8 @@ import com.fjrcloud.community.framework.common.util.object.BeanUtils;
import com.fjrcloud.community.framework.common.util.servlet.ServletUtils; import com.fjrcloud.community.framework.common.util.servlet.ServletUtils;
import com.fjrcloud.community.framework.common.util.validation.ValidationUtils; import com.fjrcloud.community.framework.common.util.validation.ValidationUtils;
import com.fjrcloud.community.framework.datapermission.core.annotation.DataPermission; import com.fjrcloud.community.framework.datapermission.core.annotation.DataPermission;
import com.fjrcloud.community.framework.tenant.core.context.TenantContextHolder;
import com.fjrcloud.community.framework.tenant.core.util.TenantUtils;
import com.fjrcloud.community.module.system.api.logger.dto.LoginLogCreateReqDTO; import com.fjrcloud.community.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.fjrcloud.community.module.system.api.sms.SmsCodeApi; import com.fjrcloud.community.module.system.api.sms.SmsCodeApi;
import com.fjrcloud.community.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import com.fjrcloud.community.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
@ -20,6 +22,7 @@ import com.fjrcloud.community.module.system.controller.admin.auth.vo.*;
import com.fjrcloud.community.module.system.convert.auth.AuthConvert; import com.fjrcloud.community.module.system.convert.auth.AuthConvert;
import com.fjrcloud.community.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import com.fjrcloud.community.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.fjrcloud.community.module.system.dal.dataobject.user.AdminUserDO; import com.fjrcloud.community.module.system.dal.dataobject.user.AdminUserDO;
import com.fjrcloud.community.module.system.dal.dataobject.user.UserTenantRelDO;
import com.fjrcloud.community.module.system.enums.logger.LoginLogTypeEnum; import com.fjrcloud.community.module.system.enums.logger.LoginLogTypeEnum;
import com.fjrcloud.community.module.system.enums.logger.LoginResultEnum; import com.fjrcloud.community.module.system.enums.logger.LoginResultEnum;
import com.fjrcloud.community.module.system.enums.oauth2.OAuth2ClientConstants; import com.fjrcloud.community.module.system.enums.oauth2.OAuth2ClientConstants;
@ -29,6 +32,7 @@ import com.fjrcloud.community.module.system.service.member.MemberService;
import com.fjrcloud.community.module.system.service.oauth2.OAuth2TokenService; import com.fjrcloud.community.module.system.service.oauth2.OAuth2TokenService;
import com.fjrcloud.community.module.system.service.social.SocialUserService; import com.fjrcloud.community.module.system.service.social.SocialUserService;
import com.fjrcloud.community.module.system.service.user.AdminUserService; import com.fjrcloud.community.module.system.service.user.AdminUserService;
import com.fjrcloud.community.module.system.service.usertenant.UserTenantRelService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -38,17 +42,13 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Validator; import javax.validation.Validator;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import static com.fjrcloud.community.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.fjrcloud.community.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.fjrcloud.community.framework.common.util.servlet.ServletUtils.getClientIP; import static com.fjrcloud.community.framework.common.util.servlet.ServletUtils.getClientIP;
import static com.fjrcloud.community.module.system.enums.ErrorCodeConstants.*; import static com.fjrcloud.community.module.system.enums.ErrorCodeConstants.*;
/**
* Auth Service
*
* @author
*/
@Service @Service
@Slf4j @Slf4j
public class AdminAuthServiceImpl implements AdminAuthService { public class AdminAuthServiceImpl implements AdminAuthService {
@ -69,12 +69,11 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private CaptchaService captchaService; private CaptchaService captchaService;
@Resource @Resource
private SmsCodeApi smsCodeApi; private SmsCodeApi smsCodeApi;
@Resource
private UserTenantRelService userTenantRelService;
/**
* true
*/
@Value("${fjrcloud.captcha.enable:true}") @Value("${fjrcloud.captcha.enable:true}")
@Setter // 为了单测:开启或者关闭验证码 @Setter
private Boolean captchaEnable; private Boolean captchaEnable;
@Override @Override
@ -112,13 +111,28 @@ public class AdminAuthServiceImpl implements AdminAuthService {
socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
} }
// 创建 Token 令牌,记录登录日志
Long tenantId = TenantContextHolder.getTenantId();
if (tenantId == null) {
createLoginLog(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
validateUserTenantPermission(user.getId(), tenantId);
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
} }
private void validateUserTenantPermission(Long userId, Long tenantId) {
UserTenantRelDO rel = userTenantRelService.getUserTenantRel(userId, tenantId);
if (rel == null) {
createLoginLog(userId, null, LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.TENANT_PERMISSION_DENIED);
throw exception(USER_TENANT_REL_NOT_EXISTS);
}
}
@Override @Override
public void sendSmsCode(AuthSmsSendReqVO reqVO) { public void sendSmsCode(AuthSmsSendReqVO reqVO) {
// 如果是重置密码场景,需要校验图形验证码是否正确
if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) { if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) {
ResponseModel response = doValidateCaptcha(reqVO); ResponseModel response = doValidateCaptcha(reqVO);
if (!response.isSuccess()) { if (!response.isSuccess()) {
@ -145,7 +159,14 @@ public class AdminAuthServiceImpl implements AdminAuthService {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
// 创建 Token 令牌,记录登录日志 Long tenantId = TenantContextHolder.getTenantId();
if (tenantId == null) {
createLoginLog(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, LoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
validateUserTenantPermission(user.getId(), tenantId);
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
} }
@ -183,7 +204,11 @@ public class AdminAuthServiceImpl implements AdminAuthService {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
// 创建 Token 令牌,记录登录日志 Long tenantId = TenantContextHolder.getTenantId();
if (tenantId != null) {
validateUserTenantPermission(user.getId(), tenantId);
}
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
} }
@ -210,13 +235,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
} }
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
// 插入登陆日志
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
// 创建访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), Long tenantId = TenantContextHolder.getTenantId();
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
// 构建返回结果 OAuth2AccessTokenDO accessTokenDO;
return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class); if (tenantId != null) {
accessTokenDO = TenantUtils.execute(tenantId, () ->
oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null));
} else {
accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
}
List<TenantInfoVO> tenants = userTenantRelService.getUserTenants(userId);
AuthLoginRespVO respVO = BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class);
respVO.setTenants(tenants);
return respVO;
} }
@Override @Override
@ -227,12 +264,10 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Override @Override
public void logout(String token, Integer logType) { public void logout(String token, Integer logType) {
// 删除访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
if (accessTokenDO == null) { if (accessTokenDO == null) {
return; return;
} }
// 删除成功,则记录登出日志
createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType);
} }
@ -267,20 +302,16 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Override @Override
public AuthLoginRespVO register(AuthRegisterReqVO registerReqVO) { public AuthLoginRespVO register(AuthRegisterReqVO registerReqVO) {
// 1. 校验验证码
validateCaptcha(registerReqVO); validateCaptcha(registerReqVO);
// 2. 校验用户名是否已存在
Long userId = userService.registerUser(registerReqVO); Long userId = userService.registerUser(registerReqVO);
// 3. 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
} }
@VisibleForTesting @VisibleForTesting
void validateCaptcha(AuthRegisterReqVO reqVO) { void validateCaptcha(AuthRegisterReqVO reqVO) {
ResponseModel response = doValidateCaptcha(reqVO); ResponseModel response = doValidateCaptcha(reqVO);
// 验证不通过
if (!response.isSuccess()) { if (!response.isSuccess()) {
throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg());
} }

View File

@ -12,6 +12,7 @@ import com.fjrcloud.community.framework.common.util.collection.CollectionUtils;
import com.fjrcloud.community.framework.common.util.object.BeanUtils; import com.fjrcloud.community.framework.common.util.object.BeanUtils;
import com.fjrcloud.community.framework.common.util.validation.ValidationUtils; import com.fjrcloud.community.framework.common.util.validation.ValidationUtils;
import com.fjrcloud.community.framework.datapermission.core.util.DataPermissionUtils; import com.fjrcloud.community.framework.datapermission.core.util.DataPermissionUtils;
import com.fjrcloud.community.framework.tenant.core.context.TenantContextHolder;
import com.fjrcloud.community.module.infra.api.config.ConfigApi; import com.fjrcloud.community.module.infra.api.config.ConfigApi;
import com.fjrcloud.community.module.system.controller.admin.auth.vo.AuthRegisterReqVO; import com.fjrcloud.community.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
import com.fjrcloud.community.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; import com.fjrcloud.community.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
@ -30,6 +31,7 @@ import com.fjrcloud.community.module.system.service.dept.PostService;
import com.fjrcloud.community.module.system.service.oauth2.OAuth2TokenService; import com.fjrcloud.community.module.system.service.oauth2.OAuth2TokenService;
import com.fjrcloud.community.module.system.service.permission.PermissionService; import com.fjrcloud.community.module.system.service.permission.PermissionService;
import com.fjrcloud.community.module.system.service.tenant.TenantService; import com.fjrcloud.community.module.system.service.tenant.TenantService;
import com.fjrcloud.community.module.system.service.usertenant.UserTenantRelService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.service.impl.DiffParseFunction;
@ -78,10 +80,14 @@ public class AdminUserServiceImpl implements AdminUserService {
@Resource @Resource
@Lazy // 延迟,避免循环依赖报错 @Lazy // 延迟,避免循环依赖报错
private TenantService tenantService; private TenantService tenantService;
@Resource @Resource
@Lazy // 懒加载,避免循环依赖 @Lazy // 懒加载,避免循环依赖
private OAuth2TokenService oauth2TokenService; private OAuth2TokenService oauth2TokenService;
@Resource
private UserTenantRelService userTenantRelService;
@Resource @Resource
private UserPostMapper userPostMapper; private UserPostMapper userPostMapper;
@ -93,52 +99,50 @@ public class AdminUserServiceImpl implements AdminUserService {
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}", @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}",
success = SYSTEM_USER_CREATE_SUCCESS) success = SYSTEM_USER_CREATE_SUCCESS)
public Long createUser(UserSaveReqVO createReqVO) { public Long createUser(UserSaveReqVO createReqVO) {
// 1.1 校验账户配合
tenantService.handleTenantInfo(tenant -> { tenantService.handleTenantInfo(tenant -> {
long count = userMapper.selectCount(); long count = userMapper.selectCount();
if (count >= tenant.getAccountCount()) { if (count >= tenant.getAccountCount()) {
throw exception(USER_COUNT_MAX, tenant.getAccountCount()); throw exception(USER_COUNT_MAX, tenant.getAccountCount());
} }
}); });
// 1.2 校验正确性
validateUserForCreateOrUpdate(null, createReqVO.getUsername(), validateUserForCreateOrUpdate(null, createReqVO.getUsername(),
createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds()); createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds());
// 2.1 插入用户
AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class); AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 user.setStatus(CommonStatusEnum.ENABLE.getStatus());
user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码 user.setPassword(encodePassword(createReqVO.getPassword()));
userMapper.insert(user); userMapper.insert(user);
// 2.2 插入关联岗位
if (CollectionUtil.isNotEmpty(user.getPostIds())) { if (CollectionUtil.isNotEmpty(user.getPostIds())) {
userPostMapper.insertBatch(convertList(user.getPostIds(), userPostMapper.insertBatch(convertList(user.getPostIds(),
postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId))); postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId)));
} }
// 3. 记录操作日志上下文 Long tenantId = TenantContextHolder.getTenantId();
if (tenantId != null) {
userTenantRelService.assignUserToTenant(user.getId(), tenantId, true);
}
LogRecordContext.putVariable("user", user); LogRecordContext.putVariable("user", user);
return user.getId(); return user.getId();
} }
@Override @Override
public Long registerUser(AuthRegisterReqVO registerReqVO) { public Long registerUser(AuthRegisterReqVO registerReqVO) {
// 1.1 校验是否开启注册
if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) { if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) {
throw exception(USER_REGISTER_DISABLED); throw exception(USER_REGISTER_DISABLED);
} }
// 1.2 校验账户配合
tenantService.handleTenantInfo(tenant -> { tenantService.handleTenantInfo(tenant -> {
long count = userMapper.selectCount(); long count = userMapper.selectCount();
if (count >= tenant.getAccountCount()) { if (count >= tenant.getAccountCount()) {
throw exception(USER_COUNT_MAX, tenant.getAccountCount()); throw exception(USER_COUNT_MAX, tenant.getAccountCount());
} }
}); });
// 1.3 校验正确性
validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null); validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null);
// 2. 插入用户
AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class); AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class);
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 user.setStatus(CommonStatusEnum.ENABLE.getStatus());
user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码 user.setPassword(encodePassword(registerReqVO.getPassword()));
userMapper.insert(user); userMapper.insert(user);
return user.getId(); return user.getId();
} }
@ -148,18 +152,14 @@ public class AdminUserServiceImpl implements AdminUserService {
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
success = SYSTEM_USER_UPDATE_SUCCESS) success = SYSTEM_USER_UPDATE_SUCCESS)
public void updateUser(UserSaveReqVO updateReqVO) { public void updateUser(UserSaveReqVO updateReqVO) {
updateReqVO.setPassword(null); // 特殊:此处不更新密码 updateReqVO.setPassword(null);
// 1. 校验正确性
AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(), AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds()); updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds());
// 2.1 更新用户
AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class); AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);
userMapper.updateById(updateObj); userMapper.updateById(updateObj);
// 2.2 更新岗位
updateUserPost(updateReqVO, updateObj); updateUserPost(updateReqVO, updateObj);
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class)); LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class));
LogRecordContext.putVariable("user", oldUser); LogRecordContext.putVariable("user", oldUser);
} }
@ -167,11 +167,9 @@ public class AdminUserServiceImpl implements AdminUserService {
private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) { private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) {
Long userId = reqVO.getId(); Long userId = reqVO.getId();
Set<Long> dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId); Set<Long> dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId);
// 计算新增和删除的岗位编号
Set<Long> postIds = CollUtil.emptyIfNull(updateObj.getPostIds()); Set<Long> postIds = CollUtil.emptyIfNull(updateObj.getPostIds());
Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds); Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds);
Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds); Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds);
// 执行新增和删除。对于已经授权的岗位,不用做任何处理
if (!CollectionUtil.isEmpty(createPostIds)) { if (!CollectionUtil.isEmpty(createPostIds)) {
userPostMapper.insertBatch(convertList(createPostIds, userPostMapper.insertBatch(convertList(createPostIds,
postId -> new UserPostDO().setUserId(userId).setPostId(postId))); postId -> new UserPostDO().setUserId(userId).setPostId(postId)));
@ -188,21 +186,17 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override @Override
public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) { public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) {
// 校验正确性
validateUserExists(id); validateUserExists(id);
validateEmailUnique(id, reqVO.getEmail()); validateEmailUnique(id, reqVO.getEmail());
validateMobileUnique(id, reqVO.getMobile()); validateMobileUnique(id, reqVO.getMobile());
// 执行更新
userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id)); userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id));
} }
@Override @Override
public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) { public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) {
// 校验旧密码密码
validateOldPassword(id, reqVO.getOldPassword()); validateOldPassword(id, reqVO.getOldPassword());
// 执行更新
AdminUserDO updateObj = new AdminUserDO().setId(id); AdminUserDO updateObj = new AdminUserDO().setId(id);
updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码 updateObj.setPassword(encodePassword(reqVO.getNewPassword()));
userMapper.updateById(updateObj); userMapper.updateById(updateObj);
} }
@ -210,31 +204,25 @@ public class AdminUserServiceImpl implements AdminUserService {
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}", @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}",
success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS) success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS)
public void updateUserPassword(Long id, String password) { public void updateUserPassword(Long id, String password) {
// 1. 校验用户存在
AdminUserDO user = validateUserExists(id); AdminUserDO user = validateUserExists(id);
// 2. 更新密码
AdminUserDO updateObj = new AdminUserDO(); AdminUserDO updateObj = new AdminUserDO();
updateObj.setId(id); updateObj.setId(id);
updateObj.setPassword(encodePassword(password)); // 加密密码 updateObj.setPassword(encodePassword(password));
userMapper.updateById(updateObj); userMapper.updateById(updateObj);
// 3. 记录操作日志上下文
LogRecordContext.putVariable("user", user); LogRecordContext.putVariable("user", user);
LogRecordContext.putVariable("newPassword", updateObj.getPassword()); LogRecordContext.putVariable("newPassword", updateObj.getPassword());
} }
@Override @Override
public void updateUserStatus(Long id, Integer status) { public void updateUserStatus(Long id, Integer status) {
// 校验用户存在
validateUserExists(id); validateUserExists(id);
// 更新状态
AdminUserDO updateObj = new AdminUserDO(); AdminUserDO updateObj = new AdminUserDO();
updateObj.setId(id); updateObj.setId(id);
updateObj.setStatus(status); updateObj.setStatus(status);
userMapper.updateById(updateObj); userMapper.updateById(updateObj);
// 如果是禁用用户,则删除其 Token 信息
if (CommonStatusEnum.isDisable(status)) { if (CommonStatusEnum.isDisable(status)) {
oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue()); oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue());
} }
@ -245,27 +233,20 @@ public class AdminUserServiceImpl implements AdminUserService {
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}", @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
success = SYSTEM_USER_DELETE_SUCCESS) success = SYSTEM_USER_DELETE_SUCCESS)
public void deleteUser(Long id) { public void deleteUser(Long id) {
// 1. 校验用户存在
AdminUserDO user = validateUserExists(id); AdminUserDO user = validateUserExists(id);
// 2.1 删除用户
userMapper.deleteById(id); userMapper.deleteById(id);
// 2.2 删除用户关联数据
permissionService.processUserDeleted(id); permissionService.processUserDeleted(id);
// 2.2 删除用户岗位
userPostMapper.deleteByUserId(id); userPostMapper.deleteByUserId(id);
// 3. 记录操作日志上下文
LogRecordContext.putVariable("user", user); LogRecordContext.putVariable("user", user);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void deleteUserList(List<Long> ids) { public void deleteUserList(List<Long> ids) {
// 1. 批量删除用户
userMapper.deleteByIds(ids); userMapper.deleteByIds(ids);
// 2. 批量删除用户关联数据
ids.forEach(id -> { ids.forEach(id -> {
permissionService.processUserDeleted(id); permissionService.processUserDeleted(id);
userPostMapper.deleteByUserId(id); userPostMapper.deleteByUserId(id);
@ -284,7 +265,6 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override @Override
public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) { public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) {
// 如果有角色编号,查询角色对应的用户编号
Set<Long> userIds = null; Set<Long> userIds = null;
if (reqVO.getRoleId() != null) { if (reqVO.getRoleId() != null) {
userIds = permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())); userIds = permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId()));
@ -293,7 +273,6 @@ public class AdminUserServiceImpl implements AdminUserService {
} }
} }
// 分页查询
return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds); return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds);
} }
@ -335,10 +314,8 @@ public class AdminUserServiceImpl implements AdminUserService {
if (CollUtil.isEmpty(ids)) { if (CollUtil.isEmpty(ids)) {
return; return;
} }
// 获得岗位信息
List<AdminUserDO> users = userMapper.selectByIds(ids); List<AdminUserDO> users = userMapper.selectByIds(ids);
Map<Long, AdminUserDO> userMap = CollectionUtils.convertMap(users, AdminUserDO::getId); Map<Long, AdminUserDO> userMap = CollectionUtils.convertMap(users, AdminUserDO::getId);
// 校验
ids.forEach(id -> { ids.forEach(id -> {
AdminUserDO user = userMap.get(id); AdminUserDO user = userMap.get(id);
if (user == null) { if (user == null) {
@ -355,36 +332,23 @@ public class AdminUserServiceImpl implements AdminUserService {
return userMapper.selectListByNickname(nickname); return userMapper.selectListByNickname(nickname);
} }
/**
*
*
* @param deptId
* @return
*/
private Set<Long> getDeptCondition(Long deptId) { private Set<Long> getDeptCondition(Long deptId) {
if (deptId == null) { if (deptId == null) {
return Collections.emptySet(); return Collections.emptySet();
} }
Set<Long> deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId); Set<Long> deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId);
deptIds.add(deptId); // 包括自身 deptIds.add(deptId);
return deptIds; return deptIds;
} }
private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
Long deptId, Set<Long> postIds) { Long deptId, Set<Long> postIds) {
// 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
return DataPermissionUtils.executeIgnore(() -> { return DataPermissionUtils.executeIgnore(() -> {
// 校验用户存在
AdminUserDO user = validateUserExists(id); AdminUserDO user = validateUserExists(id);
// 校验用户名唯一
validateUsernameUnique(id, username); validateUsernameUnique(id, username);
// 校验手机号唯一
validateMobileUnique(id, mobile); validateMobileUnique(id, mobile);
// 校验邮箱唯一
validateEmailUnique(id, email); validateEmailUnique(id, email);
// 校验部门处于开启状态
deptService.validateDeptList(CollectionUtils.singleton(deptId)); deptService.validateDeptList(CollectionUtils.singleton(deptId));
// 校验岗位处于开启状态
postService.validatePostList(postIds); postService.validatePostList(postIds);
return user; return user;
}); });
@ -411,7 +375,6 @@ public class AdminUserServiceImpl implements AdminUserService {
if (user == null) { if (user == null) {
return; return;
} }
// 如果 id 为空,说明不用比较是否为相同 id 的用户
if (id == null) { if (id == null) {
throw exception(USER_USERNAME_EXISTS); throw exception(USER_USERNAME_EXISTS);
} }
@ -429,7 +392,6 @@ public class AdminUserServiceImpl implements AdminUserService {
if (user == null) { if (user == null) {
return; return;
} }
// 如果 id 为空,说明不用比较是否为相同 id 的用户
if (id == null) { if (id == null) {
throw exception(USER_EMAIL_EXISTS); throw exception(USER_EMAIL_EXISTS);
} }
@ -447,7 +409,6 @@ public class AdminUserServiceImpl implements AdminUserService {
if (user == null) { if (user == null) {
return; return;
} }
// 如果 id 为空,说明不用比较是否为相同 id 的用户
if (id == null) { if (id == null) {
throw exception(USER_MOBILE_EXISTS); throw exception(USER_MOBILE_EXISTS);
} }
@ -456,11 +417,6 @@ public class AdminUserServiceImpl implements AdminUserService {
} }
} }
/**
*
* @param id id
* @param oldPassword
*/
@VisibleForTesting @VisibleForTesting
void validateOldPassword(Long id, String oldPassword) { void validateOldPassword(Long id, String oldPassword) {
AdminUserDO user = userMapper.selectById(id); AdminUserDO user = userMapper.selectById(id);
@ -473,25 +429,21 @@ public class AdminUserServiceImpl implements AdminUserService {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 @Transactional(rollbackFor = Exception.class)
public UserImportRespVO importUserList(List<UserImportExcelVO> importUsers, boolean isUpdateSupport) { public UserImportRespVO importUserList(List<UserImportExcelVO> importUsers, boolean isUpdateSupport) {
// 1.1 参数校验
if (CollUtil.isEmpty(importUsers)) { if (CollUtil.isEmpty(importUsers)) {
throw exception(USER_IMPORT_LIST_IS_EMPTY); throw exception(USER_IMPORT_LIST_IS_EMPTY);
} }
// 1.2 初始化密码不能为空
String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY); String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY);
if (StrUtil.isEmpty(initPassword)) { if (StrUtil.isEmpty(initPassword)) {
throw exception(USER_IMPORT_INIT_PASSWORD); throw exception(USER_IMPORT_INIT_PASSWORD);
} }
// 2. 遍历,逐个创建 or 更新
UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>()) UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>())
.updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
AtomicInteger index = new AtomicInteger(1); AtomicInteger index = new AtomicInteger(1);
importUsers.forEach(importUser -> { importUsers.forEach(importUser -> {
int currentIndex = index.getAndIncrement(); int currentIndex = index.getAndIncrement();
// 2.1.1 校验字段是否符合要求
try { try {
ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword)); ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword));
} catch (ConstraintViolationException ex) { } catch (ConstraintViolationException ex) {
@ -499,7 +451,6 @@ public class AdminUserServiceImpl implements AdminUserService {
respVO.getFailureUsernames().put(key, ex.getMessage()); respVO.getFailureUsernames().put(key, ex.getMessage());
return; return;
} }
// 2.1.2 校验,判断是否有不符合的原因
try { try {
validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(), validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
importUser.getDeptId(), null); importUser.getDeptId(), null);
@ -508,7 +459,6 @@ public class AdminUserServiceImpl implements AdminUserService {
return; return;
} }
// 2.2.1 判断如果不存在,在进行插入
AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername()); AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
if (existUser == null) { if (existUser == null) {
userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class) userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class)
@ -516,7 +466,6 @@ public class AdminUserServiceImpl implements AdminUserService {
respVO.getCreateUsernames().add(importUser.getUsername()); respVO.getCreateUsernames().add(importUser.getUsername());
return; return;
} }
// 2.2.2 如果存在,判断是否允许更新
if (!isUpdateSupport) { if (!isUpdateSupport) {
respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg()); respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg());
return; return;
@ -539,12 +488,6 @@ public class AdminUserServiceImpl implements AdminUserService {
return passwordEncoder.matches(rawPassword, encodedPassword); return passwordEncoder.matches(rawPassword, encodedPassword);
} }
/**
*
*
* @param password
* @return
*/
private String encodePassword(String password) { private String encodePassword(String password) {
return passwordEncoder.encode(password); return passwordEncoder.encode(password);
} }

View File

@ -0,0 +1,21 @@
package com.fjrcloud.community.module.system.service.usertenant;
import com.fjrcloud.community.module.system.controller.admin.auth.vo.TenantInfoVO;
import com.fjrcloud.community.module.system.dal.dataobject.user.UserTenantRelDO;
import java.util.List;
public interface UserTenantRelService {
List<TenantInfoVO> getUserTenants(Long userId);
UserTenantRelDO getUserTenantRel(Long userId, Long tenantId);
UserTenantRelDO getDefaultTenant(Long userId);
void assignUserToTenant(Long userId, Long tenantId, Boolean isDefault);
void removeUserFromTenant(Long userId, Long tenantId);
void batchAssignUserToTenants(Long userId, List<Long> tenantIds);
}

View File

@ -0,0 +1,114 @@
package com.fjrcloud.community.module.system.service.usertenant;
import cn.hutool.core.collection.CollUtil;
import com.fjrcloud.community.framework.common.exception.ServiceException;
import com.fjrcloud.community.module.system.controller.admin.auth.vo.TenantInfoVO;
import com.fjrcloud.community.module.system.dal.dataobject.tenant.TenantDO;
import com.fjrcloud.community.module.system.dal.dataobject.user.UserTenantRelDO;
import com.fjrcloud.community.module.system.dal.mysql.tenant.TenantMapper;
import com.fjrcloud.community.module.system.dal.mysql.user.UserTenantRelMapper;
import com.fjrcloud.community.module.system.enums.ErrorCodeConstants;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static com.fjrcloud.community.framework.common.exception.util.ServiceExceptionUtil.exception;
@Service
public class UserTenantRelServiceImpl implements UserTenantRelService {
@Resource
private UserTenantRelMapper userTenantRelMapper;
@Resource
private TenantMapper tenantMapper;
@Override
public List<TenantInfoVO> getUserTenants(Long userId) {
List<UserTenantRelDO> rels = userTenantRelMapper.selectListByUserId(userId);
if (CollUtil.isEmpty(rels)) {
return Collections.emptyList();
}
return rels.stream().map(rel -> {
TenantDO tenant = tenantMapper.selectById(rel.getTenantId());
return TenantInfoVO.builder()
.tenantId(rel.getTenantId())
.tenantName(tenant != null ? tenant.getName() : "")
.isDefault(rel.getDefaultTenant())
.build();
}).collect(Collectors.toList());
}
@Override
public UserTenantRelDO getUserTenantRel(Long userId, Long tenantId) {
return userTenantRelMapper.selectByUserIdAndTenantId(userId, tenantId);
}
@Override
public UserTenantRelDO getDefaultTenant(Long userId) {
return userTenantRelMapper.selectDefaultByUserId(userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void assignUserToTenant(Long userId, Long tenantId, Boolean isDefault) {
TenantDO tenant = tenantMapper.selectById(tenantId);
if (tenant == null) {
throw exception(ErrorCodeConstants.TENANT_NOT_EXISTS);
}
UserTenantRelDO existRel = userTenantRelMapper.selectByUserIdAndTenantId(userId, tenantId);
if (existRel != null) {
throw exception(ErrorCodeConstants.USER_TENANT_REL_EXISTS);
}
if (Boolean.TRUE.equals(isDefault)) {
clearDefaultTenant(userId);
}
UserTenantRelDO rel = new UserTenantRelDO();
rel.setUserId(userId);
rel.setTenantId(tenantId);
rel.setDefaultTenant(isDefault != null ? isDefault : false);
userTenantRelMapper.insert(rel);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeUserFromTenant(Long userId, Long tenantId) {
UserTenantRelDO rel = userTenantRelMapper.selectByUserIdAndTenantId(userId, tenantId);
if (rel == null) {
return;
}
userTenantRelMapper.deleteById(rel.getId());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void batchAssignUserToTenants(Long userId, List<Long> tenantIds) {
if (CollUtil.isEmpty(tenantIds)) {
return;
}
for (Long tenantId : tenantIds) {
try {
assignUserToTenant(userId, tenantId, false);
} catch (ServiceException e) {
// 忽略已存在的关联
}
}
}
private void clearDefaultTenant(Long userId) {
UserTenantRelDO defaultRel = userTenantRelMapper.selectDefaultByUserId(userId);
if (defaultRel != null) {
defaultRel.setDefaultTenant(false);
userTenantRelMapper.updateById(defaultRel);
}
}
}