From e0efca7edfac3409e036047ca1175f14700da7a2 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 9 Jul 2025 21:13:50 +0800 Subject: [PATCH] Add ranking filtering by tags and categories --- open-isle-cli/src/views/HomePageView.vue | 13 ++++- .../openisle/controller/PostController.java | 17 ++++++- .../openisle/repository/PostRepository.java | 7 +++ .../com/openisle/service/PostService.java | 51 +++++++++++++++++-- .../controller/PostControllerTest.java | 31 +++++++++++ 5 files changed, 113 insertions(+), 6 deletions(-) diff --git a/open-isle-cli/src/views/HomePageView.vue b/open-isle-cli/src/views/HomePageView.vue index f7aeed922..b30d8230d 100644 --- a/open-isle-cli/src/views/HomePageView.vue +++ b/open-isle-cli/src/views/HomePageView.vue @@ -142,7 +142,16 @@ export default { } const buildRankUrl = () => { - return `${API_BASE_URL}/api/posts/ranking?page=${page.value}&pageSize=${pageSize}` + let url = `${API_BASE_URL}/api/posts/ranking?page=${page.value}&pageSize=${pageSize}` + if (selectedCategory.value) { + url += `&categoryId=${selectedCategory.value}` + } + if (selectedTags.value.length) { + selectedTags.value.forEach(t => { + url += `&tagIds=${t}` + }) + } + return url } const fetchPosts = async (reset = false) => { @@ -239,6 +248,8 @@ export default { watch([selectedCategory, selectedTags], () => { if (selectedTopic.value === '最新') { fetchPosts(true) + } else if (selectedTopic.value === '排行榜') { + fetchRanking(true) } }) diff --git a/src/main/java/com/openisle/controller/PostController.java b/src/main/java/com/openisle/controller/PostController.java index f0303dcb8..8a420a1d0 100644 --- a/src/main/java/com/openisle/controller/PostController.java +++ b/src/main/java/com/openisle/controller/PostController.java @@ -86,9 +86,22 @@ public class PostController { } @GetMapping("/ranking") - public List rankingPosts(@RequestParam(value = "page", required = false) Integer page, + public List rankingPosts(@RequestParam(value = "categoryId", required = false) Long categoryId, + @RequestParam(value = "categoryIds", required = false) List categoryIds, + @RequestParam(value = "tagId", required = false) Long tagId, + @RequestParam(value = "tagIds", required = false) List tagIds, + @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "pageSize", required = false) Integer pageSize) { - return postService.listPostsByViews(page, pageSize) + List ids = categoryIds; + if (categoryId != null) { + ids = java.util.List.of(categoryId); + } + List tids = tagIds; + if (tagId != null) { + tids = java.util.List.of(tagId); + } + + return postService.listPostsByViews(ids, tids, page, pageSize) .stream().map(this::toDto).collect(Collectors.toList()); } diff --git a/src/main/java/com/openisle/repository/PostRepository.java b/src/main/java/com/openisle/repository/PostRepository.java index d1161ac15..22e541c44 100644 --- a/src/main/java/com/openisle/repository/PostRepository.java +++ b/src/main/java/com/openisle/repository/PostRepository.java @@ -25,6 +25,13 @@ public interface PostRepository extends JpaRepository { List findDistinctByTagsInAndStatus(List tags, PostStatus status, Pageable pageable); List findDistinctByCategoryInAndTagsInAndStatus(List categories, List tags, PostStatus status); List findDistinctByCategoryInAndTagsInAndStatus(List categories, List tags, PostStatus status, Pageable pageable); + + List findByCategoryInAndStatusOrderByViewsDesc(List categories, PostStatus status); + List findByCategoryInAndStatusOrderByViewsDesc(List categories, PostStatus status, Pageable pageable); + List findDistinctByTagsInAndStatusOrderByViewsDesc(List tags, PostStatus status); + List findDistinctByTagsInAndStatusOrderByViewsDesc(List tags, PostStatus status, Pageable pageable); + List findDistinctByCategoryInAndTagsInAndStatusOrderByViewsDesc(List categories, List tags, PostStatus status); + List findDistinctByCategoryInAndTagsInAndStatusOrderByViewsDesc(List categories, List tags, PostStatus status, Pageable pageable); List findByTitleContainingIgnoreCaseOrContentContainingIgnoreCaseAndStatus(String titleKeyword, String contentKeyword, PostStatus status); List findByContentContainingIgnoreCaseAndStatus(String keyword, PostStatus status); List findByTitleContainingIgnoreCaseAndStatus(String keyword, PostStatus status); diff --git a/src/main/java/com/openisle/service/PostService.java b/src/main/java/com/openisle/service/PostService.java index 861b716c2..15d10db25 100644 --- a/src/main/java/com/openisle/service/PostService.java +++ b/src/main/java/com/openisle/service/PostService.java @@ -108,14 +108,59 @@ public class PostService { } public List listPostsByViews(Integer page, Integer pageSize) { + return listPostsByViews(null, null, page, pageSize); + } + + public List listPostsByViews(java.util.List categoryIds, + java.util.List tagIds, + Integer page, + Integer pageSize) { Pageable pageable = null; if (page != null && pageSize != null) { pageable = PageRequest.of(page, pageSize); } - if (pageable != null) { - return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED, pageable); + + boolean hasCategories = categoryIds != null && !categoryIds.isEmpty(); + boolean hasTags = tagIds != null && !tagIds.isEmpty(); + + if (!hasCategories && !hasTags) { + if (pageable != null) { + return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED, pageable); + } + return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED); } - return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED); + + if (hasCategories) { + java.util.List categories = categoryRepository.findAllById(categoryIds); + if (categories.isEmpty()) { + return java.util.List.of(); + } + if (hasTags) { + java.util.List tags = tagRepository.findAllById(tagIds); + if (tags.isEmpty()) { + return java.util.List.of(); + } + if (pageable != null) { + return postRepository.findDistinctByCategoryInAndTagsInAndStatusOrderByViewsDesc( + categories, tags, PostStatus.PUBLISHED, pageable); + } + return postRepository.findDistinctByCategoryInAndTagsInAndStatusOrderByViewsDesc( + categories, tags, PostStatus.PUBLISHED); + } + if (pageable != null) { + return postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED, pageable); + } + return postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED); + } + + java.util.List tags = tagRepository.findAllById(tagIds); + if (tags.isEmpty()) { + return java.util.List.of(); + } + if (pageable != null) { + return postRepository.findDistinctByTagsInAndStatusOrderByViewsDesc(tags, PostStatus.PUBLISHED, pageable); + } + return postRepository.findDistinctByTagsInAndStatusOrderByViewsDesc(tags, PostStatus.PUBLISHED); } public List listPostsByCategories(java.util.List categoryIds, diff --git a/src/test/java/com/openisle/controller/PostControllerTest.java b/src/test/java/com/openisle/controller/PostControllerTest.java index 2ecad2598..604abc5d1 100644 --- a/src/test/java/com/openisle/controller/PostControllerTest.java +++ b/src/test/java/com/openisle/controller/PostControllerTest.java @@ -220,4 +220,35 @@ class PostControllerTest { verify(postService).listPostsByCategoriesAndTags(eq(java.util.List.of(1L)), eq(java.util.List.of(1L, 2L)), eq(0), eq(5)); verify(postService, never()).listPostsByCategories(any(), any(), any()); } + + @Test + void rankingPostsFiltered() throws Exception { + User user = new User(); + user.setUsername("alice"); + Category cat = new Category(); + cat.setName("tech"); + Tag tag = new Tag(); + tag.setId(1L); + tag.setName("java"); + Post post = new Post(); + post.setId(3L); + post.setTitle("rank"); + post.setCreatedAt(LocalDateTime.now()); + post.setAuthor(user); + post.setCategory(cat); + post.setTags(java.util.Set.of(tag)); + + Mockito.when(postService.listPostsByViews(eq(java.util.List.of(1L)), eq(java.util.List.of(1L, 2L)), eq(0), eq(5))) + .thenReturn(List.of(post)); + + mockMvc.perform(get("/api/posts/ranking") + .param("tagIds", "1,2") + .param("page", "0") + .param("pageSize", "5") + .param("categoryId", "1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(3)); + + verify(postService).listPostsByViews(eq(java.util.List.of(1L)), eq(java.util.List.of(1L, 2L)), eq(0), eq(5)); + } }