diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/UserManagementRelationApi.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/UserManagementRelationApi.java new file mode 100644 index 0000000..e3148b7 --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/UserManagementRelationApi.java @@ -0,0 +1,49 @@ +package com.njcn.rdms.module.system.api.user; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.framework.common.util.collection.CollectionUtils; +import com.njcn.rdms.module.system.api.user.dto.UserManagementRelationRespDTO; +import com.njcn.rdms.module.system.enums.ApiConstants; +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.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 用户带人关系") +public interface UserManagementRelationApi { + + String PREFIX = ApiConstants.PREFIX + "/user-management-relation"; + + @GetMapping(PREFIX + "/list-by-manager") + @Operation(summary = "根据管理者用户ID获得带人关系列表") + @Parameter(name = "managerUserId", description = "管理者用户ID", example = "1", required = true) + CommonResult> getRelationListByManagerUserId(@RequestParam("managerUserId") Long managerUserId); + + @GetMapping(PREFIX + "/list-by-subordinate") + @Operation(summary = "根据被管理者用户ID获得带人关系列表") + @Parameter(name = "subordinateUserId", description = "被管理者用户ID", example = "2", required = true) + CommonResult> getRelationListBySubordinateUserId(@RequestParam("subordinateUserId") Long subordinateUserId); + + @GetMapping(PREFIX + "/list") + @Operation(summary = "获得带人关系列表") + @Parameter(name = "ids", description = "关系编号数组", example = "1,2", required = true) + CommonResult> getRelationList(@RequestParam("ids") Collection ids); + + default Map getRelationMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return MapUtil.empty(); + } + List list = getRelationList(ids).getData(); + return CollectionUtils.convertMap(list, UserManagementRelationRespDTO::getId); + } + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/UserManagementRelationRespDTO.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/UserManagementRelationRespDTO.java new file mode 100644 index 0000000..174aae2 --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/UserManagementRelationRespDTO.java @@ -0,0 +1,35 @@ +package com.njcn.rdms.module.system.api.user.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 用户带人关系 Response DTO + * + * @author dklive + */ +@Schema(description = "RPC 服务 - 用户带人关系 Response DTO") +@Data +public class UserManagementRelationRespDTO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "管理者用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long managerUserId; + + @Schema(description = "被管理用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long subordinateUserId; + + @Schema(description = "生效开始时间") + private LocalDateTime effectiveFrom; + + @Schema(description = "生效结束时间") + private LocalDateTime effectiveUntil; + + @Schema(description = "备注") + private String remark; + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java index 0376b64..ec2c205 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java @@ -54,6 +54,10 @@ public interface ErrorCodeConstants { ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); ErrorCode USER_IS_RESIGNED = new ErrorCode(1_002_003_012, "名字为【{}】的用户已离职"); + // ========== 用户带人关系模块 1-002-003-100 ========== + ErrorCode USER_MANAGEMENT_RELATION_NOT_FOUND = new ErrorCode(1_002_003_100, "用户带人关系不存在"); + ErrorCode USER_MANAGEMENT_RELATION_MANAGER_EXISTS = new ErrorCode(1_002_003_101, "该用户已有直属上级,不能重复添加"); + // ========== 部门模块 1-002-004-000 ========== ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在"); diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/api/user/UserManagementRelationApiImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/api/user/UserManagementRelationApiImpl.java new file mode 100644 index 0000000..b9bd24c --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/api/user/UserManagementRelationApiImpl.java @@ -0,0 +1,48 @@ +package com.njcn.rdms.module.system.api.user; + +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.module.system.api.user.dto.UserManagementRelationRespDTO; +import com.njcn.rdms.module.system.dal.dataobject.user.UserManagementRelationDO; +import com.njcn.rdms.module.system.service.user.UserManagementRelationService; +import io.swagger.v3.oas.annotations.Hidden; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.njcn.rdms.framework.common.pojo.CommonResult.success; + +@RestController +@Validated +@Hidden +public class UserManagementRelationApiImpl implements UserManagementRelationApi { + + @Resource + private UserManagementRelationService userManagementRelationService; + + @Override + public CommonResult> getRelationListByManagerUserId(Long managerUserId) { + List list = userManagementRelationService.getRelationListByManagerUserId(managerUserId); + return success(BeanUtils.toBean(list, UserManagementRelationRespDTO.class)); + } + + @Override + public CommonResult> getRelationListBySubordinateUserId(Long subordinateUserId) { + List list = userManagementRelationService.getRelationListBySubordinateUserId(subordinateUserId); + return success(BeanUtils.toBean(list, UserManagementRelationRespDTO.class)); + } + + @Override + public CommonResult> getRelationList(Collection ids) { + if (ids == null || ids.isEmpty()) { + return success(Collections.emptyList()); + } + List list = userManagementRelationService.getRelationList(ids); + return success(BeanUtils.toBean(list, UserManagementRelationRespDTO.class)); + } + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserManagementRelationController.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserManagementRelationController.java new file mode 100644 index 0000000..59c239f --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserManagementRelationController.java @@ -0,0 +1,191 @@ +package com.njcn.rdms.module.system.controller.admin.user; + +import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog; +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.framework.excel.core.util.ExcelUtils; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationQueryReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationRespVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationSaveReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationTreeRespVO; +import com.njcn.rdms.module.system.dal.dataobject.user.UserManagementRelationDO; +import com.njcn.rdms.module.system.service.user.UserManagementRelationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static com.njcn.rdms.framework.common.pojo.CommonResult.success; + +/** + * 用户带人关系 Controller + * + * 提供用户带人关系的管理接口,包括: + * - 创建、更新、删除用户带人关系 + * - 查询用户带人关系列表和详情 + * - 获取用户带人关系树形结构 + * - 导出用户带人关系数据 + * + * @author dklive + */ +@Tag(name = "管理后台 - 用户带人关系") +@RestController +@RequestMapping("/system/user-management-relation") +@Validated +public class UserManagementRelationController { + + @Resource + private UserManagementRelationService userManagementRelationService; + + /** + * 创建用户带人关系 + * + * 权限要求:system:user-management-relation:create + * + * @param createReqVO 创建请求VO + * @return 关系记录主键ID + */ + @PostMapping("/create") + @Operation(summary = "创建用户带人关系") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:create')") + public CommonResult createUserManagementRelation(@Valid @RequestBody UserManagementRelationSaveReqVO createReqVO) { + Long id = userManagementRelationService.createRelation(createReqVO); + return success(id); + } + + /** + * 修改用户带人关系 + * + * 权限要求:system:user-management-relation:update + * + * @param updateReqVO 更新请求VO + * @return 操作结果 + */ + @PutMapping("/update") + @Operation(summary = "修改用户带人关系") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:update')") + public CommonResult updateUserManagementRelation(@Valid @RequestBody UserManagementRelationSaveReqVO updateReqVO) { + userManagementRelationService.updateRelation(updateReqVO); + return success(true); + } + + /** + * 删除用户带人关系 + * + * 根据主键ID删除单条用户带人关系记录 + * 权限要求:system:user-management-relation:delete + * + * @param id 关系记录主键ID + * @return 操作结果 + */ + @DeleteMapping("/delete") + @Operation(summary = "删除用户带人关系") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:delete')") + public CommonResult deleteUserManagementRelation(@RequestParam("id") Long id) { + userManagementRelationService.deleteRelation(id); + return success(true); + } + + /** + * 批量删除用户带人关系 + * + * 根据主键ID列表批量删除用户带人关系记录 + * 权限要求:system:user-management-relation:delete + * + * @param ids 关系记录主键ID列表 + * @return 操作结果 + */ + @DeleteMapping("/delete-list") + @Operation(summary = "批量删除用户带人关系") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:delete')") + public CommonResult deleteUserManagementRelationList(@RequestParam("ids") List ids) { + userManagementRelationService.deleteRelationList(ids); + return success(true); + } + + /** + * 获得用户带人关系信息 + * + * 根据主键ID查询单条用户带人关系记录 + * 权限要求:system:user-management-relation:query + * + * @param id 关系记录主键ID + * @return 用户带人关系详情 + */ + @GetMapping(value = "/get") + @Operation(summary = "获得用户带人关系信息") + @Parameter(name = "id", description = "关系编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:query')") + public CommonResult getUserManagementRelation(@RequestParam("id") Long id) { + UserManagementRelationDO relation = userManagementRelationService.getRelation(id); + return success(BeanUtils.toBean(relation, UserManagementRelationRespVO.class)); + } + + /** + * 获取用户带人关系列表 + * + * 根据查询条件查询用户带人关系记录列表 + * 权限要求:system:user-management-relation:query + * + * @param reqVO 查询条件VO + * @return 用户带人关系列表 + */ + @GetMapping("/query") + @Operation(summary = "获取用户带人关系列表") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:query')") + public CommonResult> getUserManagementRelationQuery(@Validated UserManagementRelationQueryReqVO reqVO) { + List list = userManagementRelationService.getRelationQuery(reqVO); + return success(list); + } + + /** + * 获取用户带人关系树形结构 + * + * 构建用户上下级关系的树形结构,用于前端树形控件展示 + * 树形结构特点: + * - 根节点:最高领导,没有上级 + * - 中间节点:有上级也有下级 + * - 叶子节点:基层员工,没有下级 + * + * 权限要求:system:user-management-relation:query + * + * @return 用户带人关系树形列表 + */ + @GetMapping("/tree") + @Operation(summary = "获取用户带人关系树形结构", description = "用于前端树形控件展示,包含用户的上下级层级关系") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:query')") + public CommonResult> getUserManagementRelationTree() { + return success(userManagementRelationService.getRelationTree()); + } + + + /** + * 导出用户带人关系 Excel + * + * 根据查询条件导出用户带人关系数据到Excel文件 + * 权限要求:system:user-management-relation:export + * + * @param response HTTP响应对象 + * @param reqVO 查询条件VO + * @throws IOException IO异常 + */ + @GetMapping("/export-excel") + @Operation(summary = "导出用户带人关系 Excel") + @PreAuthorize("@ss.hasPermission('system:user-management-relation:export')") + @ApiAccessLog(operateType = EXPORT) + public void export(HttpServletResponse response, @Validated UserManagementRelationQueryReqVO reqVO) throws IOException { + List list = userManagementRelationService.getRelationQuery(reqVO); + ExcelUtils.write(response, "用户带人关系数据.xls", "用户带人关系列表", UserManagementRelationRespVO.class, + BeanUtils.toBean(list, UserManagementRelationRespVO.class)); + } + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationQueryReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationQueryReqVO.java new file mode 100644 index 0000000..a15b85d --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationQueryReqVO.java @@ -0,0 +1,16 @@ +package com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 用户带人关系列表 Request VO") +@Data +public class UserManagementRelationQueryReqVO { + + @Schema(description = "管理者用户ID", example = "1") + private Long managerUserId; + + @Schema(description = "被管理用户ID", example = "2") + private Long subordinateUserId; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationRespVO.java new file mode 100644 index 0000000..3695f37 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationRespVO.java @@ -0,0 +1,42 @@ +package com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户带人关系信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class UserManagementRelationRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("主键ID") + private Long id; + + @Schema(description = "管理者用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("管理者用户ID") + private Long managerUserId; + + @Schema(description = "被管理用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("被管理用户ID") + private Long subordinateUserId; + + @Schema(description = "生效开始时间") + @ExcelProperty("生效开始时间") + private LocalDateTime effectiveFrom; + + @Schema(description = "生效结束时间") + @ExcelProperty("生效结束时间") + private LocalDateTime effectiveUntil; + + @Schema(description = "备注", example = "直属上级关系") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationSaveReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationSaveReqVO.java new file mode 100644 index 0000000..0dfc6e0 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationSaveReqVO.java @@ -0,0 +1,33 @@ +package com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户带人关系创建/修改 Request VO") +@Data +public class UserManagementRelationSaveReqVO { + + @Schema(description = "主键ID", example = "1024") + private Long id; + + @Schema(description = "管理者用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "管理者用户ID不能为空") + private Long managerUserId; + + @Schema(description = "被管理用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "被管理用户ID不能为空") + private Long subordinateUserId; + + @Schema(description = "生效开始时间") + private LocalDateTime effectiveFrom; + + @Schema(description = "生效结束时间") + private LocalDateTime effectiveUntil; + + @Schema(description = "备注", example = "直属上级关系") + private String remark; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationTreeRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationTreeRespVO.java new file mode 100644 index 0000000..6eae919 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/userManagementRelation/UserManagementRelationTreeRespVO.java @@ -0,0 +1,60 @@ +package com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 用户带人关系树形 Response VO + * + * 用于前端树形控件展示用户的上下级层级关系 + * 包含关系记录的主键ID,便于前端执行删除和更新操作 + * + * @author hongawen + */ +@Schema(description = "管理后台 - 用户带人关系树形 Response VO") +@Data +public class UserManagementRelationTreeRespVO { + + /** + * 关系记录主键ID + * 用于前端执行删除和更新操作 + */ + @Schema(description = "关系记录主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + /** + * 用户ID + */ + @Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long userId; + + /** + * 用户昵称 + */ + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + private String userNickname; + + /** + * 上级用户ID + * 最高领导此字段为null + */ + @Schema(description = "上级用户ID", example = "1") + private Long managerUserId; + + /** + * 上级用户昵称 + * 最高领导此字段为null + */ + @Schema(description = "上级用户昵称", example = "李四") + private String managerNickname; + + /** + * 下级用户列表 + * 基层员工此字段为空列表 + */ + @Schema(description = "下级用户列表") + private List children; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/UserManagementRelationDO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/UserManagementRelationDO.java new file mode 100644 index 0000000..5eb769f --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/UserManagementRelationDO.java @@ -0,0 +1,77 @@ +package com.njcn.rdms.module.system.dal.dataobject.user; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +/** + * 用户带人关系表 DO + * + * 用于存储用户之间的直属上下级管理关系 + * 每条记录代表一个管理者与被管理者之间的关系 + * + * 表名:system_user_management_relation + * + * 业务场景: + * - 组织架构中的直属上下级关系管理 + * - 支持关系的生效时间范围设置 + * - 一个用户可以有多个上级,但只有一个直属上级 + * - 一个用户可以有多个下属 + * + * @author dklive + */ +@TableName("system_user_management_relation") +@Data +@EqualsAndHashCode(callSuper = true) +public class UserManagementRelationDO extends BaseDO { + + /** + * 主键ID + */ + @TableId + private Long id; + + /** + * 管理者用户ID + * + * 表示上级用户的ID,即管理者的用户ID + * 对应 system_users 表的 id 字段 + */ + private Long managerUserId; + + /** + * 被管理用户ID + * + * 表示下级用户的ID,即被管理者的用户ID + * 对应 system_users 表的 id 字段 + */ + private Long subordinateUserId; + + /** + * 生效开始时间 + * + * 关系开始生效的时间 + * 为空表示立即长期生效 + */ + private LocalDateTime effectiveFrom; + + /** + * 生效结束时间 + * + * 关系失效的时间 + * 为空表示长期有效 + */ + private LocalDateTime effectiveUntil; + + /** + * 备注 + * + * 用于记录关系的额外说明信息 + */ + private String remark; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/UserManagementRelationMapper.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/UserManagementRelationMapper.java new file mode 100644 index 0000000..aeabeed --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/UserManagementRelationMapper.java @@ -0,0 +1,76 @@ +package com.njcn.rdms.module.system.dal.mysql.user; + +import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX; +import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationQueryReqVO; +import com.njcn.rdms.module.system.dal.dataobject.user.UserManagementRelationDO; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 用户带人关系 Mapper 接口 + * + * 提供用户带人关系表的数据访问操作 + * 继承 BaseMapperX 获得基础的 CRUD 功能 + * + * @author hongawen + */ +@Mapper +public interface UserManagementRelationMapper extends BaseMapperX { + + /** + * 根据查询条件查询用户带人关系列表 + * + * 支持的查询条件: + * - managerUserId:管理者用户ID,精确匹配 + * - subordinateUserId:被管理用户ID,精确匹配 + * + * 排序规则:按主键ID降序排列 + * + * @param reqVO 查询条件VO + * @return 用户带人关系DO列表 + */ + default List selectList(UserManagementRelationQueryReqVO reqVO) { + LocalDateTime now = LocalDateTime.now(); + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(UserManagementRelationDO::getManagerUserId, reqVO.getManagerUserId()) + .eqIfPresent(UserManagementRelationDO::getSubordinateUserId, reqVO.getSubordinateUserId()) + .orderByDesc(UserManagementRelationDO::getId) + // (from IS NULL OR from <= now) + .and(w -> w.isNull(UserManagementRelationDO::getEffectiveFrom) + .or().le(UserManagementRelationDO::getEffectiveFrom, now)) + // (until IS NULL OR until >= now) + .and(w -> w.isNull(UserManagementRelationDO::getEffectiveUntil) + .or().ge(UserManagementRelationDO::getEffectiveUntil, now)) + ); + } + + /** + * 根据管理者用户ID查询其下属关系列表 + * + * 查询指定用户作为管理者时的所有带人关系记录 + * 用于获取某个用户的所有直接下属 + * + * @param managerUserId 管理者用户ID + * @return 用户带人关系DO列表 + */ + default List selectListByManagerUserId(Long managerUserId) { + return selectList(UserManagementRelationDO::getManagerUserId, managerUserId); + } + + /** + * 根据被管理者用户ID查询其上级关系列表 + * + * 查询指定用户作为被管理者时的所有带人关系记录 + * 用于获取某个用户的所有直接上级 + * + * @param subordinateUserId 被管理者用户ID + * @return 用户带人关系DO列表 + */ + default List selectListBySubordinateUserId(Long subordinateUserId) { + return selectList(UserManagementRelationDO::getSubordinateUserId, subordinateUserId); + } + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/UserManagementRelationService.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/UserManagementRelationService.java new file mode 100644 index 0000000..e3b4540 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/UserManagementRelationService.java @@ -0,0 +1,115 @@ +package com.njcn.rdms.module.system.service.user; + +import cn.hutool.core.collection.CollUtil; +import com.njcn.rdms.framework.common.util.collection.CollectionUtils; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationQueryReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationSaveReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationTreeRespVO; +import com.njcn.rdms.module.system.dal.dataobject.user.UserManagementRelationDO; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 用户带人关系 Service 接口 + * + * @author dklive + */ +public interface UserManagementRelationService { + + /** + * 创建用户带人关系 + * + * @param createReqVO 关系信息 + * @return 关系编号 + */ + Long createRelation(UserManagementRelationSaveReqVO createReqVO); + + /** + * 更新用户带人关系 + * + * @param updateReqVO 关系信息 + */ + void updateRelation(UserManagementRelationSaveReqVO updateReqVO); + + /** + * 删除用户带人关系 + * + * @param id 关系编号 + */ + void deleteRelation(Long id); + + /** + * 批量删除用户带人关系 + * + * @param ids 关系编号数组 + */ + void deleteRelationList(List ids); + + /** + * 获得用户带人关系 + * + * @param id 关系编号 + * @return 用户带人关系 + */ + UserManagementRelationDO getRelation(Long id); + + /** + * 获得用户带人关系列表 + * + * @param ids 关系编号数组 + * @return 用户带人关系列表 + */ + List getRelationList(Collection ids); + + /** + * 获得用户带人关系列表 + * + * @param reqVO 查询条件 + * @return 用户带人关系列表 + */ + List getRelationQuery(UserManagementRelationQueryReqVO reqVO); + + /** + * 根据管理者用户ID获得带人关系列表 + * + * @param managerUserId 管理者用户ID + * @return 带人关系列表 + */ + List getRelationListByManagerUserId(Long managerUserId); + + /** + * 根据被管理者用户ID获得带人关系列表 + * + * @param subordinateUserId 被管理者用户ID + * @return 带人关系列表 + */ + List getRelationListBySubordinateUserId(Long subordinateUserId); + + /** + * 获得用户带人关系树形结构 + * + * 构建用户上下级关系的树形结构,用于前端树形控件展示 + * - 最高领导:没有上级,作为树的根节点 + * - 基层员工:没有下级,children为空列表 + * + * @return 用户带人关系树形列表 + */ + List getRelationTree(); + + /** + * 获得用户带人关系 Map + * + * @param ids 关系编号数组 + * @return 用户带人关系 Map + */ + default Map getRelationMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return new HashMap<>(); + } + return CollectionUtils.convertMap(getRelationList(ids), UserManagementRelationDO::getId); + } + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/UserManagementRelationServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/UserManagementRelationServiceImpl.java new file mode 100644 index 0000000..7d4db40 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/UserManagementRelationServiceImpl.java @@ -0,0 +1,597 @@ +package com.njcn.rdms.module.system.service.user; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.google.common.annotations.VisibleForTesting; +import com.njcn.rdms.framework.common.exception.ServiceException; +import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationQueryReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationSaveReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.userManagementRelation.UserManagementRelationTreeRespVO; +import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO; +import com.njcn.rdms.module.system.dal.dataobject.user.UserManagementRelationDO; +import com.njcn.rdms.module.system.dal.mysql.user.AdminUserMapper; +import com.njcn.rdms.module.system.dal.mysql.user.UserManagementRelationMapper; +import jakarta.annotation.Resource; +import lombok.Data; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_MANAGEMENT_RELATION_MANAGER_EXISTS; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_MANAGEMENT_RELATION_NOT_FOUND; + +/** + * 用户带人关系 Service 实现类 + * 提供用户带人关系的增删改查功能,以及树形结构构建功能 + * 树形结构用于前端展示用户的上下级层级关系 + * 业务规则: + * 1. 一个用户只能有一个直属上级 + * 2. 一个用户可以有多个下属 + * 3. 可以将自己设置为被管理者(自己管理自己) + * + * @author dklive + */ +@Service +@Validated +public class UserManagementRelationServiceImpl implements UserManagementRelationService { + + @Resource + private UserManagementRelationMapper userManagementRelationMapper; + + @Resource + private AdminUserMapper adminUserMapper; + + /** + * 树形结构构建上下文 + * 包含构建树形结构所需的所有基础数据 + */ + @Data + private static class TreeBuildContext { + private Map userMap; + private Map> managerToSubordinatesMap; + private Map subordinateToRelationMap; + private Set hasManagerUserIds; + private Set allUserIds; + } + + /** + * 创建用户带人关系 + * 业务逻辑: + * 1. 校验被管理者是否已有直属上级(一个用户只能有一个直属上级) + * 2. 插入关系记录 + * + * @param createReqVO 创建请求VO + * @return 关系记录主键ID + */ + @Override + public Long createRelation(UserManagementRelationSaveReqVO createReqVO) { + validateRelationForCreateOrUpdate(null, createReqVO.getSubordinateUserId()); + UserManagementRelationDO relation = BeanUtils.toBean(createReqVO, UserManagementRelationDO.class); + userManagementRelationMapper.insert(relation); + return relation.getId(); + } + + /** + * 更新用户带人关系 + * 业务逻辑: + * 1. 校验关系记录是否存在 + * 2. 校验被管理者是否已有其他直属上级(一个用户只能有一个直属上级) + * 3. 更新关系记录 + * + * @param updateReqVO 更新请求VO + */ + @Override + public void updateRelation(UserManagementRelationSaveReqVO updateReqVO) { + validateRelationForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getSubordinateUserId()); + UserManagementRelationDO updateObj = BeanUtils.toBean(updateReqVO, UserManagementRelationDO.class); + userManagementRelationMapper.updateById(updateObj); + } + + /** + * 删除用户带人关系 + * 业务逻辑: + * 1. 校验关系记录是否存在 + * 2. 根据主键ID删除关系记录 + * + * @param id 关系记录主键ID + */ + @Override + public void deleteRelation(Long id) { + validateRelationExists(id); + userManagementRelationMapper.deleteById(id); + } + + /** + * 批量删除用户带人关系 + * 业务逻辑: + * 根据主键ID列表批量删除关系记录 + * + * @param ids 关系记录主键ID列表 + */ + @Override + public void deleteRelationList(List ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + userManagementRelationMapper.deleteByIds(ids); + } + + /** + * 校验关系记录的合法性 + * 校验规则: + * 1. 更新时,校验关系记录是否存在 + * 2. 被管理者用户只能有一个直属上级(创建时校验,更新时校验是否更换了被管理者) + * + * @param id 关系记录主键ID(创建时为null) + * @param subordinateUserId 被管理者用户ID + */ + private void validateRelationForCreateOrUpdate(Long id, Long subordinateUserId) { + validateRelationExists(id); + validateSubordinateHasNoOtherManager(id, subordinateUserId); + } + + /** + * 校验被管理者是否已有其他直属上级 + * 业务规则:一个用户只能有一个直属上级 + * 校验逻辑: + * 1. 查询该被管理者用户的所有关系记录 + * 2. 如果存在其他关系记录(排除当前记录),则抛出异常 + * + * @param excludeId 排除的关系记录主键ID(创建时为null,更新时为当前记录ID) + * @param subordinateUserId 被管理者用户ID + * @throws ServiceException 被管理者已有其他直属上级时抛出异常 + */ + private void validateSubordinateHasNoOtherManager(Long excludeId, Long subordinateUserId) { + if (subordinateUserId == null) { + return; + } + List existingRelations = + userManagementRelationMapper.selectListBySubordinateUserId(subordinateUserId); + if (CollUtil.isEmpty(existingRelations)) { + return; + } + if (excludeId == null) { + throw exception(USER_MANAGEMENT_RELATION_MANAGER_EXISTS); + } + boolean hasOtherManager = existingRelations.stream() + .anyMatch(relation -> !relation.getId().equals(excludeId)); + if (hasOtherManager) { + throw exception(USER_MANAGEMENT_RELATION_MANAGER_EXISTS); + } + } + + /** + * 校验关系记录是否存在 + * + * @param id 关系记录主键ID + * @throws ServiceException 关系记录不存在时抛出异常 + */ + @VisibleForTesting + void validateRelationExists(Long id) { + if (id == null) { + return; + } + if (userManagementRelationMapper.selectById(id) == null) { + throw exception(USER_MANAGEMENT_RELATION_NOT_FOUND); + } + } + + /** + * 根据主键ID获取用户带人关系 + * + * @param id 关系记录主键ID + * @return 用户带人关系DO + */ + @Override + public UserManagementRelationDO getRelation(Long id) { + return userManagementRelationMapper.selectOne( + buildValidQuery().eq(UserManagementRelationDO::getId, id) + ); + } + + /** + * 根据主键ID列表获取用户带人关系列表 + * + * @param ids 关系记录主键ID列表 + * @return 用户带人关系DO列表 + */ + @Override + public List getRelationList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return userManagementRelationMapper.selectList( + buildValidQuery().in(UserManagementRelationDO::getId, ids) + ); + } + + /** + * 根据查询条件获取用户带人关系树形结构 + * 业务逻辑: + * 1. 构建树形结构上下文(包含所有基础数据) + * 2. 根据查询条件确定目标用户ID(subordinateUserId优先于managerUserId) + * 3. 如果有目标用户,只构建包含该用户的分支(上级链 + 下级树) + * 4. 如果没有目标用户,返回完整的树形结构 + *

+ * 查询参数优先级: + * - 当subordinateUserId和managerUserId同时存在时,优先使用subordinateUserId + * - 正常业务场景下,两个参数只会传递其中一个 + *

+ * 边界情况处理: + * - 两个参数都为null:返回完整的树形结构(等同于getRelationTree) + * - subordinateUserId不为null:构建包含该用户的上级链和下级树,不包含同级节点 + * - managerUserId不为null:构建以该用户为根节点的完整下级树 + * + * @param reqVO 查询条件VO,包含subordinateUserId或managerUserId + * @return 用户带人关系树形结构列表 + */ + @Override + public List getRelationQuery(UserManagementRelationQueryReqVO reqVO) { + TreeBuildContext context = buildTreeContext(); + if (context == null) { + return Collections.emptyList(); + } + + Long targetUserId = determineTargetUserId(reqVO, context); + if (targetUserId == null) { + return buildFullTree(context); + } + + return buildTargetBranchTree(targetUserId, context); + } + + /** + * 根据管理者用户ID获取其下属关系列表 + * + * @param managerUserId 管理者用户ID + * @return 用户带人关系DO列表 + */ + @Override + public List getRelationListByManagerUserId(Long managerUserId) { + return userManagementRelationMapper.selectListByManagerUserId(managerUserId); + } + + /** + * 根据被管理者用户ID获取其上级关系列表 + * 注意:由于一个用户只能有一个直属上级,此方法最多返回一条记录 + * + * @param subordinateUserId 被管理者用户ID + * @return 用户带人关系DO列表 + */ + @Override + public List getRelationListBySubordinateUserId(Long subordinateUserId) { + return userManagementRelationMapper.selectListBySubordinateUserId(subordinateUserId); + } + + /** + * 获取用户带人关系树形结构 + * 业务逻辑: + * 1. 构建树形结构上下文(包含所有基础数据) + * 2. 找出所有根节点(没有上级的用户,即最高领导;也包括自己管理自己的用户) + * 3. 从根节点开始递归构建完整的树形结构 + *

+ * 边界情况处理: + * - 根节点(还没有设置直属上级的用户):managerUserId设置为自己的userId,managerNickname为自己的昵称 + * - 自己管理自己的用户:managerUserId等于userId,managerNickname为自己的昵称 + * - 基层员工:没有下级,children为空列表 + * - 空数据:返回空列表 + * + * @return 用户带人关系树形结构列表 + */ + @Override + public List getRelationTree() { + TreeBuildContext context = buildTreeContext(); + if (context == null) { + return Collections.emptyList(); + } + return buildFullTree(context); + } + + /** + * 构建树形结构上下文 + * 查询并组装构建树形结构所需的所有基础数据 + * + * @return 树形结构上下文,如果没有数据则返回null + */ + private TreeBuildContext buildTreeContext() { + List allRelations = userManagementRelationMapper.selectList(new UserManagementRelationQueryReqVO()); + if (CollUtil.isEmpty(allRelations)) { + return null; + } + + Set allUserIds = new HashSet<>(); + for (UserManagementRelationDO relation : allRelations) { + allUserIds.add(relation.getManagerUserId()); + allUserIds.add(relation.getSubordinateUserId()); + } + + List users = adminUserMapper.selectByIds(allUserIds); + Map userMap = users.stream() + .collect(Collectors.toMap(AdminUserDO::getId, user -> user)); + + Map> managerToSubordinatesMap = allRelations.stream() + .collect(Collectors.groupingBy( + UserManagementRelationDO::getManagerUserId, + Collectors.mapping(UserManagementRelationDO::getSubordinateUserId, Collectors.toList()) + )); + + Map subordinateToRelationMap = allRelations.stream() + .collect(Collectors.toMap( + UserManagementRelationDO::getSubordinateUserId, + relation -> relation, + (existing, replacement) -> existing + )); + + Set hasManagerUserIds = allRelations.stream() + .filter(relation -> !relation.getManagerUserId().equals(relation.getSubordinateUserId())) + .map(UserManagementRelationDO::getSubordinateUserId) + .collect(Collectors.toSet()); + + TreeBuildContext context = new TreeBuildContext(); + context.setUserMap(userMap); + context.setManagerToSubordinatesMap(managerToSubordinatesMap); + context.setSubordinateToRelationMap(subordinateToRelationMap); + context.setHasManagerUserIds(hasManagerUserIds); + context.setAllUserIds(allUserIds); + return context; + } + + /** + * 构建完整的树形结构 + * 从所有根节点开始构建完整的树形结构 + * + * @param context 树形结构上下文 + * @return 完整的树形结构列表 + */ + private List buildFullTree(TreeBuildContext context) { + List rootUserIds = context.getAllUserIds().stream() + .filter(userId -> !context.getHasManagerUserIds().contains(userId)) + .toList(); + + List treeList = new ArrayList<>(); + for (Long rootUserId : rootUserIds) { + UserManagementRelationTreeRespVO rootNode = buildRootNode(rootUserId, context); + if (rootNode != null) { + treeList.add(rootNode); + } + } + return treeList; + } + + /** + * 构建目标用户相关的分支树形结构 + * 只包含目标用户的上级链和下级树,不包含同级节点 + *

+ * 构建逻辑: + * 1. 从目标用户向上追溯到根节点,记录上级链 + * 2. 从根节点开始,只沿着包含目标用户的分支向下构建 + * 3. 构建目标用户的所有下级 + * + * @param targetUserId 目标用户ID + * @param context 树形结构上下文 + * @return 目标用户相关的分支树形结构列表 + */ + private List buildTargetBranchTree(Long targetUserId, TreeBuildContext context) { + LinkedList pathToRoot = findPathToRoot(targetUserId, context); + if (pathToRoot.isEmpty()) { + return Collections.emptyList(); + } + + Long rootUserId = pathToRoot.getFirst(); + UserManagementRelationTreeRespVO rootNode = buildRootNode(rootUserId, context); + if (rootNode == null) { + return Collections.emptyList(); + } + + if (pathToRoot.size() > 1) { + buildBranchPath(rootNode, pathToRoot, context); + } + + return Collections.singletonList(rootNode); + } + + /** + * 从目标用户向上追溯到根节点,记录路径 + * + * @param targetUserId 目标用户ID + * @param context 树形结构上下文 + * @return 从根节点到目标用户的路径(包含根节点和目标用户) + */ + private LinkedList findPathToRoot(Long targetUserId, TreeBuildContext context) { + LinkedList path = new LinkedList<>(); + Set visited = new HashSet<>(); + Long currentUserId = targetUserId; + + while (currentUserId != null && !visited.contains(currentUserId)) { + visited.add(currentUserId); + path.addFirst(currentUserId); + + if (!context.getHasManagerUserIds().contains(currentUserId)) { + break; + } + + UserManagementRelationDO relation = context.getSubordinateToRelationMap().get(currentUserId); + if (relation == null) { + break; + } + + Long managerUserId = relation.getManagerUserId(); + if (managerUserId.equals(currentUserId)) { + break; + } + currentUserId = managerUserId; + } + + return path; + } + + /** + * 沿着指定路径构建分支 + * 从根节点的下一层开始,只构建路径中包含的用户节点 + * + * @param parentNode 父节点 + * @param pathToRoot 从根节点到目标用户的路径 + * @param context 树形结构上下文 + */ + private void buildBranchPath(UserManagementRelationTreeRespVO parentNode, LinkedList pathToRoot, TreeBuildContext context) { + if (pathToRoot.size() <= 1) { + return; + } + + Long parentUserId = parentNode.getUserId(); + List subordinateIds = context.getManagerToSubordinatesMap().get(parentUserId); + if (CollUtil.isEmpty(subordinateIds)) { + return; + } + + Long nextUserIdInPath = pathToRoot.get(1); + for (Long subordinateId : subordinateIds) { + if (subordinateId.equals(parentUserId)) { + continue; + } + + UserManagementRelationDO childRelation = context.getSubordinateToRelationMap().get(subordinateId); + Long childRelationId = childRelation != null ? childRelation.getId() : null; + + if (subordinateId.equals(nextUserIdInPath)) { + UserManagementRelationTreeRespVO childNode = buildTreeNode(childRelationId, subordinateId, parentUserId, context); + if (childNode != null) { + parentNode.setChildren(Collections.singletonList(childNode)); + if (pathToRoot.size() > 2) { + LinkedList remainingPath = new LinkedList<>(pathToRoot.subList(1, pathToRoot.size())); + buildBranchPath(childNode, remainingPath, context); + } + } + break; + } + } + } + + /** + * 构建根节点 + * + * @param rootUserId 根节点用户ID + * @param context 树形结构上下文 + * @return 根节点 + */ + private UserManagementRelationTreeRespVO buildRootNode(Long rootUserId, TreeBuildContext context) { + UserManagementRelationDO rootRelation = context.getSubordinateToRelationMap().get(rootUserId); + Long rootRelationId = rootRelation != null ? rootRelation.getId() : null; + Long rootManagerUserId; + if (rootRelation == null) { + rootManagerUserId = rootUserId; + } else if (rootRelation.getManagerUserId().equals(rootRelation.getSubordinateUserId())) { + rootManagerUserId = rootUserId; + } else { + rootManagerUserId = rootRelation.getManagerUserId(); + } + return buildTreeNode(rootRelationId, rootUserId, rootManagerUserId, context); + } + + /** + * 确定目标用户ID + * 根据查询参数优先级确定目标用户: + * 1. 如果subordinateUserId不为null,返回subordinateUserId + * 2. 如果managerUserId不为null,返回managerUserId + * 3. 否则返回null(表示查询所有) + * + * @param reqVO 查询条件VO + * @param context 树形结构上下文 + * @return 目标用户ID,如果没有指定则返回null + */ + private Long determineTargetUserId(UserManagementRelationQueryReqVO reqVO, TreeBuildContext context) { + if (reqVO == null) { + return null; + } + if (reqVO.getSubordinateUserId() != null && context.getAllUserIds().contains(reqVO.getSubordinateUserId())) { + return reqVO.getSubordinateUserId(); + } + if (reqVO.getManagerUserId() != null && context.getAllUserIds().contains(reqVO.getManagerUserId())) { + return reqVO.getManagerUserId(); + } + return null; + } + + /** + * 构建树形节点 + * 构建逻辑: + * 1. 根据用户ID获取用户信息 + * 2. 设置节点的基本信息(关系记录ID、用户ID、用户昵称) + * 3. 设置上级信息(上级用户ID、上级用户昵称) + * 4. 递归构建所有下级节点列表 + * + * @param relationId 关系记录主键ID(根节点且非自己管理自己时为null) + * @param userId 当前用户ID + * @param managerUserId 上级用户ID(根节点时为自己的userId) + * @param context 树形结构上下文 + * @return 树形节点 + */ + private UserManagementRelationTreeRespVO buildTreeNode(Long relationId, Long userId, Long managerUserId, + TreeBuildContext context) { + AdminUserDO user = context.getUserMap().get(userId); + if (user == null) { + return null; + } + + UserManagementRelationTreeRespVO node = new UserManagementRelationTreeRespVO(); + node.setId(relationId); + node.setUserId(userId); + node.setUserNickname(user.getNickname()); + node.setManagerUserId(managerUserId); + + if (managerUserId != null) { + AdminUserDO managerUser = context.getUserMap().get(managerUserId); + if (managerUser != null) { + node.setManagerNickname(managerUser.getNickname()); + } + } + + List subordinateIds = context.getManagerToSubordinatesMap().get(userId); + if (CollUtil.isNotEmpty(subordinateIds)) { + List children = new ArrayList<>(); + for (Long subordinateId : subordinateIds) { + if (subordinateId.equals(userId)) { + continue; + } + UserManagementRelationDO childRelation = context.getSubordinateToRelationMap().get(subordinateId); + Long childRelationId = childRelation != null ? childRelation.getId() : null; + UserManagementRelationTreeRespVO childNode = buildTreeNode(childRelationId, subordinateId, userId, context); + if (childNode != null) { + children.add(childNode); + } + } + node.setChildren(children); + } else { + node.setChildren(Collections.emptyList()); + } + + return node; + } + + /** + * 统一时间有效性过滤条件: + *

+ * 1.如果这两个字段都是null,那就直接查出来 + * 2.如果effective_util是null、effective_from不是null,那就只需要当前时间>=effective_from就行 + * 3.如果effective_from是null、effective_util不是null,那就只需要当前时间<=effective_util就行 + *

+ * 如果上面三个条件都不满足,那就判断为此条关系已经失效,不查出来 + */ + private LambdaQueryWrapper buildValidQuery() { + LocalDateTime now = LocalDateTime.now(); + return new LambdaQueryWrapper() + // (from IS NULL OR from <= now) + .and(w -> w.isNull(UserManagementRelationDO::getEffectiveFrom) + .or().le(UserManagementRelationDO::getEffectiveFrom, now)) + // (until IS NULL OR until >= now) + .and(w -> w.isNull(UserManagementRelationDO::getEffectiveUntil) + .or().ge(UserManagementRelationDO::getEffectiveUntil, now)); + // BaseMapperX 通常已自动处理 deleted 字段,无需额外加 + } + +}