fix(ops): 修复工单location字段重复和parent_path格式问题
1. 修改 parent_path 格式为以 "/" 开头(如:/1/2/3)
- buildParentPath: 父级是根节点时返回 "/1" 而非 "1"
2. 修复 buildAreaPath 方法的去重逻辑
- 从名称层面去重改为 ID 层面去重
- 避免误删不同ID但名称相同的合法情况
- 只去除数据错误导致的重复ID
- 添加警告日志记录重复ID
3. 调整 isDescendant 方法以适配新的 parent_path 格式
- 简化判断逻辑,移除冗余的 startsWith 检查
4. 更新测试用例以匹配新格式
- Mock数据: parentPath("10") -> parentPath("/10")
- 期望值: "10/1" -> "/10/1"
5. 统一 location 路径格式(不带前导斜杠)
- 示例: "徐汇万科中心一期/A座写字楼/11楼/电梯厅"
变更影响:
- ops_bus_area.parent_path: "1" -> "/1", "1/2" -> "/1/2"
- ops_order.location: 无前导斜杠
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<Long> 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<OpsBusAreaDO> parents = opsBusAreaMapper.selectBatchIds(parentIds);
|
||||
// 7. 批量查询所有父级区域(避免 N+1 查询)
|
||||
List<OpsBusAreaDO> 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<Long, String> 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<String> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 以 / 开头
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user