feat(tenant): 新增租户用户管理功能

- 在SysUserPost实体类中添加tenantId字段
- 新增SysTenantUserDTO用于租户用户数据传输
- 在SysTenantController中新增用户租户管理接口
- 实现租户用户的增删改查及分页查询功能
- 优化ValidateCodeFilter中的验证码校验逻辑
- 修复PigxUserDetailsService中空部门列表处理问题
This commit is contained in:
PIG AI committed 2025-07-04 12:01:27 +08:00
1 parent db332055ae
commit c8a8da7497
8 files changed
+312 -9

No files matched your search

@@ -133,13 +133,12 @@ public class ValidateCodeFilter extends OncePerRequestFilter {
throw new ValidateCodeException("验证码不合法");
}
String saveCode = codeObj;
if (StrUtil.isBlank(saveCode)) {
if (StrUtil.isBlank(codeObj)) {
RedisUtils.delete(key);
throw new ValidateCodeException("验证码不合法");
}
if (!StrUtil.equals(saveCode, code)) {
if (!StrUtil.equals(codeObj, code)) {
RedisUtils.delete(key);
throw new ValidateCodeException("验证码不合法");
}
@@ -28,7 +28,7 @@ public interface CacheConstants {
/**
* 用户信息缓存
*/
String USER_DETAILS = "user_details";
String USER_DETAILS = GLOBALLY + "user_details";
/**
* 移动端用户信息缓存
@@ -16,6 +16,7 @@
*/
package com.pig4cloud.pigx.common.security.service;
import cn.hutool.core.collection.CollUtil;
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
@@ -98,7 +99,11 @@ public interface PigxUserDetailsService extends UserDetailsService, Ordered {
// 构造security用户
return new PigxUser(info.getUserId(), info.getUsername(), info.getDeptList().get(0).getDeptId(), info.getPhone(), info.getAvatar(),
Long deptId = null;
if (CollUtil.isNotEmpty(info.getDeptList())) {
deptId = CollUtil.getFirst(info.getDeptList()).getDeptId();
}
return new PigxUser(info.getUserId(), info.getUsername(), deptId, info.getPhone(), info.getAvatar(),
info.getNickname(), info.getName(), info.getEmail(), info.getTenantId(),
SecurityConstants.BCRYPT + info.getPassword()
, true, true, UserTypeEnum.TOB.getStatus()
@@ -0,0 +1,38 @@
package com.pig4cloud.pigx.admin.api.dto;
import lombok.Data;
/**
* 系统租户用户数据传输对象
*
* @author lengleng
* @date 2025/06/30
*/
@Data
public class SysTenantUserDTO {
/**
* 租户ID
*/
private Long tenantId;
/**
* 用户ID数组
*/
private Long[] userIds;
/**
* 角色ID
*/
private Long roleId;
/**
* 帖子ID
*/
private Long postId;
/**
* 部门ID
*/
private Long deptId;
}
@@ -50,4 +50,10 @@ public class SysUserPost extends Model<SysUserPost> {
@Schema(description = "岗位id")
private Long postId;
/**
* 租户ID
*/
@Schema(description = "租户ID")
private Long tenantId;
}
@@ -23,6 +23,8 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.admin.api.dto.SysTenantUserDTO;
import com.pig4cloud.pigx.admin.api.dto.UserDTO;
import com.pig4cloud.pigx.admin.api.entity.SysTenant;
import com.pig4cloud.pigx.admin.service.SysMenuService;
import com.pig4cloud.pigx.admin.service.SysTenantService;
@@ -198,4 +200,60 @@ public class SysTenantController {
return R.ok(sysTenantService.getUserTenant());
}
@PostMapping("/user-tenant")
@HasPermission("sys_systenant_del")
public R addUserTenant(@RequestBody SysTenantUserDTO tenantUserDTO) {
return R.ok(sysTenantService.saveTenantUser(tenantUserDTO));
}
/**
* 获取用户租户分页信息
*
* @param page 分页参数
* @param userDTO 用户查询参数
* @return 租户分页结果
*/
@GetMapping("/user-tenant/page")
@HasPermission("sys_systenant_del")
public R getUserTenantPage(@ParameterObject Page page, @ParameterObject UserDTO userDTO) {
return R.ok(sysTenantService.getUserTenantPage(page, userDTO));
}
/**
* 根据租户用户信息删除用户租户关系
*
* @param tenantUserDTO 租户用户信息DTO
* @return 操作结果
*/
@SysLog("删除租户>用户关系")
@DeleteMapping("/remove-users")
@HasPermission("sys_systenant_del")
public R removeUserTenantById(@RequestBody SysTenantUserDTO tenantUserDTO) {
return R.ok(sysTenantService.removeTenantUser(tenantUserDTO));
}
/**
* 根据条件查询租户用户列表
*
* @param userDTO 用户查询条件
* @return 包含查询结果的响应对象
*/
@GetMapping("/list-users")
@HasPermission("sys_systenant_del")
public R listTenantUser(@ParameterObject UserDTO userDTO) {
return R.ok(sysTenantService.listTenantUser(userDTO));
}
/**
* 获取租户角色列表
*
* @param userDTO 用户信息参数对象
* @return 包含租户角色列表的响应结果
*/
@GetMapping("/list-org")
@HasPermission("sys_systenant_del")
public R listTenantOrg(@ParameterObject UserDTO userDTO) {
return R.ok(sysTenantService.listTenantOrg(userDTO));
}
}
@@ -17,10 +17,15 @@
package com.pig4cloud.pigx.admin.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.admin.api.dto.SysTenantUserDTO;
import com.pig4cloud.pigx.admin.api.dto.UserDTO;
import com.pig4cloud.pigx.admin.api.entity.SysTenant;
import com.pig4cloud.pigx.admin.api.entity.SysUser;
import java.util.List;
import java.util.Map;
/**
* 租户管理
@@ -59,4 +64,45 @@ public interface SysTenantService extends IService<SysTenant> {
* @return 用户所属租户列表
*/
List<SysTenant> getUserTenant();
/**
* 获取用户租户分页信息
*
* @param page 分页参数
* @param userDTO 用户信息
* @return 用户租户分页结果
*/
Page getUserTenantPage(Page page, UserDTO userDTO);
/**
* 移除租户用户
*
* @param tenantUserDTO 租户用户信息
* @return 是否移除成功
*/
Boolean removeTenantUser(SysTenantUserDTO tenantUserDTO);
/**
* 根据用户信息查询租户用户列表
*
* @param userDTO 用户信息传输对象
* @return 租户用户列表
*/
List<SysUser> listTenantUser(UserDTO userDTO);
/**
* 保存租户用户信息
*
* @param tenantUserDTO 租户用户信息DTO
* @return 保存是否成功
*/
Boolean saveTenantUser(SysTenantUserDTO tenantUserDTO);
/**
* 获取租户角色列表
*
* @param userDTO 用户信息
* @return 租户角色列表
*/
Map<String, Object> listTenantOrg(UserDTO userDTO);
}
@@ -21,14 +21,15 @@ import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.pig4cloud.pigx.admin.api.constant.TenantStateEnum;
import com.pig4cloud.pigx.admin.api.dto.SysTenantUserDTO;
import com.pig4cloud.pigx.admin.api.dto.UserDTO;
import com.pig4cloud.pigx.admin.api.entity.*;
import com.pig4cloud.pigx.admin.config.ClientDetailsInitRunner;
import com.pig4cloud.pigx.admin.mapper.SysRoleMenuMapper;
import com.pig4cloud.pigx.admin.mapper.SysTenantMapper;
import com.pig4cloud.pigx.admin.mapper.SysTenantUserMapper;
import com.pig4cloud.pigx.admin.mapper.*;
import com.pig4cloud.pigx.admin.service.*;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
@@ -87,7 +88,14 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
private final SysDictService dictService;
private final SysTenantUserMapper tenantUserMapper;
private final SysUserMapper sysUserMapper;
private final SysTenantUserMapper sysTenantUserMapper;
private final SysUserPostMapper sysUserPostMapper;
private final SysUserRoleMapper sysUserRoleMapper;
private final SysUserDeptMapper sysUserDeptMapper;
private final SysRoleMapper sysRoleMapper;
private final SysDeptMapper sysDeptMapper;
private final SysPostMapper sysPostMapper;
/**
* 获取正常状态租户
@@ -321,6 +329,149 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
return baseMapper.selectJoinList(wrapper);
}
/**
* 获取用户租户分页信息
*
* @param page 分页参数
* @param userDTO 用户信息
* @return 用户租户分页结果
*/
@Override
public Page getUserTenantPage(Page page, UserDTO userDTO) {
MPJLambdaWrapper<SysUser> wrapper = new MPJLambdaWrapper<SysUser>()
.selectAll(SysUser.class)
.innerJoin(SysTenantUser.class, SysTenantUser::getUserId, SysUser::getUserId)
.like(StrUtil.isNotBlank(userDTO.getUsername()), SysUser::getUsername, userDTO.getUsername())
.like(StrUtil.isNotBlank(userDTO.getPhone()), SysUser::getPhone, userDTO.getPhone())
.like(StrUtil.isNotBlank(userDTO.getEmail()), SysUser::getEmail, userDTO.getEmail())
.eq(Objects.nonNull(userDTO.getTenantId()), SysTenantUser::getTenantId, userDTO.getTenantId());
return TenantBroker.noneAs(() -> sysUserMapper.selectJoinPage(page, wrapper));
}
/**
* 移除租户用户
*
* @param tenantUserDTO 租户用户信息
* @return 是否移除成功
*/
@Override
public Boolean removeTenantUser(SysTenantUserDTO tenantUserDTO) {
ArrayList<Long> userIdList = CollUtil.toList(tenantUserDTO.getUserIds());
// 删除租户用户关系
sysTenantUserMapper.delete(Wrappers.<SysTenantUser>lambdaQuery()
.eq(SysTenantUser::getTenantId, tenantUserDTO.getTenantId())
.in(SysTenantUser::getUserId, userIdList)
);
TenantBroker.runAs(tenantUserDTO.getTenantId(), (tenantId -> {
// 删除用户岗位
sysUserPostMapper.delete(Wrappers.<SysUserPost>lambdaQuery()
.in(SysUserPost::getUserId, userIdList));
// 删除用户角色
sysUserRoleMapper.delete(Wrappers.<SysUserRole>lambdaQuery()
.in(SysUserRole::getUserId, userIdList));
// 删除用户部门
sysUserDeptMapper.delete(Wrappers.<SysUserDept>lambdaQuery()
.in(SysUserDept::getUserId, userIdList));
}));
return Boolean.TRUE;
}
/**
* 根据用户信息查询租户用户列表
*
* @param userDTO 用户信息传输对象
* @return 租户用户列表
*/
@Override
public List<SysUser> listTenantUser(UserDTO userDTO) {
return TenantBroker.noneAs(() -> sysUserMapper
.selectList(Wrappers.<SysUser>lambdaQuery().like(StrUtil.isNotBlank(userDTO.getUsername()), SysUser::getUsername, userDTO.getUsername())));
}
/**
* 保存租户用户信息
*
* @param tenantUserDTO 租户用户信息DTO
* @return 保存是否成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveTenantUser(SysTenantUserDTO tenantUserDTO) {
TenantBroker.runAs(tenantUserDTO.getTenantId(), (tenantId -> {
for (Long userId : tenantUserDTO.getUserIds()) {
List<SysTenantUser> sysTenantUsers = sysTenantUserMapper.selectList(Wrappers.<SysTenantUser>lambdaQuery()
.eq(SysTenantUser::getTenantId, tenantId)
.eq(SysTenantUser::getUserId, userId));
if (CollUtil.isEmpty(sysTenantUsers)) {
// 插入租户用户关系
SysTenantUser sysTenantUser = new SysTenantUser();
sysTenantUser.setUserId(userId);
sysTenantUser.setTenantId(tenantId);
sysTenantUserMapper.insert(sysTenantUser);
}
List<SysUserRole> userRoleList = sysUserRoleMapper.selectList(Wrappers.<SysUserRole>lambdaQuery()
.eq(SysUserRole::getUserId, userId)
.eq(SysUserRole::getRoleId, tenantUserDTO.getRoleId()));
if (CollUtil.isEmpty(userRoleList)) {
// 插入用户角色关系
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(userId);
sysUserRole.setRoleId(tenantUserDTO.getRoleId());
sysUserRoleMapper.insert(sysUserRole);
}
List<SysUserDept> userDeptList = sysUserDeptMapper.selectList(Wrappers.<SysUserDept>lambdaQuery()
.eq(SysUserDept::getUserId, userId)
.eq(SysUserDept::getDeptId, tenantUserDTO.getDeptId()));
if (CollUtil.isEmpty(userDeptList)) {
// 插入用户部门关系
SysUserDept sysUserDept = new SysUserDept();
sysUserDept.setUserId(userId);
sysUserDept.setDeptId(tenantUserDTO.getDeptId());
sysUserDeptMapper.insert(sysUserDept);
}
List<SysUserPost> userPostList = sysUserPostMapper.selectList(Wrappers.<SysUserPost>lambdaQuery()
.eq(SysUserPost::getUserId, userId)
.eq(SysUserPost::getPostId, tenantUserDTO.getPostId()));
if (CollUtil.isEmpty(userPostList)) {
// 插入用户岗位关系
SysUserPost sysUserPost = new SysUserPost();
sysUserPost.setUserId(userId);
sysUserPost.setPostId(tenantUserDTO.getPostId());
sysUserPostMapper.insert(sysUserPost);
}
}
}));
return Boolean.TRUE;
}
/**
* 获取租户角色列表
*
* @param userDTO 用户信息
* @return 租户角色列表
*/
@Override
public Map<String, Object> listTenantOrg(UserDTO userDTO) {
return TenantBroker.applyAs(userDTO.getTenantId(), tenantId -> {
// 过滤有权限配置的菜单
List<SysRole> sysRoles = sysRoleMapper.selectList(Wrappers.lambdaQuery()).stream()
.filter(role -> roleMenuMapper.exists(Wrappers.<SysRoleMenu>lambdaQuery()
.eq(SysRoleMenu::getRoleId, role.getRoleId()))).toList();
List<SysDept> sysDepts = sysDeptMapper.selectList(Wrappers.lambdaQuery());
List<SysPost> sysPosts = sysPostMapper.selectList(Wrappers.lambdaQuery());
return Map.of("sysRoles", sysRoles, "sysDepts", sysDepts, "sysPosts", sysPosts);
});
}
/**
* 保存新的租户菜单,维护成新的菜单
*