跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java算法

黑马点评商铺分页查询异常排查与修复

针对黑马点评项目中商铺列表滚动分页失效及首页重复查询问题,定位到后端 Redis GEO 分页逻辑与默认页大小配置不匹配。通过调整 SystemConstants.DEFAULT_PAGE_SIZE 至合理数值并优化 Stream 流式分页处理,解决了数据偏移异常,实现了按距离排序的准确分页展示。

邪神洛基发布于 2026/4/10更新于 2026/9/1070 浏览
黑马点评商铺分页查询异常排查与修复

黑马点评商铺分页查询异常排查与修复

在开发过程中遇到了一个棘手的前端交互问题。页面中选择美食类别后往下翻,无法自动滚动分页查询所有的美食店铺数据,前端滚动没反应。

文章配图

还有个比较诡异的点:前端点击'距离'排序后,滚动查询第一页时数据被查询了两次,后续翻页则正常。如下图所示。

排查许久后,定位到根源在于后端分页配置的大小设定。我的后端代码如下:

Controller 层

/**
 * 根据商铺类型分页查询商铺信息
 * @param typeId 商铺类型
 * @param current 页码
 * @param x 经度
 * @param y 纬度
 * @return 商铺列表
 */
@GetMapping("/of/type")
public Result queryShopByType(
        @RequestParam("typeId") Integer typeId,
        @RequestParam(value = "current", defaultValue = "1") Integer current,
        @RequestParam("x") Double x,
        @RequestParam("y") Double y
) {
    // 调用 Service 层方法(修正方法名驼峰规范)
    return shopService.queryShopByType(typeId, current, x, y);
}

ServiceImpl 层

@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
    if (x == null || y == null) {
        // 直接返回对应类型的店铺就行了
        Page<Shop> page = query()
                .eq("type_id", typeId)
                .page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));
        return Result.ok(page.getRecords());
    }

    int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
    int end = current * SystemConstants.DEFAULT_PAGE_SIZE;
    String key = "geo:shop:type:" + typeId;

    // 构造 5000 米的距离对象(核心:指定米为单位,5000m)
    Distance distance = new Distance(5000, Metrics.METERS);

    // 完整的 GEO 圆形区域查询(补全所有参数)
    GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = stringRedisTemplate.opsForGeo()
            .search(key,
                    GeoReference.fromCoordinate(new Point(x, y)),
                    distance,
                    RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                            .sortAscending() // 按距离从近到远排序(附近商户必备)
                            .limit(end) // 预查足够数据,为后续分页准备
                            .includeDistance() // 核心:让 Redis 返回每个元素到圆心的距离
                            .includeCoordinates() // 预查数据,避免分页漏条
            );

    // 判空:geoResults 为空或无内容,直接返回空列表
    if (geoResults == null || geoResults.getContent().isEmpty()) {
        return Result.ok(Collections.emptyList());
    }

    // 核心:Stream 分页(skip+limit)并接收结果,提取【商铺 ID+距离】
    List<GeoResult<RedisGeoCommands.GeoLocation<String>>> pageGeoList = geoResults.getContent().stream()
            .skip(from) // 跳过前 from 条,实现分页
            .limit(SystemConstants.DEFAULT_PAGE_SIZE) // 截取分页条数(一页的数量)
            .collect(Collectors.toList()); // 必须 collect 接收结果,否则跳过不生效

    // 提取商铺 ID(批量查库,替代循环单查,提升性能 10 倍+)
    List<Long> shopIds = pageGeoList.stream()
            .map(geoResult -> Long.valueOf(geoResult.getContent().getName()))
            .collect(Collectors.toList());

    if (shopIds.isEmpty()) {
        return Result.ok(Collections.emptyList());
    }

    String shopIdstr = StringUtil.join(shopIds, ",");

    // 批量查询商铺详情(MyBatis-Plus 批量查,避免循环 eq 单查)
    List<Shop> shopList = query().in("id", shopIds).last(("ORDER BY FIELD(id, " + shopIdstr + "))).list();

    // 给商铺赋值距离(并保证 shopList 顺序和 pageGeoList 一致)
    for (int i = 0; i < pageGeoList.size(); i++) {
        GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = pageGeoList.get(i);
        Shop shop = shopList.get(i);
        // 距离转 Double,加非空判断(防止空指针)
        Double distValue = geoResult.getDistance().getValue();
        shop.setDistance(distValue);
    }

    return Result.ok(shopList);
}

将 DEFAULT_PAGE_SIZE 的大小改为 5 以上即可解决该问题。这主要是因为 Redis GEO 搜索返回的数据量如果小于预期,会导致分页偏移计算出现偏差,适当增大初始查询范围能覆盖更多潜在数据,从而保证分页逻辑稳定。

目录

  1. 黑马点评商铺分页查询异常排查与修复
  2. Controller 层
  3. ServiceImpl 层

更多推荐文章

查看全部
  • OpenClaw 部署全流程:本地安装、云端托管与进阶配置
  • Python 爬虫技术原理与实战应用指南
  • Java 常用类速查:包装类、String、StringBuilder、Math、日期类
  • 前缀和算法进阶:中心下标与子数组求和
  • VS Code Copilot 完整使用教程
  • Llama Factory 微调算法深度解析与显存优化
  • 阿里发布全新 AI IDE Qoder:透明化开发与智能代理模式
  • OpenAI 指控 DeepSeek 非法蒸馏,字节发布 Seedance 2.0,Java 26 预览版发布
  • Next AI Draw.io 简介、安装与使用指南
  • DeepSeek 各版本说明与优缺点分析
  • Playwright 与 MCP AI 实现自动化浏览器操作
  • 使用 Trae 集成 Claude Code 进行 AI 编程开发
  • LeetCode 二叉树经典算法题解汇总
  • Python 调用高德地图 MCP 服务查询天气示例
  • Qt Creator 集成 GitHub Copilot 插件配置指南
  • Claude Code 本地部署与项目实战详解
  • 基于 QNN 框架在 Android 端部署 LLaMA-7B 大模型实战
  • 基于 Flask 和 Python 的智慧养老服务系统设计与实现
  • VSCode 使用 Git 快速提交代码指南
  • YOLOv8.3 无人机航拍小目标检测实战与优化策略

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online