From ddd5c5a4936f045a91022427c8157acda2b00239 Mon Sep 17 00:00:00 2001 From: hongawen <83944980@qq.com> Date: Thu, 26 Mar 2026 14:01:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/enums/ErrorCodeConstants.java | 5 + .../dept/OrgLeaderRelationController.java | 101 +++++++++++ .../vo/orgleader/OrgLeaderRelationRespVO.java | 39 +++++ .../orgleader/OrgLeaderRelationSaveReqVO.java | 35 ++++ .../dataobject/dept/OrgLeaderRelationDO.java | 47 +++++ .../mysql/dept/OrgLeaderRelationMapper.java | 26 +++ .../dept/OrgLeaderRelationService.java | 52 ++++++ .../dept/OrgLeaderRelationServiceImpl.java | 160 ++++++++++++++++++ 8 files changed, 465 insertions(+) create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/OrgLeaderRelationController.java create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationRespVO.java create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationSaveReqVO.java create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dept/OrgLeaderRelationDO.java create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/OrgLeaderRelationMapper.java create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationService.java create mode 100644 rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationServiceImpl.java 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 6a37b68..f5e0706 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 @@ -138,4 +138,9 @@ public interface ErrorCodeConstants { ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置"); + ErrorCode ORG_LEADER_RELATION_NOT_FOUND = new ErrorCode(1_002_004_100, "当前组织负责人关系不存在"); + ErrorCode ORG_LEADER_USER_NOT_IN_DEPT = new ErrorCode(1_002_004_101, "用户({})不在当前组织范围内,不能设置为负责人"); + ErrorCode ORG_LEADER_EFFECTIVE_RANGE_INVALID = new ErrorCode(1_002_004_102, "负责人生效时间区间不合法"); + ErrorCode ORG_LEADER_RELATION_OVERLAP = new ErrorCode(1_002_004_103, "同一组织下该用户的负责人时间区间存在重叠"); + } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/OrgLeaderRelationController.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/OrgLeaderRelationController.java new file mode 100644 index 0000000..09c6210 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/OrgLeaderRelationController.java @@ -0,0 +1,101 @@ +package com.njcn.rdms.module.system.controller.admin.dept; + +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader.OrgLeaderRelationRespVO; +import com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader.OrgLeaderRelationSaveReqVO; +import com.njcn.rdms.module.system.controller.admin.user.vo.user.UserSimpleRespVO; +import com.njcn.rdms.module.system.convert.user.UserConvert; +import com.njcn.rdms.module.system.dal.dataobject.dept.DeptDO; +import com.njcn.rdms.module.system.dal.dataobject.dept.OrgLeaderRelationDO; +import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO; +import com.njcn.rdms.module.system.service.dept.DeptService; +import com.njcn.rdms.module.system.service.dept.OrgLeaderRelationService; +import com.njcn.rdms.module.system.service.user.AdminUserService; +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.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +import static com.njcn.rdms.framework.common.pojo.CommonResult.success; +import static com.njcn.rdms.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 组织负责人关系") +@RestController +@RequestMapping("/system/org-leader") +@Validated +public class OrgLeaderRelationController { + + @Resource + private OrgLeaderRelationService orgLeaderRelationService; + @Resource + private DeptService deptService; + @Resource + private AdminUserService adminUserService; + + @PostMapping("/create") + @Operation(summary = "创建组织负责人关系") + @PreAuthorize("@ss.hasPermission('system:org-leader:create')") + public CommonResult createOrgLeaderRelation(@Valid @RequestBody OrgLeaderRelationSaveReqVO createReqVO) { + return success(orgLeaderRelationService.createOrgLeaderRelation(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改组织负责人关系") + @PreAuthorize("@ss.hasPermission('system:org-leader:update')") + public CommonResult updateOrgLeaderRelation(@Valid @RequestBody OrgLeaderRelationSaveReqVO updateReqVO) { + orgLeaderRelationService.updateOrgLeaderRelation(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除组织负责人关系") + @Parameter(name = "id", description = "负责人关系编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:org-leader:delete')") + public CommonResult deleteOrgLeaderRelation(@RequestParam("id") Long id) { + orgLeaderRelationService.deleteOrgLeaderRelation(id); + return success(true); + } + + @GetMapping("/list-by-dept") + @Operation(summary = "查询组织下的负责人关系列表") + @Parameter(name = "deptId", description = "组织节点 ID", required = true, example = "100") + @PreAuthorize("@ss.hasPermission('system:org-leader:query')") + public CommonResult> getOrgLeaderRelationListByDept(@RequestParam("deptId") Long deptId) { + List relations = orgLeaderRelationService.getOrgLeaderRelationListByDeptId(deptId); + List respList = BeanUtils.toBean(relations, OrgLeaderRelationRespVO.class); + Map userMap = adminUserService.getUserMap(convertList(relations, OrgLeaderRelationDO::getUserId)); + respList.forEach(respVO -> { + AdminUserDO user = userMap.get(respVO.getUserId()); + if (user != null) { + respVO.setUserNickname(user.getNickname()); + } + }); + return success(respList); + } + + @GetMapping("/candidate-users") + @Operation(summary = "查询组织负责人候选用户列表") + @Parameter(name = "deptId", description = "组织节点 ID", required = true, example = "100") + @PreAuthorize("@ss.hasPermission('system:org-leader:query')") + public CommonResult> getCandidateUsers(@RequestParam("deptId") Long deptId) { + List users = orgLeaderRelationService.getCandidateUsersByDeptId(deptId); + Map deptMap = deptService.getDeptMap(convertList(users, AdminUserDO::getDeptId)); + return success(UserConvert.INSTANCE.convertSimpleList(users, deptMap)); + } + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationRespVO.java new file mode 100644 index 0000000..e3eaed1 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationRespVO.java @@ -0,0 +1,39 @@ +package com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 组织负责人关系 Response VO") +@Data +public class OrgLeaderRelationRespVO { + + @Schema(description = "负责人关系编号", example = "1024") + private Long id; + + @Schema(description = "组织节点 ID", example = "100") + private Long deptId; + + @Schema(description = "负责人用户 ID", example = "1") + private Long userId; + + @Schema(description = "负责人用户昵称", example = "管理员") + private String userNickname; + + @Schema(description = "生效开始时间") + private LocalDateTime effectiveFrom; + + @Schema(description = "生效结束时间") + private LocalDateTime effectiveUntil; + + @Schema(description = "备注", example = "部门负责人") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationSaveReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationSaveReqVO.java new file mode 100644 index 0000000..e604048 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/orgleader/OrgLeaderRelationSaveReqVO.java @@ -0,0 +1,35 @@ +package com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 组织负责人关系创建/修改 Request VO") +@Data +public class OrgLeaderRelationSaveReqVO { + + @Schema(description = "负责人关系编号", example = "1024") + private Long id; + + @Schema(description = "组织节点 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "组织节点不能为空") + private Long deptId; + + @Schema(description = "负责人用户 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "负责人用户不能为空") + private Long userId; + + @Schema(description = "生效开始时间") + private LocalDateTime effectiveFrom; + + @Schema(description = "生效结束时间") + private LocalDateTime effectiveUntil; + + @Schema(description = "备注", example = "部门负责人") + @Size(max = 500, message = "备注长度不能超过 500 个字符") + private String remark; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dept/OrgLeaderRelationDO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dept/OrgLeaderRelationDO.java new file mode 100644 index 0000000..d3aad26 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dept/OrgLeaderRelationDO.java @@ -0,0 +1,47 @@ +package com.njcn.rdms.module.system.dal.dataobject.dept; + +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 + */ +@TableName("system_org_leader_relation") +@Data +@EqualsAndHashCode(callSuper = true) +public class OrgLeaderRelationDO extends BaseDO { + + @TableId + private Long id; + + /** + * 组织节点 ID + */ + private Long deptId; + + /** + * 负责人用户 ID + */ + private Long userId; + + /** + * 生效开始时间 + */ + 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/dept/OrgLeaderRelationMapper.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/OrgLeaderRelationMapper.java new file mode 100644 index 0000000..efc94ca --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/OrgLeaderRelationMapper.java @@ -0,0 +1,26 @@ +package com.njcn.rdms.module.system.dal.mysql.dept; + +import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX; +import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.njcn.rdms.module.system.dal.dataobject.dept.OrgLeaderRelationDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface OrgLeaderRelationMapper extends BaseMapperX { + + default List selectListByDeptId(Long deptId) { + return selectList(new LambdaQueryWrapperX() + .eq(OrgLeaderRelationDO::getDeptId, deptId) + .orderByDesc(OrgLeaderRelationDO::getId)); + } + + default List selectListByDeptIdAndUserId(Long deptId, Long userId) { + return selectList(new LambdaQueryWrapperX() + .eq(OrgLeaderRelationDO::getDeptId, deptId) + .eq(OrgLeaderRelationDO::getUserId, userId) + .orderByDesc(OrgLeaderRelationDO::getId)); + } + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationService.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationService.java new file mode 100644 index 0000000..3bd566b --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationService.java @@ -0,0 +1,52 @@ +package com.njcn.rdms.module.system.service.dept; + +import com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader.OrgLeaderRelationSaveReqVO; +import com.njcn.rdms.module.system.dal.dataobject.dept.OrgLeaderRelationDO; +import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO; + +import java.util.List; + +/** + * 组织负责人关系 Service + */ +public interface OrgLeaderRelationService { + + /** + * 创建组织负责人关系 + * + * @param createReqVO 创建请求 + * @return 负责人关系 ID + */ + Long createOrgLeaderRelation(OrgLeaderRelationSaveReqVO createReqVO); + + /** + * 更新组织负责人关系 + * + * @param updateReqVO 更新请求 + */ + void updateOrgLeaderRelation(OrgLeaderRelationSaveReqVO updateReqVO); + + /** + * 删除组织负责人关系 + * + * @param id 关系 ID + */ + void deleteOrgLeaderRelation(Long id); + + /** + * 查询指定组织下的负责人关系列表 + * + * @param deptId 组织 ID + * @return 负责人关系列表 + */ + List getOrgLeaderRelationListByDeptId(Long deptId); + + /** + * 查询组织负责人候选用户 + * + * @param deptId 组织 ID + * @return 候选用户列表 + */ + List getCandidateUsersByDeptId(Long deptId); + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationServiceImpl.java new file mode 100644 index 0000000..635309d --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/OrgLeaderRelationServiceImpl.java @@ -0,0 +1,160 @@ +package com.njcn.rdms.module.system.service.dept; + +import cn.hutool.core.collection.CollUtil; +import com.google.common.annotations.VisibleForTesting; +import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader.OrgLeaderRelationSaveReqVO; +import com.njcn.rdms.module.system.dal.dataobject.dept.DeptDO; +import com.njcn.rdms.module.system.dal.dataobject.dept.OrgLeaderRelationDO; +import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO; +import com.njcn.rdms.module.system.dal.mysql.dept.OrgLeaderRelationMapper; +import com.njcn.rdms.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singleton; + +/** + * 组织负责人关系 Service 实现类 + */ +@Service +@Validated +public class OrgLeaderRelationServiceImpl implements OrgLeaderRelationService { + + @Resource + private OrgLeaderRelationMapper orgLeaderRelationMapper; + @Resource + private DeptService deptService; + @Resource + private AdminUserService adminUserService; + + @Override + public Long createOrgLeaderRelation(OrgLeaderRelationSaveReqVO createReqVO) { + validateOrgLeaderRelationForCreateOrUpdate(null, createReqVO); + OrgLeaderRelationDO relation = BeanUtils.toBean(createReqVO, OrgLeaderRelationDO.class); + orgLeaderRelationMapper.insert(relation); + return relation.getId(); + } + + @Override + public void updateOrgLeaderRelation(OrgLeaderRelationSaveReqVO updateReqVO) { + if (updateReqVO.getId() == null) { + throw exception(ORG_LEADER_RELATION_NOT_FOUND); + } + validateOrgLeaderRelationForCreateOrUpdate(updateReqVO.getId(), updateReqVO); + OrgLeaderRelationDO relation = BeanUtils.toBean(updateReqVO, OrgLeaderRelationDO.class); + orgLeaderRelationMapper.updateById(relation); + } + + @Override + public void deleteOrgLeaderRelation(Long id) { + validateOrgLeaderRelationExists(id); + orgLeaderRelationMapper.deleteById(id); + } + + @Override + public List getOrgLeaderRelationListByDeptId(Long deptId) { + validateDeptExists(deptId); + return orgLeaderRelationMapper.selectListByDeptId(deptId); + } + + @Override + public List getCandidateUsersByDeptId(Long deptId) { + validateDeptExists(deptId); + Set deptScope = new HashSet<>(deptService.getChildDeptIdListFromCache(deptId)); + deptScope.add(deptId); + List users = adminUserService.getUserListByDeptIds(deptScope); + users.removeIf(user -> !adminUserService.isUserAvailable(user)); + users.sort(Comparator.comparing(AdminUserDO::getDeptId) + .thenComparing(AdminUserDO::getNickname) + .thenComparing(AdminUserDO::getId)); + return users; + } + + private void validateOrgLeaderRelationForCreateOrUpdate(Long id, OrgLeaderRelationSaveReqVO reqVO) { + validateOrgLeaderRelationExists(id); + deptService.validateDeptList(singleton(reqVO.getDeptId())); + adminUserService.validateUserList(singleton(reqVO.getUserId())); + validateEffectiveRange(reqVO.getEffectiveFrom(), reqVO.getEffectiveUntil()); + validateUserInDeptScope(reqVO.getDeptId(), reqVO.getUserId()); + validateRelationOverlap(id, reqVO.getDeptId(), reqVO.getUserId(), + reqVO.getEffectiveFrom(), reqVO.getEffectiveUntil()); + } + + @VisibleForTesting + void validateEffectiveRange(LocalDateTime effectiveFrom, LocalDateTime effectiveUntil) { + if (effectiveFrom != null && effectiveUntil != null && effectiveFrom.isAfter(effectiveUntil)) { + throw exception(ORG_LEADER_EFFECTIVE_RANGE_INVALID); + } + } + + @VisibleForTesting + void validateUserInDeptScope(Long deptId, Long userId) { + AdminUserDO user = adminUserService.getUser(userId); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + Set deptScope = new HashSet<>(deptService.getChildDeptIdListFromCache(deptId)); + deptScope.add(deptId); + if (!deptScope.contains(user.getDeptId())) { + throw exception(ORG_LEADER_USER_NOT_IN_DEPT, user.getNickname()); + } + } + + @VisibleForTesting + void validateRelationOverlap(Long id, Long deptId, Long userId, + LocalDateTime effectiveFrom, LocalDateTime effectiveUntil) { + List relations = orgLeaderRelationMapper.selectListByDeptIdAndUserId(deptId, userId); + if (CollUtil.isEmpty(relations)) { + return; + } + for (OrgLeaderRelationDO relation : relations) { + if (Objects.equals(relation.getId(), id)) { + continue; + } + if (isTimeRangeOverlap(effectiveFrom, effectiveUntil, + relation.getEffectiveFrom(), relation.getEffectiveUntil())) { + throw exception(ORG_LEADER_RELATION_OVERLAP); + } + } + } + + private boolean isTimeRangeOverlap(LocalDateTime start1, LocalDateTime end1, + LocalDateTime start2, LocalDateTime end2) { + return !isAfter(start1, end2) && !isAfter(start2, end1); + } + + private boolean isAfter(LocalDateTime start, LocalDateTime end) { + return start != null && end != null && start.isAfter(end); + } + + private OrgLeaderRelationDO validateOrgLeaderRelationExists(Long id) { + if (id == null) { + return null; + } + OrgLeaderRelationDO relation = orgLeaderRelationMapper.selectById(id); + if (relation == null) { + throw exception(ORG_LEADER_RELATION_NOT_FOUND); + } + return relation; + } + + private DeptDO validateDeptExists(Long deptId) { + DeptDO dept = deptService.getDept(deptId); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + return dept; + } + +}