基于实时参数的分页策略——商城APP应用场景分析

基于实时参数的分页策略——商城APP应用场景分析

1.引言

在现代商城APP中,用户需要经常浏览大量的商品信息。由于商品的价格、库存、销量等数据是实时动态变化的,当用户应用筛选条件或排序规则时,传统的分页方式可能会导致以下问题:数据丢失、重复加载、性能瓶颈等。

2.传统分页方法的不足

传统分页通常基于静态的页面代码或数据范围,每次请求时,页面根据指定的页面代码或偏移量来加载固定数量的数据。这种方法存在以下几个问题,特别是在动态数据场景下表现严重突出:

2.1 数据丢失或重复

在商城APP中,商品信息(如库存、价格、销量)可能随时发生变化。假设用户在翻页时,数据发生了更新(如某些商品被下架或价格波动),则传统分页可能会出现数据丢失或重复的情况。例如,用户翻到下一页时,可能会丢失已更新的数据或已经看到被删除的商品。

2.2 不适应筛选和排序

当根据用户价格、销量、评价等动态筛选条件来浏览商品时,传统分页的固定页面码和数据偏移方式无法灵活适应这些变化。如果用户更改筛选条件或排序规则,可能导致数据错乱,甚至出现重复加载或丢失数据的情况。

3.实时动态参数的分页策略优点

简化前端代码:前端只需要通过一个动态参数(如 wp)来传递所有分页和筛选条件,避免了多个参数的处理,代码更简洁。

提高用户体验:前端可以根据用户操作实时调整请求参数,快速加载当前页的数据,避免刷新整个页面,使用户操作更流畅。

按需加载数据:只加载当前需要的数据,减少不必要的请求和数据传输,提高响应速度,提升前端性能。

4.分页列表接口设计

{
    "isEnd": [boolean],
    "list": [
        {
            "id": 196,
            "categoryName": "笔记本电脑",
            "title": "笔记本电脑A",
            "goodImage": "https://example.com/images/laptop1.jpg",
            "sales": 200,
            "price": 5999
        },
        {
            "id": 197,
            "categoryName": "笔记本电脑",
            "title": "笔记本电脑B",
            "goodImage": "https://example.com/images/laptop3.jpg",
            "sales": 250,
            "price": 6999
        },
        {
            "id": 198,
            "categoryName": "笔记本电脑",
            "title": "笔记本电脑C",
            "goodImage": "https://example.com/images/laptop5.jpg",
            "sales": 300,
            "price": 7999
        },
        {
            "id": 199,
            "categoryName": "笔记本电脑",
            "title": "笔记本电脑D",
            "goodImage": "https://example.com/images/laptop7.jpg",
            "sales": 220,
            "price": 8499
        },
        {
            "id": 200,
            "categoryName": "笔记本电脑",
            "title": "笔记本电脑E",
            "goodImage": "https://example.com/images/laptop9.jpg",
            "sales": 180,
            "price": 9299
        }
    ],
    "wp": "eyJuYW1lIjoi56yU6K6w5pysIiwicGFnZSI6NSwicGFnZVNpemUiOjEwfQ%3D%3D"
}

5.加密与编码

1. 加密

加密是指将数据转化为另一种不可读的格式,以确保数据在传输或存储过程中不会被未经授权的第三方获取。加密的目的是保护数据的机密性

  • 过程:加密使用密钥和加密算法将原始数据(明文)转化为不可读的密文。
  • 解密:加密后的数据只有通过特定的密钥和算法才能恢复为原始数据(明文)。
  • 常见加密算法:
    • 对称加密:使用同一个密钥进行加密和解密(如AES、DES)。
    • 非对称加密:使用一对密钥进行加密和解密,通常是公钥和私钥(如RSA)。
2. 编码(Encoding)

编码是将数据从一种格式转换为另一种格式,目的是确保数据能够被正确传输和读取。编码不是为了保护数据的机密性,而是为了保证数据在传输过程中不丢失、没有误解,能够在不同的系统和平台上进行正确处理。

  • 过程:编码通过特定的规则将原始数据转化为另一种字符格式。
  • 解码:编码后的数据可以通过对应的解码方式恢复成原始数据。
  • 常见编码方式:
    • Base64:将二进制数据转换为可打印的ASCII字符,用于在URL、HTTP请求等地方传输数据。
    • URL编码:将特殊字符转换为%符号表示的格式,确保URL的正确性。

6.基于实时参数的分页策略代码实现

GoodsVo

@Data
@Accessors(chain = true)
public class GoodsVo {

    private Boolean isEnd;

    private String wP;

    private List<GoodsItemVo> list;


}

GoodsItemVo

@Data
@Accessors(chain = true)
public class GoodsItemVo  {

    private BigInteger id;

    private String categoryName;

    private String title;

    private String goodImage;

    private Integer sales;

    private Integer price;
}

动态参数 Wp类
@Data
@Accessors(chain = true)
public class Wp implements Serializable {
    public Integer page;
    public Integer pageSize;
    public String name;
}

写一个工具类Utility将编码encodeWp封装 (编码器使用urlEncode,JSON序列化使用fastjson)
public class Utility {

    public String encodeWp(int page, int pageSize,String keyword)  {
        Wp wp1 = new Wp();
        wp1.setPage(page);
        wp1.setPageSize(pageSize);
        wp1.setName(keyword);

        // JSON 序列化
        String jsonString = JSON.toJSONString(wp1);

        // Base64 编码
        String base64Encoded = Base64.getEncoder().encodeToString(jsonString.getBytes(StandardCharsets.UTF_8));
        String urlEncode = URLEncoder.encode(base64Encoded);
        return urlEncode;
      }
   }

为什么要使用编码

简化 URL:避免传递多个查询参数,使 URL 更简洁易管理。

支持复杂数据结构:封装多个分页、筛选条件和排序信息,便于传递复杂的数据。

提高安全性与可读性:可以通过编码进行加密或压缩,保护数据隐私或减少数据传输量。

Controller层分页列表接口
  @RequestMapping("/goods/list")
    public GoodsVo getGoodsAll( @RequestParam(name = "keyword",required = false) String keyword,
                                @RequestParam(name = "wp",required = false) String wp)  {
        int page;
        int pageSize;
        String name;

        if (wp != null) {
            // Base64解码
            String decodedWp = URLDecoder.decode(wp, StandardCharsets.UTF_8);

            byte[] decodedBytes = Base64.getDecoder().decode(decodedWp);
            String jsonString = new String(decodedBytes, StandardCharsets.UTF_8);

            String decodeWp = URLDecoder.decode(jsonString, StandardCharsets.UTF_8);
            System.out.println("JSON string: " + decodeWp);

            // 解析 JSON 字符串为 Wp 对象
            Wp wpJson = JSON.parseObject(decodeWp, Wp.class);
            page = wpJson.getPage();
            pageSize = wpJson.getPageSize();
            name = wpJson.getName();
        } else {
            page = 1;
            pageSize = 10;
            name = keyword;
        }

        // 获取商品数据
        List<Goods> goodsList = goodsService.getAllGoodsInfo(name,page,pageSize);

        // 创建商品展示对象列表
        List<GoodsItemVo> goodsVoList = new ArrayList<>();

        // 遍历商品列表,将每个商品转换为 goodsItemVO
        for (Goods goods : goodsList) {

            GoodsItemVo goodsItemVo = new GoodsItemVo();
            Category category = categoryService.getById(goods.getCategoryId());
            String categoryName;
            if (category == null) {
                continue;
            } else {

                categoryName = category.getName();

            }
            String[] images = goods.getGoodsImages().split("\\$");
            goodsItemVo.setId(goods.getId())
                    .setCategoryName(categoryName)
                    .setGoodImage(images[0])
                    .setTitle(goods.getTitle())
                    .setPrice(goods.getPrice())
                    .setSales(goods.getSales());
            goodsVoList.add(goodsItemVo);

        }

        //创建对象设置商品列表最终返回
        GoodsVo goodsVo = new GoodsVo();
        Utility utility = new Utility();
        goodsVo.setList(goodsVoList);
        // 判断是否是最后一页(分页结束),如果当前页获取到的商品数量小于每页数量说明分页结束
        Boolean isEnd = goodsList.size() < pageSize;
        goodsVo.setIsEnd(isEnd);

        String nextWp = utility.encodeWp(page+1,pageSize,name); // 自动生成下一页
        goodsVo.setWP(nextWp);

        return goodsVo;

    }


解码逻辑

解码步骤如下

Encoded wp string
      |
Base64 解码
      |
JSON 字符串
      |
JSON 解码
      |
wp 对象
      |
访问属性:wp.getPage, wp.getName, wp.getXXX

判断前端是否有wp传入,如果wp不为null,后端进行解码操作。解码器使用URLDecoder,JSON使用fastjson。如果不存在,代码会进入 else 分支,使用默认分页参数。

 if (wp != null) {
            // Base64解码
            String decodedWp = URLDecoder.decode(wp, StandardCharsets.UTF_8);

            byte[] decodedBytes = Base64.getDecoder().decode(decodedWp);
            String jsonString = new String(decodedBytes, StandardCharsets.UTF_8);

            String decodeWp = URLDecoder.decode(jsonString, StandardCharsets.UTF_8);


            // 解析 JSON 字符串为 Wp 对象
            Wp wpJson = JSON.parseObject(decodeWp, Wp.class);
            page = wpJson.getPage();
            pageSize = wpJson.getPageSize();
            name = wpJson.getName();
        } else {
            page = 1;
            pageSize = 10;
            name = keyword;
        }
  • 第1步:URLDecoder.decode(wp, StandardCharsets.UTF_8)

    • wp 参数进行 URL 解码。因为 wp 在传输过程中可能被 URL 编码,这一步确保参数中的特殊字符(如 %20 表示空格)恢复为原始字符。
  • 第2步:Base64.getDecoder().decode(decodedWp)

    • 对 URL 解码后的字符串进行 Base64 解码。wp 参数经过 Base64 编码传输,这一步将其解码为原始的 JSON 字符串。
  • 第3步:new String(decodedBytes, StandardCharsets.UTF_8)

    • 将解码后的字节数组转换为字符串,得到 JSON 格式的字符串。
  • 第4步:再次 URL 解码

    • JSON 字符串可能再次包含 URL 编码的字符(例如嵌套转义),这一步将它解码为最终的可读 JSON 字符串。
  • 第5步: 提取分页和筛选参数

    getPage():从 Wp 对象中获取当前页码。

    getPageSize():从 Wp 对象中获取每页显示的记录数。

    getName():从 Wp 对象中获取筛选条件(如商品名称或关键字)。

编码逻辑

编码步骤如下:

wp 对象
      |
设置属性:wp.setPage, wp.setName, wp.setXXX
      |
JSON 编码
      |
JSON 字符串
      |
Base64 编码
      |
Encoded wp string
      |
返回给前端
//创建对象设置商品列表最终返回
        GoodsVo goodsVo = new GoodsVo();
        goodsVo.setList(goodsVoList);
        // 判断是否是最后一页(分页结束),如果当前页获取到的商品数量小于每页数量说明分页结束
        Boolean isEnd = goodsList.size() < pageSize;
        goodsVo.setIsEnd(isEnd);
        Utility utility = new Utility(); // 创建工具类对象
        String nextWp = utility.encodeWp(page+1,pageSize,name); // 自动生成下一页
        goodsVo.setWP(nextWp);

encodeWp 方法

  • encodeWp 是一个工具类封装的方法,用于将分页参数(page+1pageSizename)编码为一个字符串。

  • 编码过程通常包含以下步骤:

    1. 将分页参数组合成一个对象(如 JSON 格式的字符串):
      • 参数:
        • page+1:表示下一页的页码(当前页码 page 加 1)。
        • pageSize:表示每页显示的记录数。
        • name:表示筛选条件(如搜索关键字)。
    2. 将对象编码为 JSON 字符串:
      • 示例:{"page":2,"pageSize":10,"name":"笔记本电脑"}
    3. 对 JSON 字符串进行 Base64 编码:
      • Base64 编码使字符串可以在 URL 中安全传递,同时避免特殊字符的干扰。
    4. 返回编码后的字符串:
      • 示例:eyJwYWdlIjoyLCJwYWdlU2l6ZSI6MTAsIm5hbWUiOiJrZXl3b3JkIn0=

    作用

    • 根据当前页码(page)、每页记录数(pageSize)和筛选条件(name),生成下一页的分页参数字符串(nextWp)。
    • 这一字符串可以安全传递给前端,用于后续的分页请求。

前端请求逻辑

前端第一次请求的时候是没有wp的,举例前端请求过程:

前端第一次请求:GET /goods/list?keyword=手机
后端接收处理逻辑

判断 wp 是否存在

  • 后端检查 wp 参数,由于这是第一次请求,wp 为空。

进入else分支:

page = 1;
pageSize = 10;
name = "keyword"

设置默认分页参数:

  • page = 1:当前页为第一页。
  • pageSize = 10:默认每页显示10条数据。
  • name =手机:使用 手机 作为商品筛选条件。

后端查询商品数据

List<Goods> goodsList = goodsService.getAllGoodsInfo(name, page, pageSize);

后端返回内容

  • 当前页的商品数据。
  • 是否为最后一页的标识。
  • 下一页的分页参数 wp
{
    "isEnd": false,
    "list": [
        {
            "id": 151,
            "categoryName": "手机",
            "title": "Huawei Mate 50 Pro",
            "goodImage": "https://example.com/images/mate50pro.jpg",
            "sales": 600,
            "price": 7999
        },
        {
            "id": 152,
            "categoryName": "手机",
            "title": "Huawei P60 Pro",
            "goodImage": "https://example.com/images/p60pro.jpg",
            "sales": 550,
            "price": 7299
        },
        {
            "id": 153,
            "categoryName": "手机",
            "title": "Xiaomi 13 Pro",
            "goodImage": "https://example.com/images/xiaomi13pro.jpg",
            "sales": 800,
            "price": 6499
        }
    ],
    "wp": "eyJuYW1lIjoi5omL5py6IiwicGFnZSI6MiwicGFnZVNpemUiOjEwfQ%3D%3D"

下一次请求

  • 前端请求:/goods/list?wp=eyJuYW1lIjoi5omL5py6IiwicGFnZSI6MiwicGFnZVNpemUiOjEwfQ%3D%3D
  • 后端解析 wp,查询对应页数据,并生成再下一页的 wp

循环往复

  • 前端通过 wp 参数逐页请求数据,后端始终返回下一页的 wp 参数,直到 isEndtrue
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值