You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何访问/categories端点返回全量分类而非分页结果?

分类列表分页异常问题

调用带pageNum参数的/categories/page/{pageNum}端点并传入关键词时,客户端可返回分页分类列表;但调用/categories端点时,返回所有分类结果而非分页数据。

控制器代码

@GetMapping("/categories")
@PreAuthorize("hasAnyAuthority('Admin', 'Editor')")
public String listFirstPage(@Param("sortDir") String sortDir, Model model) {
    if(sortDir == null)
        sortDir = "asc";
    return listByPage(1, "name", null, model);
}

@GetMapping("/categories/page/{pageNum}")
public String listByPage(@PathVariable("pageNum")int pageNum,
        @Param("sortDir") String sortDir,
        @Param("keyword") String keyword,
        Model model) {
    if (sortDir == null || sortDir.isEmpty()) {
        sortDir = "asc";
    }
    
    CategoryPageInfo pageInfo = new CategoryPageInfo();
    List<Category> listByPage = service.listByPage(pageInfo, pageNum, sortDir, model, keyword);
    
     long startCount = (pageNum - 1) * CategoryService.ROOT_CATEGORY_PER_PAGE + 1;
     long endCount = startCount + CategoryService.ROOT_CATEGORY_PER_PAGE -1;
     if(endCount > pageInfo.getTotalElement())
         endCount = pageInfo.getTotalElement();
    
    String reverseSortDir = sortDir.equals("asc") ? "desc" : "asc";
    
    model.addAttribute("totalPages", pageInfo.getTotalPages());
    model.addAttribute("totalElements", pageInfo.getTotalElement());
    model.addAttribute("currentPage", pageNum);
    model.addAttribute("pageNum", pageNum);
    model.addAttribute("sortField", "name");
    model.addAttribute("sortDir", sortDir);
    model.addAttribute("keyword", keyword);
    model.addAttribute("categories", listByPage);
    model.addAttribute("startCount", startCount);
    model.addAttribute("endCount", endCount);
    model.addAttribute("reverseSortDir", reverseSortDir);
    return "categories/categories.html";
}

Service层代码

package com.shopme.admin.category;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import com.shopme.admin.FileUploadUtil;
import com.shopme.common.entity.Category;

import jakarta.transaction.Transactional;

@Service
@Transactional
public class CategoryService {
    public static final int ROOT_CATEGORY_PER_PAGE = 4;

    @Autowired
    private CategoryRepository repo;

    public List<Category> listByPage(CategoryPageInfo pageInfo, int pageNum, String sortDir, Model model,
            String keyword) {
        Sort sort = Sort.by("name");

        if (sortDir.equals("asc")) {
            sort = sort.ascending();
        } else if (sortDir.equals("desc")) {
            sort = sort.descending();
        }

        Pageable pageable = PageRequest.of(pageNum - 1, ROOT_CATEGORY_PER_PAGE, sort);

        Page<Category> categoriesPage = null;

        if (keyword != null && !keyword.isEmpty()) {
            categoriesPage = repo.search(keyword, pageable);
        } else {
            categoriesPage = repo.findRootCategoriesPage(pageable);
        }

        List<Category> rootCategories = categoriesPage.getContent();

        pageInfo.setTotalElement(categoriesPage.getTotalElements());
        pageInfo.setTotalPages(categoriesPage.getTotalPages());

        if (keyword != null && !keyword.isEmpty()) {
            List<Category> searchResult = categoriesPage.getContent();
            for(Category category : searchResult) {
                category.setHasChildren(category.getChildren().size() > 0);
            }
            
            return searchResult;
            
        } else {
            return listHierarchicalCategories(rootCategories, sortDir);
        }
        
        
    }

    private List<Category> listHierarchicalCategories(List<Category> rootCategories, String sortDir) {
        List<Category> listHierarchicalCategories = new ArrayList<>();

        for (Category rootCategory : rootCategories) {
            listHierarchicalCategories.add(Category.copyFull(rootCategory));

            Set<Category> children = sortSubCategory(rootCategory.getChildren(), sortDir);

            for (Category subCategory : children) {
                String name = "--" + subCategory.getName();
                listHierarchicalCategories.add(Category.copyFull(subCategory, name));

                listSubHierarchicalCategories(listHierarchicalCategories, subCategory, 1, sortDir);
            }
        }

        return listHierarchicalCategories;
    }

    private void listSubHierarchicalCategories(List<Category> listHierarchicalCategories, Category parent, int subLevel,
            String sortDir) {
        Set<Category> children = sortSubCategory(parent.getChildren(), sortDir);
        int newSubLevel = subLevel + 1;

        for (Category subCategory : children) {
            StringBuilder name = new StringBuilder();
            for (int i = 0; i < newSubLevel; i++) {
                name.append("--");
            }
            name.append(subCategory.getName());

            listHierarchicalCategories.add(Category.copyFull(subCategory, name.toString()));

            listSubHierarchicalCategories(listHierarchicalCategories, subCategory, newSubLevel, sortDir);
        }

    }
}

问题分析与解决方案

你的判断准确,问题出在非关键词搜索场景下,分页仅限制了根分类的数量,但递归添加的所有子分类未受分页控制,导致最终返回的列表包含当前页根分类下的所有层级子分类,总数量远超设定的分页条数。

核心逻辑说明

当前代码的分页逻辑是针对根分类设计的:

  • repo.findRootCategoriesPage(pageable)会按分页参数查询指定数量的根分类
  • 后续的层级递归是为了展示分类的树形结构,子分类属于根分类的附属内容,不需要分页限制

调整方案

  1. 确认分页统计逻辑:pageInfo中的totalPages和totalElements是基于根分类总数计算的,前端页面的分页按钮、条数统计需基于这些值展示,而非返回的所有分类(根+子)的数量。
  2. 保持层级展示逻辑:不需要修改递归添加子分类的代码,因为分类管理页面通常是按根分类分页,子分类随根分类展开展示,这是合理的交互逻辑。
  3. 如果需求是全部分类分页:若要将所有分类(含子分类)扁平化后分页,需重构Service逻辑——先将所有分类扁平化,再对扁平化后的列表进行分页,但这种方式会丢失分类层级结构,一般不推荐用于分类管理场景。

内容的提问来源于stack exchange,提问作者Antonio Della Luce

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:35:35