diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleanorder/CleanOrderServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleanorder/CleanOrderServiceImpl.java index 9b0de33..1dd0195 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleanorder/CleanOrderServiceImpl.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleanorder/CleanOrderServiceImpl.java @@ -30,6 +30,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.Duration; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -485,22 +486,34 @@ public class CleanOrderServiceImpl implements CleanOrderService { } }) .map(Long::parseLong) - .filter(pid -> !pid.equals(areaId)) // 排除当前区域,避免重复 + .filter(pid -> !pid.equals(areaId)) // 排除当前区域,避免循环引用 .collect(Collectors.toList()); - // 5. 无有效父级,直接返回区域名称 - if (parentIds.isEmpty()) { + // 5. 在ID层面去重相邻重复的ID(只去除数据错误导致的重复,保留不同ID的相同名称) + List deduplicatedIds = new ArrayList<>(); + Long lastId = null; + for (Long parentId : parentIds) { + if (!parentId.equals(lastId)) { + deduplicatedIds.add(parentId); + lastId = parentId; + } else { + log.warn("检测到parent_path中重复的ID: areaId={}, duplicateId={}", areaId, parentId); + } + } + + // 6. 无有效父级,直接返回区域名称 + if (deduplicatedIds.isEmpty()) { return area.getAreaName(); } - // 6. 批量查询所有父级区域(避免 N+1 查询) - List parents = opsBusAreaMapper.selectBatchIds(parentIds); + // 7. 批量查询所有父级区域(避免 N+1 查询) + List parents = opsBusAreaMapper.selectBatchIds(deduplicatedIds); if (parents == null || parents.isEmpty()) { - log.warn("未找到父级区域: areaId={}, parentIds={}", areaId, parentIds); + log.warn("未找到父级区域: areaId={}, parentIds={}", areaId, deduplicatedIds); return area.getAreaName(); } - // 7. 构建ID到区域的映射 + // 8. 构建ID到区域的映射 Map parentNameMap = parents.stream() .collect(Collectors.toMap( OpsBusAreaDO::getId, @@ -508,13 +521,18 @@ public class CleanOrderServiceImpl implements CleanOrderService { (existing, replacement) -> existing // 处理重复key )); - // 8. 按顺序拼接区域路径 - String path = parentIds.stream() + // 9. 按顺序拼接区域路径(保持ID顺序) + List pathSegments = deduplicatedIds.stream() .filter(parentNameMap::containsKey) // 过滤掉不存在的父级 .map(parentNameMap::get) - .collect(Collectors.joining("/")); + .collect(Collectors.toList()); - // 9. 拼接当前区域名称 - return StrUtil.isNotBlank(path) ? path + "/" + area.getAreaName() : area.getAreaName(); + // 10. 拼接完整路径 + String path = String.join("/", pathSegments); + if (StrUtil.isBlank(path)) { + return area.getAreaName(); + } + + return path + "/" + area.getAreaName(); } } diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceImpl.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceImpl.java index 9fd3ff5..30245ca 100644 --- a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceImpl.java +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceImpl.java @@ -170,7 +170,7 @@ public class OpsBusAreaServiceImpl implements OpsBusAreaService { return null; } if (parent.getParentPath() == null || parent.getParentPath().isEmpty()) { - return String.valueOf(parentId); + return "/" + parentId; } return parent.getParentPath() + "/" + parentId; } @@ -187,9 +187,10 @@ public class OpsBusAreaServiceImpl implements OpsBusAreaService { if (target == null || target.getParentPath() == null) { return false; } - return target.getParentPath().contains("/" + sourceId + "/") - || target.getParentPath().startsWith(sourceId + "/") - || target.getParentPath().endsWith("/" + sourceId); + // parent_path 格式:/1/2/3 + // 判断 sourceId 是否在 parent_path 中 + String path = target.getParentPath(); + return path.contains("/" + sourceId + "/") || path.endsWith("/" + sourceId); } @Override diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/test/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceTest.java b/viewsh-module-ops/viewsh-module-ops-biz/src/test/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceTest.java index 42099d1..310c5c3 100644 --- a/viewsh-module-ops/viewsh-module-ops-biz/src/test/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceTest.java +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/test/java/com/viewsh/module/ops/service/area/OpsBusAreaServiceTest.java @@ -149,7 +149,7 @@ class OpsBusAreaServiceTest { OpsBusAreaDO parentArea = OpsBusAreaDO.builder() .id(1L) - .parentPath("10") + .parentPath("/10") // parent_path 现在以 / 开头 .build(); when(opsBusAreaMapper.selectById(1L)).thenReturn(parentArea); @@ -166,7 +166,7 @@ class OpsBusAreaServiceTest { assertNotNull(areaId); verify(opsBusAreaMapper, times(1)).insert(argThat((OpsBusAreaDO area) -> "A栋".equals(area.getAreaName()) && - "10/1".equals(area.getParentPath()) // PBT: parent_path 不变性 + "/10/1".equals(area.getParentPath()) // PBT: parent_path 以 / 开头 )); }