| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- // Package paginator 处理分页逻辑
- package paginator
- import (
- "fmt"
- "math"
- "strings"
- "github.com/gin-gonic/gin"
- "github.com/spf13/cast"
- "gorm.io/gorm"
- "gorm.io/gorm/clause"
- "github.com/runningwater/gohub/pkg/config"
- "github.com/runningwater/gohub/pkg/logger"
- )
- // Paging 分页数据
- type Paging struct {
- CurrentPage int // 当前页
- PageSize int // 每页条数
- TotalPage int // 总页数
- TotalCount int64 // 总条数
- NextPageURL string // 下一页的链接
- PrevPageURL string // 上一页的链接
- }
- // Paginator 分页操作类
- type Paginator struct {
- BaseURL string // 用以拼接 URL
- PageSize int // 每页条数
- CurrentPage int // 当前页
- Offset int // 数据库读取数据时 Offset 的值
- TotalCount int64 // 总条数
- TotalPage int // 总页数 = TotalCount/PerPage
- Sort string // 排序规则
- Order string // 排序顺序
- query *gorm.DB // db query 句柄
- ctx *gin.Context // gin context,方便调用
- }
- // Paginate 分页
- //
- // c —— gin.context 用来获取分页的 URL 参数
- // db —— GORM 查询句柄,用以查询数据集和获取数据总数
- // baseURL —— 用以分页链接
- // data —— 模型数组,传址获取数据
- // pageSize —— 每页条数,优先从 url 参数里取,否则使用 perPage 的值
- // 用法:
- //
- // query := database.DB.Model(Topic{}).Where("category_id = ?", cid)
- // var topics []Topic
- // paging := paginator.Paginate(
- // c,
- // query,
- // &topics,
- // app.APIURL(database.TableName(&Topic{})),
- // perPage,
- // )
- func Paginate(c *gin.Context, db *gorm.DB, data any, baseURL string, pageSize int) Paging {
- // 初始化 Paginator 实例
- p := &Paginator{
- query: db,
- ctx: c,
- }
- p.initProperties(pageSize, baseURL)
- // 查询数据库
- err := p.query.Preload(clause.Associations). // 读取关联
- Order(p.Sort + " " + p.Order). // 排序
- Limit(p.PageSize).
- Offset(p.Offset).
- Find(data).
- Error
- // 数据库出错
- if err != nil {
- logger.LogIf(err)
- return Paging{}
- }
- return Paging{
- CurrentPage: p.CurrentPage,
- PageSize: p.PageSize,
- TotalPage: p.TotalPage,
- TotalCount: p.TotalCount,
- NextPageURL: p.getNextPageURL(),
- PrevPageURL: p.getPrevPageURL(),
- }
- }
- // 初始化分页必须用到的属性,基于这些属性查询数据库
- func (p *Paginator) initProperties(pageSize int, baseURL string) {
- p.BaseURL = p.formatBaseURL(baseURL)
- p.PageSize = p.getPageSize(pageSize)
- // 排序参数(控制器中以验证过这些参数,可放心使用)
- p.Order = p.ctx.DefaultQuery(config.Get("paging.url_query_order"), "asc")
- p.Sort = p.ctx.DefaultQuery(config.Get("paging.url_query_sort"), "id")
- p.TotalCount = p.getTotalCount()
- p.TotalPage = p.getTotalPage()
- p.CurrentPage = p.getCurrentPage()
- p.Offset = (p.CurrentPage - 1) * p.PageSize
- }
- func (p *Paginator) getPageSize(pageSize int) int {
- // 优先使用请求 per_page 参数
- queryPerpage := p.ctx.Query(config.Get("paging.url_query_per_page"))
- if len(queryPerpage) > 0 {
- pageSize = cast.ToInt(queryPerpage)
- }
- // 没有传参,使用默认
- if pageSize <= 0 {
- pageSize = config.GetInt("paging.per_page")
- }
- return pageSize
- }
- // getCurrentPage 返回当前页码
- func (p *Paginator) getCurrentPage() int {
- // 优先取用户请求的 page
- page := cast.ToInt(p.ctx.Query(config.Get("paging.url_query_page")))
- if page <= 0 {
- // 默认为 1
- page = 1
- }
- // TotalPage 等于 0 ,意味着数据不够分页
- if p.TotalPage == 0 {
- return 0
- }
- // 请求页数大于总页数,返回总页数
- if page > p.TotalPage {
- return p.TotalPage
- }
- return page
- }
- // getTotalCount 返回的是数据库里的条数
- func (p *Paginator) getTotalCount() int64 {
- var count int64
- if err := p.query.Count(&count).Error; err != nil {
- return 0
- }
- return count
- }
- // getTotalPage 计算总页数
- func (p *Paginator) getTotalPage() int {
- if p.TotalCount == 0 {
- return 0
- }
- nums := int64(math.Ceil(float64(p.TotalCount) / float64(p.PageSize)))
- if nums == 0 {
- nums = 1
- }
- return int(nums)
- }
- // 兼容 URL 带与不带 `?` 的情况
- func (p *Paginator) formatBaseURL(baseURL string) string {
- if strings.Contains(baseURL, "?") {
- baseURL = baseURL + "&" + config.Get("paging.url_query_page") + "="
- } else {
- baseURL = baseURL + "?" + config.Get("paging.url_query_page") + "="
- }
- return baseURL
- }
- // 拼接分页链接
- func (p *Paginator) getPageLink(page int) string {
- return fmt.Sprintf("%v%v&%s=%s&%s=%s&%s=%v",
- p.BaseURL,
- page,
- config.Get("paging.url_query_sort"),
- p.Sort,
- config.Get("paging.url_query_order"),
- p.Order,
- config.Get("paging.url_query_per_page"),
- p.PageSize,
- )
- }
- // getNextPageURL 返回下一页的链接
- func (p *Paginator) getNextPageURL() string {
- if p.TotalPage > p.CurrentPage {
- return p.getPageLink(p.CurrentPage + 1)
- }
- return ""
- }
- // getPrevPageURL 返回下一页的链接
- func (p *Paginator) getPrevPageURL() string {
- if p.CurrentPage <= 1 || p.CurrentPage > p.TotalPage {
- return ""
- }
- return p.getPageLink(p.CurrentPage - 1)
- }
|