refactor(project): 重构权限常量定义并移除需求进度聚合功能
- 将产品和项目查询权限码统一提取到常量类中 - 移除需求进度聚合计算的相关实现代码 - 更新权限验证注解使用新的常量定义 - 清理相关的单元测试代码 - 更新错误码注释说明
This commit is contained in:
@@ -128,8 +128,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_ALREADY_EXISTS = new ErrorCode(1_008_003_004, "该用户已是当前执行的有效协办人");
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_NOT_EXISTS = new ErrorCode(1_008_003_005, "执行协办人不存在");
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_NOT_ACTIVE = new ErrorCode(1_008_003_006, "当前执行协办人已失效");
|
||||
// 保留:TD-013 解锁后业务路径已不会再触发,预留用于灰度回滚关闭关联能力
|
||||
ErrorCode PROJECT_EXECUTION_REQUIREMENT_NOT_READY = new ErrorCode(1_008_003_007, "当前阶段不支持给执行绑定项目需求");
|
||||
// 1_008_003_007 原 PROJECT_EXECUTION_REQUIREMENT_NOT_READY 已随 TD-013 清理删除,号位保留空缺不复用,避免与历史前端映射冲突
|
||||
ErrorCode PROJECT_EXECUTION_NOT_ALLOW_EDIT = new ErrorCode(1_008_003_008, "当前项目状态不允许维护执行");
|
||||
ErrorCode PROJECT_EXECUTION_OWNER_HANDOFF_REQUIRED = new ErrorCode(1_008_003_009, "该项目成员仍担任未终态执行负责人,请先完成执行负责人交接");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_003_010, "执行状态定义不存在或已停用");
|
||||
|
||||
@@ -28,6 +28,11 @@ public final class ProductObjectConstants {
|
||||
*/
|
||||
public static final String CODE_PREFIX = "CNPD";
|
||||
|
||||
/**
|
||||
* 产品查询权限码。
|
||||
*/
|
||||
public static final String PERMISSION_QUERY = "project:product:query";
|
||||
|
||||
/**
|
||||
* 产品编辑权限码。
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,11 @@ public final class ProjectObjectConstants {
|
||||
*/
|
||||
public static final String CODE_PREFIX = "CNPJ";
|
||||
|
||||
/**
|
||||
* 项目查询权限码。
|
||||
*/
|
||||
public static final String PERMISSION_QUERY = "project:project:query";
|
||||
|
||||
/**
|
||||
* 项目编辑权限码。
|
||||
*/
|
||||
|
||||
@@ -97,10 +97,8 @@ public class ProjectRequirementRespVO {
|
||||
@Schema(description = "是否为终态", example = "false")
|
||||
private Boolean terminal;
|
||||
|
||||
@Schema(description = "需求进度(TD-016 读时聚合,service 层批量计算)。"
|
||||
+ "公式:AVG(该需求自己承接的执行进度 ∪ 直接子需求进度),"
|
||||
+ "排除 rdms_object_status_model.progress_excluded_flag=1 的执行状态(当前为 cancelled);"
|
||||
+ "无任何执行且无子需求时返回 0.00。两位小数,HALF_UP。",
|
||||
@Schema(description = "需求进度。TD-016:读时聚合计算已下线(前端当前不展示需求进度,进度仅项目/执行/任务展示),"
|
||||
+ "字段保留占位、当前恒为 null;未来需要展示需求进度时再恢复服务端聚合计算。",
|
||||
example = "0.65")
|
||||
private BigDecimal progressRate;
|
||||
|
||||
|
||||
@@ -76,11 +76,11 @@ public class ProductRequirementServiceImpl implements ProductRequirementService
|
||||
|
||||
// 权限常量
|
||||
private static final String PRODUCT_CREATE_PERMISSION = "project:product:create";
|
||||
private static final String PRODUCT_QUERY_PERMISSION = "project:product:query";
|
||||
private static final String PRODUCT_UPDATE_PERMISSION = "project:product:update";
|
||||
private static final String PRODUCT_STATUS_PERMISSION = "project:product:status";
|
||||
private static final String PRODUCT_QUERY_PERMISSION = ProductObjectConstants.PERMISSION_QUERY;
|
||||
private static final String PRODUCT_UPDATE_PERMISSION = ProductObjectConstants.PERMISSION_UPDATE;
|
||||
private static final String PRODUCT_STATUS_PERMISSION = ProductObjectConstants.PERMISSION_STATUS;
|
||||
private static final String PRODUCT_REVIEW_PERMISSION = ProductObjectConstants.PERMISSION_REVIEW;
|
||||
private static final String PRODUCT_DELETE_PERMISSION = "project:product:delete";
|
||||
private static final String PRODUCT_DELETE_PERMISSION = ProductObjectConstants.PERMISSION_DELETE;
|
||||
private static final String PRODUCT_SPLIT_PERMISSION = "project:product:split";
|
||||
|
||||
// 审计动作常量
|
||||
|
||||
@@ -88,7 +88,7 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService
|
||||
STATUS_REJECTED, STATUS_CANCELLED);
|
||||
|
||||
private static final String PROJECT_CREATE_PERMISSION = "project:project:create";
|
||||
private static final String PROJECT_QUERY_PERMISSION = "project:project:query";
|
||||
private static final String PROJECT_QUERY_PERMISSION = ProjectObjectConstants.PERMISSION_QUERY;
|
||||
private static final String PROJECT_UPDATE_PERMISSION = ProjectObjectConstants.PERMISSION_UPDATE;
|
||||
private static final String PROJECT_STATUS_PERMISSION = ProjectObjectConstants.PERMISSION_STATUS;
|
||||
private static final String PROJECT_REVIEW_PERMISSION = ProjectObjectConstants.PERMISSION_REVIEW;
|
||||
@@ -219,7 +219,6 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService
|
||||
ProjectRequirementDO requirement = validateRequirementExists(id);
|
||||
validateRequirementBelongsToProject(requirement, projectId);
|
||||
ProjectRequirementRespVO respVO = buildRequirementRespVO(requirement);
|
||||
fillRequirementProgress(projectId, List.of(respVO));
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@@ -241,7 +240,6 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService
|
||||
List<ProjectRequirementRespVO> list = pageResult.getList().stream()
|
||||
.map(requirement -> buildRequirementRespVO(requirement, statusModelMap))
|
||||
.collect(Collectors.toList());
|
||||
fillRequirementProgress(pageReqVO.getProjectId(), list);
|
||||
return new PageResult<>(list, pageResult.getTotal());
|
||||
}
|
||||
|
||||
@@ -296,7 +294,6 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService
|
||||
List<ProjectRequirementRespVO> list = pagedRootRequirements.stream()
|
||||
.map(requirement -> buildRequirementRespVOWithPathChildren(requirement, pathNodeIds, childrenMap, statusModelMap))
|
||||
.collect(Collectors.toList());
|
||||
fillRequirementProgress(projectId, list);
|
||||
return new PageResult<>(list, (long) total);
|
||||
}
|
||||
|
||||
@@ -1134,124 +1131,6 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService
|
||||
return respVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* TD-016:把项目需求 RespVO 列表上的 progressRate 字段批量回填。
|
||||
* <p>口径:按 projectId 一次性算出本项目下所有需求的进度 map,再递归扫 RespVO 树(含 children)应用。
|
||||
* 公式:R.progressRate = AVG(R 自己挂的执行进度 ∪ R 的直接子需求进度),排除进度排除字典命中的执行状态;
|
||||
* 当 pool 为空(无承接执行且无子需求)时返回 0.00。两位小数 HALF_UP。
|
||||
*/
|
||||
private void fillRequirementProgress(Long projectId, Collection<ProjectRequirementRespVO> respVOList) {
|
||||
if (respVOList == null || respVOList.isEmpty() || projectId == null) {
|
||||
return;
|
||||
}
|
||||
Map<Long, BigDecimal> progressMap = computeRequirementProgressMapByProjectId(projectId);
|
||||
applyProgressRecursive(respVOList, progressMap);
|
||||
}
|
||||
|
||||
private void applyProgressRecursive(Collection<ProjectRequirementRespVO> list, Map<Long, BigDecimal> progressMap) {
|
||||
BigDecimal defaultValue = normalizeProgress(BigDecimal.ZERO);
|
||||
for (ProjectRequirementRespVO vo : list) {
|
||||
vo.setProgressRate(progressMap.getOrDefault(vo.getId(), defaultValue));
|
||||
if (vo.getChildren() != null && !vo.getChildren().isEmpty()) {
|
||||
applyProgressRecursive(vo.getChildren(), progressMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 projectId 算所有需求进度,返回 Map<requirementId, progressRate>。
|
||||
* 公式与 fillRequirementProgress 对齐:自下而上 DFS,pool = 自己挂的执行平均 ∪ 直接子需求进度。
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Map<Long, BigDecimal> computeRequirementProgressMapByProjectId(Long projectId) {
|
||||
List<ProjectRequirementDO> allRequirements = requirementMapper.selectList(
|
||||
new LambdaQueryWrapperX<ProjectRequirementDO>()
|
||||
.eq(ProjectRequirementDO::getProjectId, projectId));
|
||||
if (allRequirements.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Set<Long> requirementIds = allRequirements.stream()
|
||||
.map(ProjectRequirementDO::getId)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
|
||||
// 一次 GROUP BY 拉到所有需求"自己挂的执行平均进度"
|
||||
List<String> excludedStatusCodes = loadProgressExcludedExecutionStatusCodes();
|
||||
Map<Long, BigDecimal> ownAvgMap = new HashMap<>();
|
||||
List<Map<String, Object>> rows = projectExecutionMapper
|
||||
.selectAvgProgressGroupByProjectRequirementIds(requirementIds, excludedStatusCodes);
|
||||
if (rows != null) {
|
||||
for (Map<String, Object> row : rows) {
|
||||
Object idObj = row.get("projectRequirementId");
|
||||
Object progressObj = row.get("progressRate");
|
||||
if (idObj == null || progressObj == null) {
|
||||
continue;
|
||||
}
|
||||
Long reqId = ((Number) idObj).longValue();
|
||||
BigDecimal avg = progressObj instanceof BigDecimal
|
||||
? (BigDecimal) progressObj
|
||||
: new BigDecimal(progressObj.toString());
|
||||
ownAvgMap.put(reqId, avg);
|
||||
}
|
||||
}
|
||||
|
||||
// 按 parentId 建子需求索引(顶级父 id = 0)
|
||||
Map<Long, List<ProjectRequirementDO>> childrenIndex = allRequirements.stream()
|
||||
.collect(Collectors.groupingBy(r -> r.getParentId() == null ? 0L : r.getParentId()));
|
||||
|
||||
// DFS 自下而上递归
|
||||
Map<Long, BigDecimal> result = new HashMap<>();
|
||||
for (ProjectRequirementDO r : allRequirements) {
|
||||
if (!result.containsKey(r.getId())) {
|
||||
computeProgressDfs(r, childrenIndex, ownAvgMap, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal computeProgressDfs(ProjectRequirementDO r,
|
||||
Map<Long, List<ProjectRequirementDO>> childrenIndex,
|
||||
Map<Long, BigDecimal> ownAvgMap,
|
||||
Map<Long, BigDecimal> result) {
|
||||
if (result.containsKey(r.getId())) {
|
||||
return result.get(r.getId());
|
||||
}
|
||||
List<BigDecimal> pool = new ArrayList<>();
|
||||
BigDecimal ownAvg = ownAvgMap.get(r.getId());
|
||||
if (ownAvg != null) {
|
||||
pool.add(ownAvg);
|
||||
}
|
||||
List<ProjectRequirementDO> children = childrenIndex.getOrDefault(r.getId(), List.of());
|
||||
for (ProjectRequirementDO child : children) {
|
||||
pool.add(computeProgressDfs(child, childrenIndex, ownAvgMap, result));
|
||||
}
|
||||
BigDecimal value;
|
||||
if (pool.isEmpty()) {
|
||||
value = normalizeProgress(BigDecimal.ZERO);
|
||||
} else {
|
||||
BigDecimal sum = pool.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
value = sum.divide(BigDecimal.valueOf(pool.size()), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
result.put(r.getId(), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 rdms_object_status_model 字典动态读取执行的"进度排除状态"列表,
|
||||
* 当前命中 cancelled;任何时候运维通过 progress_excluded_flag 增减,service 层无需重新部署。
|
||||
*/
|
||||
private List<String> loadProgressExcludedExecutionStatusCodes() {
|
||||
List<String> codes = statusModelMapper
|
||||
.selectProgressExcludedStatusCodesByObjectTypeEnabled(ProjectExecutionConstants.OBJECT_TYPE);
|
||||
return codes == null ? Collections.emptyList() : codes;
|
||||
}
|
||||
|
||||
private BigDecimal normalizeProgress(BigDecimal value) {
|
||||
if (value == null) {
|
||||
return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
return value.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private List<ProjectRequirementModuleRespVO> buildModuleTree(List<ProjectRequirementModuleDO> modules, Long parentId) {
|
||||
return modules.stream()
|
||||
.filter(module -> Objects.equals(module.getParentId(), parentId))
|
||||
|
||||
@@ -79,7 +79,7 @@ public class RequirementReviewServiceImpl implements RequirementReviewService {
|
||||
|
||||
@Override
|
||||
@CheckObjectPermission(objectType = ProductObjectConstants.OBJECT_TYPE, objectId = "#productId",
|
||||
permission = "project:product:query")
|
||||
permission = ProductObjectConstants.PERMISSION_QUERY)
|
||||
public RequirementReviewRespVO getProductRequirementReview(Long productId, Long requirementId) {
|
||||
ProductRequirementDO requirement = productRequirementMapper.selectById(requirementId);
|
||||
if (requirement == null || !Objects.equals(requirement.getProductId(), productId)) {
|
||||
@@ -117,7 +117,7 @@ public class RequirementReviewServiceImpl implements RequirementReviewService {
|
||||
|
||||
@Override
|
||||
@CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#projectId",
|
||||
permission = "project:project:query")
|
||||
permission = ProjectObjectConstants.PERMISSION_QUERY)
|
||||
public RequirementReviewRespVO getProjectRequirementReview(Long projectId, Long requirementId) {
|
||||
ProjectRequirementDO requirement = projectRequirementMapper.selectById(requirementId);
|
||||
if (requirement == null || !Objects.equals(requirement.getProjectId(), projectId)) {
|
||||
|
||||
@@ -129,130 +129,4 @@ class ProjectRequirementServiceImplTest extends BaseMockitoUnitTest {
|
||||
return requirement;
|
||||
}
|
||||
|
||||
// ============== TD-016 进度聚合 ==============
|
||||
|
||||
/**
|
||||
* TD-016:叶子需求无承接执行 → 进度 0.00。
|
||||
*/
|
||||
@Test
|
||||
void computeRequirementProgress_whenLeafHasNoExecution_shouldBeZero() {
|
||||
Long projectId = 2001L;
|
||||
ProjectRequirementDO leaf = buildRequirementWithParent(9001L, projectId, "implementing", 0L);
|
||||
when(requirementMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
|
||||
.thenReturn(List.of(leaf));
|
||||
when(statusModelMapper.selectProgressExcludedStatusCodesByObjectTypeEnabled(ProjectExecutionConstants.OBJECT_TYPE))
|
||||
.thenReturn(List.of("cancelled"));
|
||||
when(projectExecutionMapper.selectAvgProgressGroupByProjectRequirementIds(any(), any()))
|
||||
.thenReturn(List.of());
|
||||
|
||||
Map<Long, BigDecimal> result = projectRequirementService.computeRequirementProgressMapByProjectId(projectId);
|
||||
|
||||
assertEquals(new BigDecimal("0.00"), result.get(9001L));
|
||||
}
|
||||
|
||||
/**
|
||||
* TD-016:叶子需求有 N 个执行,AVG(progress_rate) 即结果。
|
||||
* (SQL 层已经 GROUP BY 平均;service 只是把那个平均值放回 pool 平均,pool 大小 1 等于自身)
|
||||
*/
|
||||
@Test
|
||||
void computeRequirementProgress_whenLeafHasExecutions_shouldUseAvg() {
|
||||
Long projectId = 2001L;
|
||||
ProjectRequirementDO leaf = buildRequirementWithParent(9001L, projectId, "implementing", 0L);
|
||||
when(requirementMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
|
||||
.thenReturn(List.of(leaf));
|
||||
when(statusModelMapper.selectProgressExcludedStatusCodesByObjectTypeEnabled(ProjectExecutionConstants.OBJECT_TYPE))
|
||||
.thenReturn(List.of("cancelled"));
|
||||
when(projectExecutionMapper.selectAvgProgressGroupByProjectRequirementIds(any(), any()))
|
||||
.thenReturn(List.of(rowOf(9001L, "0.60")));
|
||||
|
||||
Map<Long, BigDecimal> result = projectRequirementService.computeRequirementProgressMapByProjectId(projectId);
|
||||
|
||||
assertEquals(new BigDecimal("0.60"), result.get(9001L));
|
||||
}
|
||||
|
||||
/**
|
||||
* TD-016:父需求 = AVG(自己挂的执行的平均, 每个直接子需求进度)。
|
||||
* 例:父 9000 自己挂的执行平均 0.40;子 9001(叶子)执行平均 0.80;
|
||||
* 父 = AVG(0.40, 0.80) = 0.60。子 9001 = 0.80。
|
||||
*/
|
||||
@Test
|
||||
void computeRequirementProgress_whenParentHasOwnExecutionsAndChildren_shouldAveragePool() {
|
||||
Long projectId = 2001L;
|
||||
ProjectRequirementDO parent = buildRequirementWithParent(9000L, projectId, "implementing", 0L);
|
||||
ProjectRequirementDO child = buildRequirementWithParent(9001L, projectId, "implementing", 9000L);
|
||||
when(requirementMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
|
||||
.thenReturn(List.of(parent, child));
|
||||
when(statusModelMapper.selectProgressExcludedStatusCodesByObjectTypeEnabled(ProjectExecutionConstants.OBJECT_TYPE))
|
||||
.thenReturn(List.of("cancelled"));
|
||||
when(projectExecutionMapper.selectAvgProgressGroupByProjectRequirementIds(any(), any()))
|
||||
.thenReturn(List.of(rowOf(9000L, "0.40"), rowOf(9001L, "0.80")));
|
||||
|
||||
Map<Long, BigDecimal> result = projectRequirementService.computeRequirementProgressMapByProjectId(projectId);
|
||||
|
||||
assertEquals(new BigDecimal("0.80"), result.get(9001L));
|
||||
assertEquals(new BigDecimal("0.60"), result.get(9000L));
|
||||
}
|
||||
|
||||
/**
|
||||
* TD-016:父需求无自挂执行、有 2 个子需求 → 父 = AVG(子1, 子2)。
|
||||
*/
|
||||
@Test
|
||||
void computeRequirementProgress_whenParentHasNoOwnExecutionPureChildren_shouldAverageChildren() {
|
||||
Long projectId = 2001L;
|
||||
ProjectRequirementDO parent = buildRequirementWithParent(9000L, projectId, "implementing", 0L);
|
||||
ProjectRequirementDO child1 = buildRequirementWithParent(9001L, projectId, "implementing", 9000L);
|
||||
ProjectRequirementDO child2 = buildRequirementWithParent(9002L, projectId, "implementing", 9000L);
|
||||
when(requirementMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
|
||||
.thenReturn(List.of(parent, child1, child2));
|
||||
when(statusModelMapper.selectProgressExcludedStatusCodesByObjectTypeEnabled(ProjectExecutionConstants.OBJECT_TYPE))
|
||||
.thenReturn(List.of("cancelled"));
|
||||
when(projectExecutionMapper.selectAvgProgressGroupByProjectRequirementIds(any(), any()))
|
||||
.thenReturn(List.of(rowOf(9001L, "0.50"), rowOf(9002L, "1.00")));
|
||||
|
||||
Map<Long, BigDecimal> result = projectRequirementService.computeRequirementProgressMapByProjectId(projectId);
|
||||
|
||||
assertEquals(new BigDecimal("0.50"), result.get(9001L));
|
||||
assertEquals(new BigDecimal("1.00"), result.get(9002L));
|
||||
assertEquals(new BigDecimal("0.75"), result.get(9000L));
|
||||
}
|
||||
|
||||
/**
|
||||
* TD-016:三层结构。爷父(9000) → 父(9100) → 叶子(9101),每层都挂执行;
|
||||
* 验证自下而上递归正确。
|
||||
* 叶子 9101 自挂执行 0.80 → 9101 = 0.80
|
||||
* 父 9100 自挂执行 0.40 + 子 0.80 → AVG = 0.60
|
||||
* 爷 9000 自挂执行 0.20 + 子 0.60 → AVG = 0.40
|
||||
*/
|
||||
@Test
|
||||
void computeRequirementProgress_multiLevel_shouldRecurseFromBottomUp() {
|
||||
Long projectId = 2001L;
|
||||
ProjectRequirementDO grand = buildRequirementWithParent(9000L, projectId, "implementing", 0L);
|
||||
ProjectRequirementDO parent = buildRequirementWithParent(9100L, projectId, "implementing", 9000L);
|
||||
ProjectRequirementDO leaf = buildRequirementWithParent(9101L, projectId, "implementing", 9100L);
|
||||
when(requirementMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
|
||||
.thenReturn(List.of(grand, parent, leaf));
|
||||
when(statusModelMapper.selectProgressExcludedStatusCodesByObjectTypeEnabled(ProjectExecutionConstants.OBJECT_TYPE))
|
||||
.thenReturn(List.of("cancelled"));
|
||||
when(projectExecutionMapper.selectAvgProgressGroupByProjectRequirementIds(any(), any()))
|
||||
.thenReturn(List.of(rowOf(9000L, "0.20"), rowOf(9100L, "0.40"), rowOf(9101L, "0.80")));
|
||||
|
||||
Map<Long, BigDecimal> result = projectRequirementService.computeRequirementProgressMapByProjectId(projectId);
|
||||
|
||||
assertEquals(new BigDecimal("0.80"), result.get(9101L));
|
||||
assertEquals(new BigDecimal("0.60"), result.get(9100L));
|
||||
assertEquals(new BigDecimal("0.40"), result.get(9000L));
|
||||
}
|
||||
|
||||
private ProjectRequirementDO buildRequirementWithParent(Long id, Long projectId, String statusCode, Long parentId) {
|
||||
ProjectRequirementDO requirement = buildRequirement(id, projectId, statusCode);
|
||||
requirement.setParentId(parentId);
|
||||
return requirement;
|
||||
}
|
||||
|
||||
private Map<String, Object> rowOf(Long requirementId, String avg) {
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("projectRequirementId", requirementId);
|
||||
row.put("progressRate", new BigDecimal(avg));
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user