前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Go项目实战-关于列表分页的封装和简化

Go项目实战-关于列表分页的封装和简化

作者头像
KevinYan
发布2025-03-07 18:13:13
发布2025-03-07 18:13:13
7100
代码可运行
举报
文章被收录于专栏:网管叨bi叨网管叨bi叨
运行总次数:0
代码可运行

上节课我们实现了商品模块中商品分类相关的功能,这节我们继续商品模块的开发来实现商品详细相关的功能,这些功能在我们梳理出来的功能用例中,我用标记了出来。

从功能用例中我们能看到与商品相关的主要功能有:

  • 商品列表
  • 商品搜索
  • 商品详情

我们会实现商品模块的主要功能接口,在其中会实际应用一下我们在搭建项目定制化的响应组件中的Pagination,来简化分页查询相关的操作,在代码实现上也比普通的方式更优雅一些。

商品列表

接下来我们来实现商品列表功能的接口, 当然真正商用级别的购物App,商品列表应该是通过 Lucene或者是ElasticSearch来实现的查找的。我们这里没有这个硬件条件,就先给大家讲一下通过数据库查询实现功能的逻辑吧。

在购物网站上,我们点击每个分类的时候,会展示分类下的商品列表。

这个时候有个内部逻辑,商品都是挂在到三级分类上的,也就是分类的叶子节点上。 那么此时我们开发功能时要能够兼顾下面几点:

  • 产品的交互逻辑上可能允许用户点击一级或者二级分类查看商品列表。
  • 因不同公司产品逻辑而定,没有绝对,但是我们的功能实现时要支持上面的情况,假设用户选择了一级或者二级分类,我们的程序需要先查出下面的三级分类,再通过这些三级分类查找对应的商品。

以上是业务方面的逻辑,在做本功能的时候我还会演示怎么通过我们之前定义分页组件Pagination,以一个相对优雅的写法写数据库的分页查询。

在 api/controller/commodity.go 中添加商品列表的Controller方法

代码语言:javascript
代码运行次数:0
复制
// CommoditiesInCategory 分类商品列表
func CommoditiesInCategory(c *gin.Context) {
 categoryId, _ := strconv.ParseInt(c.Query("category_id"), 10, 64)
 pagination := app.NewPagination(c)

 svc := appservice.NewCommodityAppSvc(c)
 commodityList, err := svc.GetCategoryCommodityList(categoryId, pagination)
if err != nil {
if errors.Is(err, errcode.ErrParams) {
   app.NewResponse(c).Error(errcode.ErrParams)
  } else {
   app.NewResponse(c).Error(errcode.ErrServer.WithCause(err))
  }
return
 }

 app.NewResponse(c).SetPagination(pagination).Success(commodityList)
}

我们在Controller方法中除了从URL查询字符串上获取商品的分类ID外,还要获取分页相关的请求参数,用它们创建Pagination对象。Pagination 对象会随着我们的调用一直往下传递,传到DomainService中,在需要的时候通过其上的方法来获取offset 和 limit 等信息。

DomainService 中查询商品列表的逻辑如下。

代码语言:javascript
代码运行次数:0
复制
// logic/domainservice/commodity.go
// GetCommodityListInCategory 获取分类下的商品列表
func (cds *CommodityDomainSvc) GetCommodityListInCategory(categoryInfo *do.CommodityCategory, pagination *app.Pagination) ([]*do.Commodity, error) {
 offset := pagination.Offset()
 size := pagination.GetPageSize()

 thirdLevelCategoryIds, err := cds.commodityDao.GetThirdLevelCategories(categoryInfo)
if err != nil {
returnnil, errcode.Wrap("GetCommodityListInCategoryError", err)
 }
 commodityModelList, totalRows, err := cds.commodityDao.GetCommoditiesInCategory(thirdLevelCategoryIds, offset, size)
if err != nil {
returnnil, errcode.Wrap("GetCommodityListInCategoryError", err)
 }
 pagination.SetTotalRows(int(totalRows))

 commodityList := make([]*do.Commodity, 0, len(commodityModelList))
 err = util.CopyProperties(&commodityList, &commodityModelList)
if err != nil {
returnnil, errcode.ErrCoverData.WithCause(err)
 }

return commodityList, nil
}

commodityDao 的 GetThirdLevelCategories 方法就是我们上面说的产品逻辑,拿到一个分类信息后先去获取一下所有的三级分类。

代码语言:javascript
代码运行次数:0
复制
// GetThirdLevelCategories 查找分类下的所有三级分类ID
func (cd *CommodityDao) GetThirdLevelCategories(categoryInfo *do.CommodityCategory) (categoryIds []int64, err error) {
if categoryInfo.Level == 3 {
return []int64{categoryInfo.ID}, nil
 } elseif categoryInfo.Level == 2 {
  categoryIds, err = cd.getSubCategoryIdList([]int64{categoryInfo.ID})
return
 } elseif categoryInfo.Level == 1 {
var secondCategoryId []int64
  secondCategoryId, err = cd.getSubCategoryIdList([]int64{categoryInfo.ID})
if err != nil {
   return
  }
  categoryIds, err = cd.getSubCategoryIdList(secondCategoryId)
return
 }
return
}

如果分类本身就是三级分类则直接返回,否则还是按照上面说的逻辑把分类下的所有三级分类先找出来。

分页查询简化

查询商品信息时因为需要分页,所以我们在CommodityDomainSvc 里先用Pagination获取分页数据需要的offset 和 limit 参数。

代码语言:javascript
代码运行次数:0
复制
func (cds *CommodityDomainSvc) GetCommodityListInCategory(categoryInfo *do.CommodityCategory, pagination *app.Pagination) ([]*do.Commodity, error) {
 offset := pagination.Offset()
 size := pagination.GetPageSize()
    .....
    commodityModelList, totalRows, err := cds.commodityDao.GetCommoditiesInCategory(thirdLevelCategoryIds, offset, size)
     if err != nil {
      return nil, errcode.Wrap("GetCommodityListInCategoryError", err)
     }
    pagination.SetTotalRows(int(totalRows))

    ......
}

商品数据查询的Dao方法除了返回商品列表数据,还会返回满足条件的总行数,这样我们把总行数再设置到Pagination对象上,因为Pagination是指针类型,它创建子Controller方法,所以我们在Controller中返回响应时直接使用 app.NewResponse(c).SetPagination(pagination) 就能把分页查询需要的信息都写入到响应中。

通过这种方式我们能避免需要分页查询数据的接口对应的AppService、DomainService、Dao方法都带上page、pageSize等参数,只需要在调用Dao方法查询数据前从Pagination对象对应的方法中取出这两个值即可。同理所有方法的返回值中也不用都带着totalRow 这个返回值。

比如加入项目访问https://github.com/go-study-lab/go-mall/compare/c14.1...c14.2能查看本节的详细代码更新。

image.png
image.png
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网管叨bi叨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 商品列表
    • 分页查询简化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档