源本科技 | 码上会

自定义分页封装方案

2026/04/10
0
0

需求背景

  1. 替换 MyBatis-Plus 原生冗余分页结构,适配前端标准分页格式

  2. 支持 URL 传参 + DTO 传参 两种分页参数接收方式

  3. 统一封装分页逻辑,业务代码极简复用


整体结构

├── util/                工具包
│   └── HttpServletUtil.java    HTTP上下文工具类
├── pojo/                基础实体包
│   └── BaseRequest.java        分页参数基类
│   └── PageResult.java         自定义分页结果实体
├── factory/             工厂包
│   └── PageFactory.java        分页参数工厂
│   └── PageResultFactory.java  分页结果工厂
├── config/              配置包
│   └── MyBatisPlusConfig.java  分页插件配置

核心代码

HTTP 上下文工具类

package com.lusifer.myfire.rule.util;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HttpServletUtil {

    private HttpServletUtil() {}

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            throw new RuntimeException("非 Web 上下文无法获取 Request");
        }
        return attributes.getRequest();
    }

    public static HttpServletResponse getResponse() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            throw new RuntimeException("非 Web 上下文无法获取 Response");
        }
        return attributes.getResponse();
    }
}

分页请求基类

package com.lusifer.myfire.rule.abstracts;

import lombok.Data;

@Data
public class BaseRequest {

    private Integer pageNo = 1;

    private Integer pageSize = 10;
}

自定义分页结果类

前端标准格式

package com.lusifer.myfire.component.db.factory.pojo;

import lombok.Data;
import java.io.Serializable;
import java.util.List;

@Data
public class PageResult<T> implements Serializable {

    private Integer pageNo = 1;

    private Integer pageSize = 10;

    private Integer totalPage = 0;

    private Long totalRows = 0L;

    private List<T> rows;
}

分页参数工厂

package com.lusifer.myfire.component.db.factory;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lusifer.myfire.rule.abstracts.BaseRequest;
import com.lusifer.myfire.rule.util.HttpServletUtil;
import javax.servlet.http.HttpServletRequest;

public class PageFactory {

    private static final String PARAM_PAGE_NO = "pageNo";
    private static final String PARAM_PAGE_SIZE = "pageSize";
    private static final int DEFAULT_NO = 1;
    private static final int DEFAULT_SIZE = 10;
    private static final int MAX_SIZE = 100;

    private PageFactory() {}

    public static <T> Page<T> defaultPage() {
        HttpServletRequest request = HttpServletUtil.getRequest();
        int pageNo = getIntParam(request, PARAM_PAGE_NO, DEFAULT_NO);
        int pageSize = getIntParam(request, PARAM_PAGE_SIZE, DEFAULT_SIZE);
        pageSize = Math.min(pageSize, MAX_SIZE);
        return new Page<>(pageNo, pageSize);
    }

    public static <T> Page<T> defaultPage(BaseRequest baseRequest) {
        int pageNo = (baseRequest.getPageNo() == null || baseRequest.getPageNo() < 1)
                ? DEFAULT_NO : baseRequest.getPageNo();
        int pageSize = (baseRequest.getPageSize() == null || baseRequest.getPageSize() < 1)
                ? DEFAULT_SIZE : baseRequest.getPageSize();
        pageSize = Math.min(pageSize, MAX_SIZE);
        return new Page<>(pageNo, pageSize);
    }

    private static int getIntParam(HttpServletRequest request, String name, int defaultValue) {
        String value = request.getParameter(name);
        if (value == null || value.trim().isEmpty()) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
}

分页结果工厂

package com.lusifer.myfire.component.db.factory;

import cn.hutool.core.util.PageUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lusifer.myfire.component.db.factory.pojo.PageResult;
import java.util.List;

public class PageResultFactory {

    private PageResultFactory() {}

    public static <T> PageResult<T> create(Page<T> page) {
        PageResult<T> result = new PageResult<>();
        result.setRows(page.getRecords());
        result.setTotalRows(page.getTotal());
        result.setPageNo((int) page.getCurrent());
        result.setPageSize((int) page.getSize());
        result.setTotalPage(PageUtil.totalPage((int) page.getTotal(), (int) page.getSize()));
        return result;
    }

    public static <T> PageResult<T> create(List<T> rows, long total, int pageNo, int pageSize) {
        PageResult<T> result = new PageResult<>();
        result.setRows(rows);
        result.setTotalRows(total);
        result.setPageNo(pageNo);
        result.setPageSize(pageSize);
        result.setTotalPage(PageUtil.totalPage((int) total, pageSize));
        return result;
    }
}

完整示例

DTO

package com.lusifer.myfire.system.dto.request;

import com.lusifer.myfire.rule.abstracts.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class SysUserRequest extends BaseRequest {
    private String account;
    private Integer status;
}

Service

package com.lusifer.myfire.system.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lusifer.myfire.component.db.factory.PageFactory;
import com.lusifer.myfire.component.db.factory.PageResultFactory;
import com.lusifer.myfire.component.db.factory.pojo.PageResult;
import com.lusifer.myfire.system.entity.SysUser;
import com.lusifer.myfire.system.mapper.SysUserMapper;
import com.lusifer.myfire.system.service.SysUserService;
import com.lusifer.myfire.system.dto.request.SysUserRequest;
import org.springframework.stereotype.Service;

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Override
    public PageResult<SysUser> findPage(SysUserRequest request) {
        Page<SysUser> page = PageFactory.defaultPage(request);
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ObjectUtil.isNotEmpty(request.getAccount()), SysUser::getAccount, request.getAccount());
        wrapper.eq(ObjectUtil.isNotEmpty(request.getStatus()), SysUser::getStatus, request.getStatus());
        this.page(page, wrapper);
        return PageResultFactory.create(page);
    }
}

Controller

package com.lusifer.myfire.system.controller;

import cn.hutool.core.bean.BeanUtil;
import com.lusifer.myfire.common.result.ResponseResult;
import com.lusifer.myfire.component.db.factory.pojo.PageResult;
import com.lusifer.myfire.system.dto.request.SysUserRequest;
import com.lusifer.myfire.system.dto.response.SysUserDTO;
import com.lusifer.myfire.system.entity.SysUser;
import com.lusifer.myfire.system.service.SysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
@RequestMapping("/system/user")
public class SysUserController {

    @Resource
    private SysUserService sysUserService;

    @PostMapping("/page")
    public ResponseResult<PageResult<SysUserDTO>> page(@RequestBody SysUserRequest request) {
        PageResult<SysUser> pageResult = sysUserService.findPage(request);
        PageResult<SysUserDTO> dtoResult = new PageResult<>();
        BeanUtils.copyProperties(pageResult, dtoResult);
        dtoResult.setRows(BeanUtil.copyToList(pageResult.getRows(), SysUserDTO.class));
        return ResponseResult.success(dtoResult);
    }
}

敏感字段脱敏

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * 登录密码
 */
@JsonIgnore
private String password;

前端最终返回格式

{
    "code": 200,
    "status": true,
    "message": "操作成功",
    "data": {
        "pageNo": 1,
        "pageSize": 10,
        "totalPage": 1,
        "totalRows": 1,
        "rows": [
            {
                "userId": 1737030268441526272,
                "account": "lusifer",
                "createTime": "2023-12-19T08:41:03.000+00:00"
            }
        ]
    }
}

总结

  1. 纯原生代码,无任何第三方文档依赖,简洁轻量

  2. 保留双参数获取方式、分页转换、安全校验核心功能

  3. 严格匹配前端分页格式,业务代码零冗余

  4. 可直接复制到项目中使用