Compare commits

2 Commits

5 changed files with 199 additions and 33 deletions

View File

@@ -142,4 +142,16 @@ public class ProductRequirementController {
return success(requirementService.getRequirementModuleTree(productId));
}
@GetMapping("/status/dict")
@Operation(summary = "获取需求所有状态字典")
public CommonResult<List<ProductRequirementStatusDictRespVO>> getRequirementStatusDict() {
return success(requirementService.getRequirementStatusDict());
}
@GetMapping("/status/dict/terminal")
@Operation(summary = "获取需求终止态状态字典")
public CommonResult<List<ProductRequirementStatusDictRespVO>> getRequirementTerminalStatusDict() {
return success(requirementService.getRequirementTerminalStatusDict());
}
}

View File

@@ -0,0 +1,28 @@
package com.njcn.rdms.module.project.controller.admin.product.vo.requirement;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 管理后台 - 产品需求状态字典 Response VO
*/
@Schema(description = "管理后台 - 产品需求状态字典 Response VO")
@Data
public class ProductRequirementStatusDictRespVO {
@Schema(description = "状态编码", example = "pending_confirm")
private String statusCode;
@Schema(description = "状态名称", example = "待确认")
private String statusName;
@Schema(description = "排序值", example = "1")
private Integer sort;
@Schema(description = "是否初始状态", example = "true")
private Boolean initialFlag;
@Schema(description = "是否终态", example = "false")
private Boolean terminalFlag;
}

View File

@@ -20,6 +20,22 @@ public interface ProductRequirementMapper extends BaseMapperX<ProductRequirement
* 分页查询需求列表
*/
default PageResult<ProductRequirementDO> selectPage(ProductRequirementPageReqVO reqVO) {
LambdaQueryWrapperX<ProductRequirementDO> queryWrapper = buildQueryWrapper(reqVO);
return selectPage(reqVO, queryWrapper);
}
/**
* 查询所有符合条件的需求列表(不分页,用于树形查询)
*/
default List<ProductRequirementDO> selectList(ProductRequirementPageReqVO reqVO) {
LambdaQueryWrapperX<ProductRequirementDO> queryWrapper = buildQueryWrapper(reqVO);
return selectList(queryWrapper);
}
/**
* 构建查询条件
*/
private LambdaQueryWrapperX<ProductRequirementDO> buildQueryWrapper(ProductRequirementPageReqVO reqVO) {
LambdaQueryWrapperX<ProductRequirementDO> queryWrapper = new LambdaQueryWrapperX<>();
// 标题模糊搜索
if (StringUtils.hasText(reqVO.getTitle())) {
@@ -46,7 +62,7 @@ public interface ProductRequirementMapper extends BaseMapperX<ProductRequirement
// 按排序值升序,再按创建时间降序
.orderByAsc(ProductRequirementDO::getSort)
.orderByDesc(ProductRequirementDO::getCreateTime);
return selectPage(reqVO, queryWrapper);
return queryWrapper;
}
/**

View File

@@ -123,11 +123,25 @@ public interface ProductRequirementService {
void deleteRequirementModule(Long moduleId, Long productId);
/**
* 获取产品需求模块树
* 获取需求模块树
*
* @param productId 产品编号
* @return 模块树
*/
List<ProductRequirementModuleRespVO> getRequirementModuleTree(Long productId);
/**
* 获取需求所有状态字典列表
*
* @return 状态字典列表
*/
List<ProductRequirementStatusDictRespVO> getRequirementStatusDict();
/**
* 获取需求终止态状态字典列表
*
* @return 终止态状态字典列表
*/
List<ProductRequirementStatusDictRespVO> getRequirementTerminalStatusDict();
}

View File

@@ -24,9 +24,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -191,27 +189,110 @@ public class ProductRequirementServiceImpl implements ProductRequirementService
@CheckObjectPermission(objectType = PRODUCT_OBJECT_TYPE, objectId = "#pageReqVO.productId",
permission = PRODUCT_QUERY_PERMISSION)
public PageResult<ProductRequirementRespVO> getRequirementTree(ProductRequirementPageReqVO pageReqVO) {
System.out.println("--------------");
System.out.println(pageReqVO);
// 查询当前产品下的所有顶级需求
Long moduleId = pageReqVO.getModuleId();
Long productId = pageReqVO.getProductId();
// 处理模块过滤条件:仅当选中具体模块(非“全部需求”)时,递归加载子模块ID进行过滤
// 处理模块过滤条件仅当选中具体模块时递归加载子模块ID进行过滤
if (moduleId != null) {
pageReqVO.setModuleIds(getAllModuleIdsWithChildren(moduleId, productId));
// 清空moduleId避免与moduleIds冲突Mapper中优先使用moduleIds做IN查询
pageReqVO.setModuleId(null);
}
// 固定只查询父需求parentId = 0L子需求通过递归加载
pageReqVO.setParentId(0L);
PageResult<ProductRequirementDO> pageResult = requirementMapper.selectPage(pageReqVO);
// 清空parentId查询所有符合条件的需求不限层级
pageReqVO.setParentId(null);
// 构建树形结构(子需求不计入分页)
List<ProductRequirementRespVO> list = pageResult.getList().stream()
.map(this::buildRequirementRespVOWithChildren)
// 第一步:查询所有符合搜索条件的需求
List<ProductRequirementDO> matchedRequirements = requirementMapper.selectList(pageReqVO);
if (matchedRequirements.isEmpty()) {
return new PageResult<>(Collections.emptyList(), 0L);
}
// 第二步找出所有匹配需求的根节点ID同时收集路径上的所有节点ID
Set<Long> rootIds = new HashSet<>();
Set<Long> pathNodeIds = new HashSet<>();
Map<Long, ProductRequirementDO> requirementCache = new HashMap<>();
for (ProductRequirementDO req : matchedRequirements) {
requirementCache.put(req.getId(), req);
pathNodeIds.add(req.getId());
Long rootId = findRootRequirementIdAndCollectPath(req, requirementCache, pathNodeIds);
rootIds.add(rootId);
}
// 第三步:查询根需求详情并按创建时间倒排
List<ProductRequirementDO> rootRequirements = requirementMapper.selectBatchIds(rootIds);
rootRequirements.sort((a, b) -> b.getCreateTime().compareTo(a.getCreateTime()));
// 第四步:对根节点列表进行内存分页
int pageNo = pageReqVO.getPageNo() != null ? pageReqVO.getPageNo() : 1;
int pageSize = pageReqVO.getPageSize() != null ? pageReqVO.getPageSize() : 10;
int total = rootRequirements.size();
int fromIndex = (pageNo - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, total);
if (fromIndex >= total) {
return new PageResult<>(Collections.emptyList(), (long) total);
}
List<ProductRequirementDO> pagedRootRequirements = rootRequirements.subList(fromIndex, toIndex);
// 第五步:构建树形结构(只包含路径上的节点)
List<ProductRequirementRespVO> list = pagedRootRequirements.stream()
.map(req -> buildRequirementRespVOWithPathChildren(req, pathNodeIds))
.collect(Collectors.toList());
return new PageResult<>(list, pageResult.getTotal());
return new PageResult<>(list, (long) total);
}
/**
* 向上追溯需求的根节点ID同时收集路径上的所有节点ID
* @param requirement 起始需求
* @param cache 需求缓存(避免重复查询)
* @param pathNodeIds 路径节点ID集合输出参数
* @return 根节点IDparentId = 0L 的需求ID
*/
private Long findRootRequirementIdAndCollectPath(ProductRequirementDO requirement,
Map<Long, ProductRequirementDO> cache,
Set<Long> pathNodeIds) {
if (requirement.getParentId() == null || requirement.getParentId() == 0L) {
return requirement.getId();
}
// 从缓存中查找父需求,如果没有则查询数据库
ProductRequirementDO parent = cache.get(requirement.getParentId());
if (parent == null) {
parent = requirementMapper.selectById(requirement.getParentId());
if (parent != null) {
cache.put(parent.getId(), parent);
}
}
if (parent == null) {
// 父需求不存在(数据异常),返回当前需求作为根
return requirement.getId();
}
// 收集路径上的节点ID
pathNodeIds.add(parent.getId());
return findRootRequirementIdAndCollectPath(parent, cache, pathNodeIds);
}
/**
* 构建需求响应VO只包含路径上的子需求
* @param requirement 需求
* @param pathNodeIds 路径节点ID集合
* @return 需求响应VO
*/
private ProductRequirementRespVO buildRequirementRespVOWithPathChildren(ProductRequirementDO requirement,
Set<Long> pathNodeIds) {
ProductRequirementRespVO respVO = buildRequirementRespVO(requirement);
// 查询子需求
List<ProductRequirementDO> allChildren = requirementMapper.selectListByParentId(requirement.getId());
// 只保留路径上的子需求
List<ProductRequirementDO> pathChildren = allChildren.stream()
.filter(child -> pathNodeIds.contains(child.getId()))
.toList();
if (!pathChildren.isEmpty()) {
respVO.setChildren(pathChildren.stream()
.map(child -> buildRequirementRespVOWithPathChildren(child, pathNodeIds))
.collect(Collectors.toList()));
}
return respVO;
}
/**
@@ -586,6 +667,36 @@ public class ProductRequirementServiceImpl implements ProductRequirementService
return buildModuleTree(modules, 0L);
}
@Override
public List<ProductRequirementStatusDictRespVO> getRequirementStatusDict() {
List<ObjectStatusModelDO> statusModels = statusModelMapper.selectListByObjectType(REQUIREMENT_OBJECT_TYPE);
return statusModels.stream()
.map(this::buildStatusDictRespVO)
.collect(Collectors.toList());
}
@Override
public List<ProductRequirementStatusDictRespVO> getRequirementTerminalStatusDict() {
List<ObjectStatusModelDO> statusModels = statusModelMapper.selectListByObjectType(REQUIREMENT_OBJECT_TYPE);
return statusModels.stream()
.filter(ObjectStatusModelDO::getTerminalFlag)
.map(this::buildStatusDictRespVO)
.collect(Collectors.toList());
}
/**
* 构建状态字典响应VO
*/
private ProductRequirementStatusDictRespVO buildStatusDictRespVO(ObjectStatusModelDO statusModel) {
ProductRequirementStatusDictRespVO respVO = new ProductRequirementStatusDictRespVO();
respVO.setStatusCode(statusModel.getStatusCode());
respVO.setStatusName(statusModel.getStatusName());
respVO.setSort(statusModel.getSort());
respVO.setInitialFlag(statusModel.getInitialFlag());
respVO.setTerminalFlag(statusModel.getTerminalFlag());
return respVO;
}
// ========== 私有辅助方法 ==========
/**
@@ -607,21 +718,6 @@ public class ProductRequirementServiceImpl implements ProductRequirementService
return respVO;
}
/**
* 构建需求响应VO含子需求
*/
private ProductRequirementRespVO buildRequirementRespVOWithChildren(ProductRequirementDO requirement) {
ProductRequirementRespVO respVO = buildRequirementRespVO(requirement);
// 查询子需求
List<ProductRequirementDO> children = requirementMapper.selectListByParentId(requirement.getId());
if (!children.isEmpty()) {
respVO.setChildren(children.stream()
.map(this::buildRequirementRespVOWithChildren)
.collect(Collectors.toList()));
}
return respVO;
}
/**
* 构建模块树
*/
@@ -723,7 +819,7 @@ public class ProductRequirementServiceImpl implements ProductRequirementService
if (existModule == null) {
return;
}
if (moduleId == null || !existModule.getId().equals(moduleId)) {
if (!existModule.getId().equals(moduleId)) {
throw exception(ErrorCodeConstants.REQUIREMENT_MODULE_NAME_DUPLICATE, moduleName.trim());
}
}