接口调整

This commit is contained in:
2026-03-26 14:01:22 +08:00
parent 10d4d6104c
commit ddd5c5a493
8 changed files with 465 additions and 0 deletions

View File

@@ -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, "同一组织下该用户的负责人时间区间存在重叠");
}

View File

@@ -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<Long> createOrgLeaderRelation(@Valid @RequestBody OrgLeaderRelationSaveReqVO createReqVO) {
return success(orgLeaderRelationService.createOrgLeaderRelation(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改组织负责人关系")
@PreAuthorize("@ss.hasPermission('system:org-leader:update')")
public CommonResult<Boolean> 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<Boolean> 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<List<OrgLeaderRelationRespVO>> getOrgLeaderRelationListByDept(@RequestParam("deptId") Long deptId) {
List<OrgLeaderRelationDO> relations = orgLeaderRelationService.getOrgLeaderRelationListByDeptId(deptId);
List<OrgLeaderRelationRespVO> respList = BeanUtils.toBean(relations, OrgLeaderRelationRespVO.class);
Map<Long, AdminUserDO> 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<List<UserSimpleRespVO>> getCandidateUsers(@RequestParam("deptId") Long deptId) {
List<AdminUserDO> users = orgLeaderRelationService.getCandidateUsersByDeptId(deptId);
Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(users, AdminUserDO::getDeptId));
return success(UserConvert.INSTANCE.convertSimpleList(users, deptMap));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<OrgLeaderRelationDO> {
default List<OrgLeaderRelationDO> selectListByDeptId(Long deptId) {
return selectList(new LambdaQueryWrapperX<OrgLeaderRelationDO>()
.eq(OrgLeaderRelationDO::getDeptId, deptId)
.orderByDesc(OrgLeaderRelationDO::getId));
}
default List<OrgLeaderRelationDO> selectListByDeptIdAndUserId(Long deptId, Long userId) {
return selectList(new LambdaQueryWrapperX<OrgLeaderRelationDO>()
.eq(OrgLeaderRelationDO::getDeptId, deptId)
.eq(OrgLeaderRelationDO::getUserId, userId)
.orderByDesc(OrgLeaderRelationDO::getId));
}
}

View File

@@ -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<OrgLeaderRelationDO> getOrgLeaderRelationListByDeptId(Long deptId);
/**
* 查询组织负责人候选用户
*
* @param deptId 组织 ID
* @return 候选用户列表
*/
List<AdminUserDO> getCandidateUsersByDeptId(Long deptId);
}

View File

@@ -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<OrgLeaderRelationDO> getOrgLeaderRelationListByDeptId(Long deptId) {
validateDeptExists(deptId);
return orgLeaderRelationMapper.selectListByDeptId(deptId);
}
@Override
public List<AdminUserDO> getCandidateUsersByDeptId(Long deptId) {
validateDeptExists(deptId);
Set<Long> deptScope = new HashSet<>(deptService.getChildDeptIdListFromCache(deptId));
deptScope.add(deptId);
List<AdminUserDO> 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<Long> 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<OrgLeaderRelationDO> 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;
}
}