feat:优化一下用户查询
This commit is contained in:
1 parent
dbf48af330
commit
8d31f20cec
9 files changed
+366
-43
No files matched your search
@@ -0,0 +1,111 @@
|
||||
-- 系统用户分页查询索引优化。
|
||||
--
|
||||
-- 适用接口:POST /admin/user/page、POST /admin/user/select。
|
||||
-- 执行影响:仅新增普通索引,不修改字段、约束和历史数据;建索引期间会消耗 IO,建议在业务低峰期执行。
|
||||
-- 执行前建议:先备份数据库,并使用文末查询核对生产库已有索引及 EXPLAIN 计划。
|
||||
|
||||
-- 用户分页的默认排序索引:覆盖租户、逻辑删除和 create_time + user_id 稳定倒序分页。
|
||||
SET @index_ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_user'
|
||||
AND index_name = 'idx_sys_user_tenant_del_created'
|
||||
),
|
||||
'SELECT ''idx_sys_user_tenant_del_created already exists'' AS message',
|
||||
'ALTER TABLE `sys_user` ADD INDEX `idx_sys_user_tenant_del_created` (`tenant_id`, `del_flag`, `create_time`, `user_id`)'
|
||||
);
|
||||
PREPARE index_stmt FROM @index_ddl;
|
||||
EXECUTE index_stmt;
|
||||
DEALLOCATE PREPARE index_stmt;
|
||||
|
||||
-- 部门数据权限和用户状态分页索引:先缩小部门及审核状态范围,再读取稳定排序列。
|
||||
SET @index_ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_user'
|
||||
AND index_name = 'idx_sys_user_tenant_del_dept_status_created'
|
||||
),
|
||||
'SELECT ''idx_sys_user_tenant_del_dept_status_created already exists'' AS message',
|
||||
'ALTER TABLE `sys_user` ADD INDEX `idx_sys_user_tenant_del_dept_status_created` (`tenant_id`, `del_flag`, `dept_id`, `process_status`, `create_time`, `user_id`)'
|
||||
);
|
||||
PREPARE index_stmt FROM @index_ddl;
|
||||
EXECUTE index_stmt;
|
||||
DEALLOCATE PREPARE index_stmt;
|
||||
|
||||
-- 角色反向筛选索引:现有主键通常为 user_id + role_id,本索引用于从角色快速定位用户。
|
||||
SET @index_ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_user_role'
|
||||
AND index_name = 'idx_sys_user_role_role_user'
|
||||
),
|
||||
'SELECT ''idx_sys_user_role_role_user already exists'' AS message',
|
||||
'ALTER TABLE `sys_user_role` ADD INDEX `idx_sys_user_role_role_user` (`role_id`, `user_id`)'
|
||||
);
|
||||
PREPARE index_stmt FROM @index_ddl;
|
||||
EXECUTE index_stmt;
|
||||
DEALLOCATE PREPARE index_stmt;
|
||||
|
||||
-- 当前页人脸聚合及有无人脸 EXISTS 筛选索引,避免按用户查询时回表扫描租户全部人脸。
|
||||
SET @index_ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_user_face'
|
||||
AND index_name = 'idx_sys_user_face_tenant_user_del'
|
||||
),
|
||||
'SELECT ''idx_sys_user_face_tenant_user_del already exists'' AS message',
|
||||
'ALTER TABLE `sys_user_face` ADD INDEX `idx_sys_user_face_tenant_user_del` (`tenant_id`, `user_id`, `del_flag`)'
|
||||
);
|
||||
PREPARE index_stmt FROM @index_ddl;
|
||||
EXECUTE index_stmt;
|
||||
DEALLOCATE PREPARE index_stmt;
|
||||
|
||||
-- 身份类型反向筛选索引:支持 identity_type 条件先定位用户,用户 ID 同时用于半连接。
|
||||
SET @index_ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_user_identity'
|
||||
AND index_name = 'idx_sys_user_identity_type_user'
|
||||
),
|
||||
'SELECT ''idx_sys_user_identity_type_user already exists'' AS message',
|
||||
'ALTER TABLE `sys_user_identity` ADD INDEX `idx_sys_user_identity_type_user` (`identity_type`, `user_id`)'
|
||||
);
|
||||
PREPARE index_stmt FROM @index_ddl;
|
||||
EXECUTE index_stmt;
|
||||
DEALLOCATE PREPARE index_stmt;
|
||||
|
||||
-- 数据权限计算会读取当前租户有效部门并在内存构建子树,本索引降低部门全集读取成本。
|
||||
SET @index_ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_dept'
|
||||
AND index_name = 'idx_sys_dept_tenant_del_parent'
|
||||
),
|
||||
'SELECT ''idx_sys_dept_tenant_del_parent already exists'' AS message',
|
||||
'ALTER TABLE `sys_dept` ADD INDEX `idx_sys_dept_tenant_del_parent` (`tenant_id`, `del_flag`, `parent_id`, `dept_id`)'
|
||||
);
|
||||
PREPARE index_stmt FROM @index_ddl;
|
||||
EXECUTE index_stmt;
|
||||
DEALLOCATE PREPARE index_stmt;
|
||||
|
||||
-- 执行后核对新增索引;确认无重复或同前缀冗余索引后,再按生产变更流程决定是否清理旧索引。
|
||||
SELECT table_name, index_name, seq_in_index, column_name
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name IN ('sys_user', 'sys_user_role', 'sys_user_face', 'sys_user_identity', 'sys_dept')
|
||||
ORDER BY table_name, index_name, seq_in_index;
|
||||
|
||||
-- 建议在执行索引后更新统计信息,并用真实租户、部门和状态参数执行 EXPLAIN ANALYZE。
|
||||
-- ANALYZE TABLE sys_user, sys_user_role, sys_user_face, sys_user_identity, sys_dept;
|
||||
+10
@@ -18,8 +18,10 @@
|
||||
package com.bsd.pjcloud.admin.mapper;
|
||||
|
||||
import com.bsd.pjcloud.admin.api.entity.SysPost;
|
||||
import com.bsd.pjcloud.admin.mapper.vo.UserPostPageRow;
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -39,4 +41,12 @@ public interface SysPostMapper extends PjcloudBaseMapper<SysPost> {
|
||||
*/
|
||||
List<SysPost> listPostsByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 批量查询当前分页用户的岗位,返回用户 ID 与岗位资料的扁平行,避免逐用户嵌套查询。
|
||||
*
|
||||
* @param userIds 当前页用户主键列表;为空时不应调用
|
||||
* @return 按用户 ID、岗位排序值和岗位 ID 升序返回岗位资料,未分配岗位的用户不产生记录
|
||||
*/
|
||||
List<UserPostPageRow> listPostsByUserIds(@Param("userIds") List<Long> userIds);
|
||||
|
||||
}
|
||||
+10
@@ -20,8 +20,10 @@
|
||||
package com.bsd.pjcloud.admin.mapper;
|
||||
|
||||
import com.bsd.pjcloud.admin.api.entity.SysRole;
|
||||
import com.bsd.pjcloud.admin.mapper.vo.UserRolePageRow;
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,4 +45,12 @@ public interface SysRoleMapper extends PjcloudBaseMapper<SysRole> {
|
||||
*/
|
||||
List<SysRole> listRolesByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 批量查询当前分页用户的角色,返回用户 ID 与角色资料的扁平行,避免逐用户嵌套查询。
|
||||
*
|
||||
* @param userIds 当前页用户主键列表;为空时不应调用
|
||||
* @return 按用户 ID、角色 ID 升序返回角色资料,未分配角色的用户不产生记录
|
||||
*/
|
||||
List<UserRolePageRow> listRolesByUserIds(@Param("userIds") List<Long> userIds);
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, pjcloud All rights reserved.
|
||||
*/
|
||||
|
||||
package com.bsd.pjcloud.admin.mapper.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户分页岗位批量查询的扁平结果行。
|
||||
*
|
||||
* <p>该类型仅服务于 Mapper 到 Service 的批量回填,不作为对外接口模型,避免为分页结果增加嵌套查询。</p>
|
||||
*/
|
||||
@Data
|
||||
public class UserPostPageRow {
|
||||
|
||||
/** 当前用户主键,来源于 sys_user_post.user_id。 */
|
||||
private Long userId;
|
||||
|
||||
/** 岗位主键,来源于 sys_post.post_id。 */
|
||||
private Long postId;
|
||||
|
||||
/** 岗位编码,来源于 sys_post.post_code。 */
|
||||
private String postCode;
|
||||
|
||||
/** 岗位名称,来源于 sys_post.post_name。 */
|
||||
private String postName;
|
||||
|
||||
/** 岗位排序值,来源于 sys_post.post_sort。 */
|
||||
private Integer postSort;
|
||||
|
||||
/** 岗位删除标记,来源于 sys_post.del_flag。 */
|
||||
private String delFlag;
|
||||
|
||||
/** 岗位创建时间,来源于 sys_post.create_time。 */
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 岗位更新时间,来源于 sys_post.update_time。 */
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 岗位备注,来源于 sys_post.remark。 */
|
||||
private String remark;
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, pjcloud All rights reserved.
|
||||
*/
|
||||
|
||||
package com.bsd.pjcloud.admin.mapper.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户分页角色批量查询的扁平结果行。
|
||||
*
|
||||
* <p>该类型仅服务于 Mapper 到 Service 的批量回填,不作为对外接口模型,避免为分页结果增加嵌套查询。</p>
|
||||
*/
|
||||
@Data
|
||||
public class UserRolePageRow {
|
||||
|
||||
/** 当前用户主键,来源于 sys_user_role.user_id。 */
|
||||
private Long userId;
|
||||
|
||||
/** 角色主键,来源于 sys_role.role_id。 */
|
||||
private Long roleId;
|
||||
|
||||
/** 角色名称,来源于 sys_role.role_name。 */
|
||||
private String roleName;
|
||||
|
||||
/** 角色编码,来源于 sys_role.role_code。 */
|
||||
private String roleCode;
|
||||
|
||||
/** 角色描述,来源于 sys_role.role_desc。 */
|
||||
private String roleDesc;
|
||||
|
||||
/** 数据权限类型,来源于 sys_role.ds_type。 */
|
||||
private Integer dsType;
|
||||
|
||||
/** 自定义数据权限范围,来源于 sys_role.ds_scope。 */
|
||||
private String dsScope;
|
||||
|
||||
/** 角色创建时间,来源于 sys_role.create_time。 */
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 角色更新时间,来源于 sys_role.update_time。 */
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 角色删除标记,来源于 sys_role.del_flag。 */
|
||||
private String delFlag;
|
||||
}
|
||||
+81
-32
@@ -51,8 +51,12 @@ import com.bsd.pjcloud.admin.handler.UserWalletCreateEvent;
|
||||
import com.bsd.pjcloud.admin.handler.FaceChangeEvent;
|
||||
import com.bsd.pjcloud.admin.mapper.SysUserFaceMapper;
|
||||
import com.bsd.pjcloud.admin.mapper.SysUserMapper;
|
||||
import com.bsd.pjcloud.admin.mapper.SysPostMapper;
|
||||
import com.bsd.pjcloud.admin.mapper.SysRoleMapper;
|
||||
import com.bsd.pjcloud.admin.mapper.SysUserPostMapper;
|
||||
import com.bsd.pjcloud.admin.mapper.SysUserRoleMapper;
|
||||
import com.bsd.pjcloud.admin.mapper.vo.UserPostPageRow;
|
||||
import com.bsd.pjcloud.admin.mapper.vo.UserRolePageRow;
|
||||
import com.bsd.pjcloud.admin.service.*;
|
||||
import com.bsd.pjcloud.admin.utils.flow.FlowUtil;
|
||||
import com.bsd.pjcloud.admin.utils.flow.dto.UserApplyFlowDto;
|
||||
@@ -141,6 +145,12 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
|
||||
private final SysUserPostMapper sysUserPostMapper;
|
||||
|
||||
/** 角色批量查询 Mapper,用于按当前分页用户一次性回填角色。 */
|
||||
private final SysRoleMapper sysRoleMapper;
|
||||
|
||||
/** 岗位批量查询 Mapper,用于按当前分页用户一次性回填岗位。 */
|
||||
private final SysPostMapper sysPostMapper;
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
@@ -338,29 +348,18 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
IPage<UserVO> list = baseMapper.getUserVosPage(page, userDTO, dataScope);
|
||||
if (ObjectUtil.isNotEmpty(list.getRecords())) {
|
||||
List<Long> userIds = list.getRecords().stream().map(UserVO::getUserId).distinct().toList();
|
||||
fillUserRolesAndPosts(list.getRecords(), userIds);
|
||||
// 分页结果统一补齐身份及扩展资料,保证管理端列表与详情接口使用同一套身份语义。
|
||||
sysUserIdentityService.fillUserIdentities(list.getRecords());
|
||||
if (ObjectUtil.isNotEmpty(userIds)) {
|
||||
SysUserFaceDto faceDto = new SysUserFaceDto();
|
||||
// 人脸聚合必须限定为当前页,禁止每次分页扫描并传输当前租户的全部人脸。
|
||||
faceDto.setUserIds(userIds);
|
||||
List<SysUserFaceVo> faceList = sysUserFaceMapper.groupConcatFaces(faceDto);
|
||||
DataUtil.convertFacesList(list.getRecords(), CollectionUtils.convertMap(faceList, SysUserFaceVo::getUserId));
|
||||
}
|
||||
// 审批流程
|
||||
if (userDTO.getIsFindCheckUser()) {
|
||||
List<String> processInstanceIds = list.getRecords().stream().map(UserVO::getProcessId).filter(ObjectUtil::isNotEmpty).distinct().toList();
|
||||
if (ObjectUtil.isNotEmpty(processInstanceIds)) {
|
||||
List<TaskDto> taskList = flowUtil.queryProcessInstanceList(SecurityUtils.getUser().getId().toString(), processInstanceIds);
|
||||
if (ObjectUtil.isNotEmpty(taskList)) {
|
||||
Map<String, TaskDto> taskMap = CollectionUtils.convertMap(taskList, TaskDto::getProcessInstanceId);
|
||||
CollectionUtils.convertList(list.getRecords(), v -> {
|
||||
TaskDto task = taskMap.get(v.getProcessId());
|
||||
if (ObjectUtil.isNotEmpty(task)) {
|
||||
v.setIsCheckUser(true);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Boolean.TRUE.equals(userDTO.getIsFindCheckUser())) {
|
||||
fillPendingApprovalFlags(list.getRecords());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
@@ -408,6 +407,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
}
|
||||
IPage<UserVO> list = baseMapper.getUserVosPage(page, userDTO, dataScope);
|
||||
if (ObjectUtil.isNotEmpty(list.getRecords())) {
|
||||
List<Long> userIds = list.getRecords().stream().map(UserVO::getUserId).distinct().toList();
|
||||
fillUserRolesAndPosts(list.getRecords(), userIds);
|
||||
sysUserIdentityService.fillUserIdentities(list.getRecords());
|
||||
}
|
||||
return list;
|
||||
@@ -418,32 +419,80 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
IPage<UserVO> list = baseMapper.getUserVosPage(page, userDTO, null);
|
||||
if (ObjectUtil.isNotEmpty(list.getRecords())) {
|
||||
List<Long> userIds = list.getRecords().stream().map(UserVO::getUserId).distinct().toList();
|
||||
fillUserRolesAndPosts(list.getRecords(), userIds);
|
||||
if (ObjectUtil.isNotEmpty(userIds)) {
|
||||
SysUserFaceDto faceDto = new SysUserFaceDto();
|
||||
faceDto.setUserIds(userIds);
|
||||
List<SysUserFaceVo> faceList = sysUserFaceMapper.groupConcatFaces(faceDto);
|
||||
DataUtil.convertFacesList(list.getRecords(), CollectionUtils.convertMap(faceList, SysUserFaceVo::getUserId));
|
||||
}
|
||||
// 审批流程
|
||||
if (userDTO.getIsFindCheckUser()) {
|
||||
List<String> processInstanceIds = list.getRecords().stream().map(UserVO::getProcessId).filter(ObjectUtil::isNotEmpty).distinct().toList();
|
||||
if (ObjectUtil.isNotEmpty(processInstanceIds)) {
|
||||
List<TaskDto> taskList = flowUtil.queryProcessInstanceList(SecurityUtils.getUser().getId().toString(), processInstanceIds);
|
||||
if (ObjectUtil.isNotEmpty(taskList)) {
|
||||
Map<String, TaskDto> taskMap = CollectionUtils.convertMap(taskList, TaskDto::getProcessInstanceId);
|
||||
CollectionUtils.convertList(list.getRecords(), v -> {
|
||||
TaskDto task = taskMap.get(v.getProcessId());
|
||||
if (ObjectUtil.isNotEmpty(task)) {
|
||||
v.setIsCheckUser(true);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Boolean.TRUE.equals(userDTO.getIsFindCheckUser())) {
|
||||
fillPendingApprovalFlags(list.getRecords());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按当前页用户批量回填角色和岗位,将原先随分页条数线性增长的嵌套查询固定为两次查询。
|
||||
*
|
||||
* @param userVos 当前页用户展示对象,方法会原位设置角色和岗位集合
|
||||
* @param userIds 当前页去重后的用户主键;为空时不访问数据库
|
||||
*/
|
||||
private void fillUserRolesAndPosts(List<UserVO> userVos, List<Long> userIds) {
|
||||
if (CollUtil.isEmpty(userVos) || CollUtil.isEmpty(userIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Long, List<SysRole>> rolesByUserId = new HashMap<>();
|
||||
for (UserRolePageRow row : sysRoleMapper.listRolesByUserIds(userIds)) {
|
||||
SysRole role = new SysRole();
|
||||
BeanUtils.copyProperties(row, role);
|
||||
rolesByUserId.computeIfAbsent(row.getUserId(), ignored -> new ArrayList<>()).add(role);
|
||||
}
|
||||
|
||||
Map<Long, List<SysPost>> postsByUserId = new HashMap<>();
|
||||
for (UserPostPageRow row : sysPostMapper.listPostsByUserIds(userIds)) {
|
||||
SysPost post = new SysPost();
|
||||
BeanUtils.copyProperties(row, post);
|
||||
postsByUserId.computeIfAbsent(row.getUserId(), ignored -> new ArrayList<>()).add(post);
|
||||
}
|
||||
|
||||
for (UserVO userVo : userVos) {
|
||||
userVo.setRoleList(new ArrayList<>(rolesByUserId.getOrDefault(userVo.getUserId(), Collections.emptyList())));
|
||||
userVo.setPostList(new ArrayList<>(postsByUserId.getOrDefault(userVo.getUserId(), Collections.emptyList())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前操作人可处理的审核中用户流程,并回填列表的可审批标记。
|
||||
*
|
||||
* <p>用户业务状态不是“审核中”时不可能存在待处理任务,因此不把已通过、已拒绝或已取消流程发送到远程流程服务。</p>
|
||||
*
|
||||
* @param userVos 当前页用户展示对象;无认证操作人或无审核中流程时不调用远程服务
|
||||
*/
|
||||
private void fillPendingApprovalFlags(List<UserVO> userVos) {
|
||||
PjcloudUser operator = SecurityUtils.getUser();
|
||||
if (operator == null) {
|
||||
return;
|
||||
}
|
||||
List<String> processInstanceIds = userVos.stream()
|
||||
.filter(userVo -> ObjectUtil.equals(userVo.getProcessStatus(), TaskCheckStatusEnum.PADDING.getCode()))
|
||||
.map(UserVO::getProcessId)
|
||||
.filter(ObjectUtil::isNotEmpty)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (CollUtil.isEmpty(processInstanceIds)) {
|
||||
return;
|
||||
}
|
||||
List<TaskDto> taskList = flowUtil.queryProcessInstanceList(operator.getId().toString(), processInstanceIds);
|
||||
if (CollUtil.isEmpty(taskList)) {
|
||||
return;
|
||||
}
|
||||
Map<String, TaskDto> taskMap = CollectionUtils.convertMap(taskList, TaskDto::getProcessInstanceId);
|
||||
userVos.forEach(userVo -> userVo.setIsCheckUser(taskMap.containsKey(userVo.getProcessId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过ID查询用户信息
|
||||
*
|
||||
|
||||
@@ -50,4 +50,25 @@
|
||||
and up.user_id = #{userId}
|
||||
</select>
|
||||
|
||||
<!-- 按当前页用户一次性读取岗位,避免用户分页结果触发 N+1 岗位查询。 -->
|
||||
<select id="listPostsByUserIds" resultType="com.bsd.pjcloud.admin.mapper.vo.UserPostPageRow">
|
||||
SELECT up.user_id,
|
||||
p.post_id,
|
||||
p.post_code,
|
||||
p.post_name,
|
||||
p.post_sort,
|
||||
p.del_flag,
|
||||
p.create_time,
|
||||
p.update_time,
|
||||
p.remark
|
||||
FROM sys_user_post up
|
||||
INNER JOIN sys_post p ON p.post_id = up.post_id
|
||||
WHERE p.del_flag = '0'
|
||||
AND up.user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
ORDER BY up.user_id, p.post_sort, p.post_id
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -50,4 +50,26 @@
|
||||
AND sys_role.del_flag = '0'
|
||||
and sys_user_role.user_id = #{userId}
|
||||
</select>
|
||||
|
||||
<!-- 按当前页用户一次性读取角色,避免用户分页结果触发 N+1 角色查询。 -->
|
||||
<select id="listRolesByUserIds" resultType="com.bsd.pjcloud.admin.mapper.vo.UserRolePageRow">
|
||||
SELECT ur.user_id,
|
||||
r.role_id,
|
||||
r.role_name,
|
||||
r.role_code,
|
||||
r.role_desc,
|
||||
r.ds_type,
|
||||
r.ds_scope,
|
||||
r.create_time,
|
||||
r.update_time,
|
||||
r.del_flag
|
||||
FROM sys_user_role ur
|
||||
INNER JOIN sys_role r ON r.role_id = ur.role_id
|
||||
WHERE r.del_flag = '0'
|
||||
AND ur.user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
ORDER BY ur.user_id, r.role_id
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -208,7 +208,11 @@
|
||||
u.user_id = #{id} and u.del_flag = '0'
|
||||
</select>
|
||||
|
||||
<select id="getUserVosPage" resultMap="baseResultMap">
|
||||
<!--
|
||||
分页主查询只映射用户主资料;角色和岗位由 Service 按当前页用户 ID 批量回填。
|
||||
这里不能使用带嵌套 select 的 baseResultMap,否则每条用户记录都会额外查询一次角色和岗位。
|
||||
-->
|
||||
<select id="getUserVosPage" resultType="com.bsd.pjcloud.admin.api.vo.UserVO">
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.process_id,
|
||||
@@ -224,14 +228,14 @@
|
||||
u.wx_pc_openid,
|
||||
u.apple_openid,
|
||||
u.qq_openid,
|
||||
u.gitee_login,
|
||||
u.osc_id,
|
||||
u.gitee_login gitee_open_id,
|
||||
u.osc_id osc_open_id,
|
||||
u.wx_cp_userid,
|
||||
u.wx_ding_userid,
|
||||
u.dept_id,
|
||||
u.create_by,
|
||||
u.create_time ucreate_time,
|
||||
u.update_time uupdate_time,
|
||||
u.create_time create_time,
|
||||
u.update_time update_time,
|
||||
u.del_flag,
|
||||
u.lock_flag,
|
||||
u.password_expire_flag,
|
||||
@@ -248,7 +252,6 @@
|
||||
FROM
|
||||
sys_user u
|
||||
LEFT JOIN sys_dept d ON d.dept_id = u.dept_id
|
||||
LEFT JOIN sys_user_role urole ON urole.user_id = u.user_id
|
||||
<where>
|
||||
u.del_flag = '0'
|
||||
<if test="query.userId != null">
|
||||
@@ -293,10 +296,15 @@
|
||||
</choose>
|
||||
</if>
|
||||
<if test="query.role != null and query.role.size() != 0">
|
||||
and urole.role_id IN
|
||||
<foreach collection="query.role" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM sys_user_role user_role
|
||||
WHERE user_role.user_id = u.user_id
|
||||
AND user_role.role_id IN
|
||||
<foreach collection="query.role" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="query.deptIds != null and query.deptIds.size() != 0">
|
||||
<choose>
|
||||
@@ -329,7 +337,6 @@
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
group by u.user_id
|
||||
<if test="_databaseId != 'mssql'">
|
||||
ORDER BY u.create_time DESC, u.user_id DESC
|
||||
</if>
|
||||
|
||||
Reference in new issue
Block a user