fix: breadcrumbs for hidden category can not be displayed (#6200)

#### What type of PR is this?
/kind bug
/area core
/area theme
/milestone 2.17.x

#### What this PR does / why we need it:
修复获取隐藏分类的面包屑路径不正确的问题

#### Which issue(s) this PR fixes:
Fixes #6197

#### Does this PR introduce a user-facing change?
```release-note
修复获取隐藏分类的面包屑路径不正确的问题
```
pull/6216/head
guqing 2024-06-28 18:02:59 +08:00 committed by GitHub
parent 80e1110da2
commit 4cafdb5a72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 71 additions and 34 deletions

View File

@ -17,6 +17,7 @@ import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -88,12 +89,16 @@ public class CategoryFinderImpl implements CategoryFinder {
@Override @Override
public Flux<CategoryTreeVo> listAsTree() { public Flux<CategoryTreeVo> listAsTree() {
return this.toCategoryTreeVoFlux(null); return listAll()
.collectList()
.flatMapMany(list -> Flux.fromIterable(listToTree(list, null)));
} }
@Override @Override
public Flux<CategoryTreeVo> listAsTree(String name) { public Flux<CategoryTreeVo> listAsTree(String name) {
return this.toCategoryTreeVoFlux(name); return listAllFor(name)
.collectList()
.flatMapMany(list -> Flux.fromIterable(listToTree(list, name)));
} }
@Override @Override
@ -103,43 +108,50 @@ public class CategoryFinderImpl implements CategoryFinder {
.map(CategoryVo::from); .map(CategoryVo::from);
} }
Flux<CategoryVo> listAllFor(String parentName) { private Flux<CategoryVo> listAllFor(String parentName) {
return categoryService.isCategoryHidden(parentName) return Mono.defer(
() -> {
if (StringUtils.isBlank(parentName)) {
return Mono.just(false);
}
return categoryService.isCategoryHidden(parentName);
})
.flatMapMany( .flatMapMany(
isHidden -> client.listAll(Category.class, new ListOptions(), defaultSort()) isHidden -> client.listAll(Category.class, new ListOptions(), defaultSort())
.filter(category -> isHidden || !category.getSpec().isHideFromList()) .filter(category -> {
if (isHidden) {
return true;
}
return !category.getSpec().isHideFromList();
})
.map(CategoryVo::from) .map(CategoryVo::from)
); );
} }
Flux<CategoryTreeVo> toCategoryTreeVoFlux(String name) { private List<CategoryTreeVo> listToTree(List<CategoryVo> categoryVos, @Nullable String name) {
return listAllFor(name) Map<String, CategoryTreeVo> nameIdentityMap = categoryVos.stream()
.collectList() .map(CategoryTreeVo::from)
.flatMapIterable(categoryVos -> { .collect(Collectors.toMap(categoryVo -> categoryVo.getMetadata().getName(),
Map<String, CategoryTreeVo> nameIdentityMap = categoryVos.stream() Function.identity()));
.map(CategoryTreeVo::from)
.collect(Collectors.toMap(categoryVo -> categoryVo.getMetadata().getName(),
Function.identity()));
nameIdentityMap.forEach((nameKey, value) -> { nameIdentityMap.forEach((nameKey, value) -> {
List<String> children = value.getSpec().getChildren(); List<String> children = value.getSpec().getChildren();
if (children == null) { if (children == null) {
return; return;
} }
for (String child : children) { for (String child : children) {
CategoryTreeVo childNode = nameIdentityMap.get(child); CategoryTreeVo childNode = nameIdentityMap.get(child);
if (childNode != null) { if (childNode != null) {
childNode.setParentName(nameKey); childNode.setParentName(nameKey);
} }
} }
}); });
var tree = listToTree(nameIdentityMap.values(), name); var tree = listToTree(nameIdentityMap.values(), name);
recomputePostCount(tree); recomputePostCount(tree);
return tree; return tree;
});
} }
static List<CategoryTreeVo> listToTree(Collection<CategoryTreeVo> list, String name) { private static List<CategoryTreeVo> listToTree(Collection<CategoryTreeVo> list, String name) {
Map<String, List<CategoryTreeVo>> parentNameIdentityMap = list.stream() Map<String, List<CategoryTreeVo>> parentNameIdentityMap = list.stream()
.filter(categoryTreeVo -> categoryTreeVo.getParentName() != null) .filter(categoryTreeVo -> categoryTreeVo.getParentName() != null)
.collect(Collectors.groupingBy(CategoryTreeVo::getParentName)); .collect(Collectors.groupingBy(CategoryTreeVo::getParentName));
@ -228,8 +240,9 @@ public class CategoryFinderImpl implements CategoryFinder {
@Override @Override
public Flux<CategoryVo> getBreadcrumbs(String name) { public Flux<CategoryVo> getBreadcrumbs(String name) {
return listAsTree() return listAllFor(name)
.collectList() .collectList()
.map(list -> listToTree(list, null))
.flatMapMany(treeNodes -> { .flatMapMany(treeNodes -> {
var rootNode = dummyVirtualRoot(treeNodes); var rootNode = dummyVirtualRoot(treeNodes);
var paths = new ArrayList<CategoryVo>(); var paths = new ArrayList<CategoryVo>();

View File

@ -13,6 +13,8 @@ import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.json.JSONException; import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
@ -190,13 +192,13 @@ class CategoryFinderImplTest {
var json = Files.readString(file.toPath()); var json = Files.readString(file.toPath());
categories = JsonUtils.jsonToObject(json, new TypeReference<>() { categories = JsonUtils.jsonToObject(json, new TypeReference<>() {
}); });
when(client.listAll(eq(Category.class), any(ListOptions.class), any(Sort.class)))
.thenReturn(Flux.fromIterable(categories));
} }
@Test @Test
void computePostCountFromTree() { void computePostCountFromTree() {
var treeVos = categoryFinder.toCategoryTreeVoFlux("全部") when(client.listAll(eq(Category.class), any(ListOptions.class), any(Sort.class)))
.thenReturn(Flux.fromIterable(categories));
var treeVos = categoryFinder.listAsTree("全部")
.collectList().block(); .collectList().block();
assertThat(treeVos).hasSize(1); assertThat(treeVos).hasSize(1);
String s = visualizeTree(treeVos.get(0).getChildren()); String s = visualizeTree(treeVos.get(0).getChildren());
@ -232,6 +234,8 @@ class CategoryFinderImplTest {
@Test @Test
void getBreadcrumbsTest() { void getBreadcrumbsTest() {
when(client.listAll(eq(Category.class), any(ListOptions.class), any(Sort.class)))
.thenReturn(Flux.fromIterable(categories));
// first level // first level
var breadcrumbs = categoryFinder.getBreadcrumbs("全部").collectList().block(); var breadcrumbs = categoryFinder.getBreadcrumbs("全部").collectList().block();
assertThat(toNames(breadcrumbs)).containsSequence("全部"); assertThat(toNames(breadcrumbs)).containsSequence("全部");
@ -259,6 +263,26 @@ class CategoryFinderImplTest {
assertThat(toNames(breadcrumbs)).isEmpty(); assertThat(toNames(breadcrumbs)).isEmpty();
} }
@Test
void getBreadcrumbsForHiddenTest() {
Map<String, Category> categoryMap = categories.stream()
.collect(
Collectors.toMap(item -> item.getMetadata().getName(), Function.identity()));
var category = categoryMap.get("IndependentNode");
category.getSpec().setHideFromList(true);
when(client.listAll(eq(Category.class), any(ListOptions.class), any(Sort.class)))
.thenReturn(Flux.fromIterable(categoryMap.values()));
when(categoryService.isCategoryHidden(eq("IndependentChild4")))
.thenReturn(Mono.just(true));
var breadcrumbs = categoryFinder.getBreadcrumbs("IndependentChild4")
.collectList().block();
assertThat(toNames(breadcrumbs)).containsSequence("全部", "FIT2CLOUD",
"IndependentNode",
"IndependentChild4");
}
static List<String> toNames(List<CategoryVo> categories) { static List<String> toNames(List<CategoryVo> categories) {
if (categories == null) { if (categories == null) {
return List.of(); return List.of();