You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Gorm+Gin中带多排序分页的GetPosts控制器函数?

优化后的GetPosts控制器实现

针对你代码中嵌套冗余、重复SQL的问题,可以通过参数解析封装、动态SQL构建、逻辑解耦这几个方向优化,既保留显式原生SQL的需求,又让代码更简洁可维护:

关键优化点

  • 封装参数解析逻辑,避免重复的类型转换和错误处理
  • 动态拼接SQL的WHERE条件与ORDER BY子句,消除重复SQL代码
  • 修正总计数查询的逻辑,匹配当前的过滤条件(比如按用户id过滤时只统计该用户的帖子)
  • 集中管理默认配置,便于后续调整
  • 扁平化嵌套结构,提升代码可读性

完整优化代码

import (
	"math"
	"strconv"

	"github.com/gin-gonic/gin"
)

// 定义默认配置,集中管理
const (
	defaultPage  = 1
	defaultLimit = 6
)

func GetAllPost(c *gin.Context) {
	var posts []models.Post
	var noOfPosts int64

	// 1. 解析分页参数
	page, err := parseQueryInt(c, "page", defaultPage)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query param - page"})
		return
	}
	limit, err := parseQueryInt(c, "limit", defaultLimit)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query param - limit"})
		return
	}
	skip := (page - 1) * limit

	// 2. 解析用户ID参数(可选)
	var userID *int
	if idStr := c.Query("id"); idStr != "" {
		id, err := strconv.Atoi(idStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query param - id"})
			return
		}
		userID = &id
	}

	// 3. 解析排序参数,生成ORDER BY子句
	sortClause := getSortClause(c.Query("sort"))

	// 4. 构建基础SQL与参数列表
	sql := "SELECT * FROM posts"
	params := []interface{}{}

	// 添加WHERE条件(如果有用户ID)
	if userID != nil {
		sql += " WHERE created_by = ?"
		params = append(params, *userID)
	}

	// 添加排序与分页
	sql += " ORDER BY " + sortClause + " OFFSET ? ROW FETCH NEXT ? ROWS ONLY"
	params = append(params, skip, limit)

	// 执行查询
	if err := initializers.DB.Raw(sql, params...).Find(&posts).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch posts"})
		return
	}

	// 5. 构建总计数查询(匹配过滤条件)
	countSQL := "SELECT COUNT(id) FROM posts"
	countParams := []interface{}{}
	if userID != nil {
		countSQL += " WHERE created_by = ?"
		countParams = append(countParams, *userID)
	}
	if err := initializers.DB.Raw(countSQL, countParams...).Scan(&noOfPosts).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to count posts"})
		return
	}

	// 计算总页数
	noOfPages := int(math.Ceil(float64(noOfPosts) / float64(limit)))

	c.JSON(http.StatusOK, gin.H{
		"posts":     posts,
		"noOfPages": noOfPages,
	})
}

// parseQueryInt 封装查询参数的整数解析逻辑
func parseQueryInt(c *gin.Context, key string, defaultValue int) (int, error) {
	valStr := c.Query(key)
	if valStr == "" {
		return defaultValue, nil
	}
	val, err := strconv.Atoi(valStr)
	if err != nil || val <= 0 { // 额外校验参数合法性(比如页码不能为负)
		return 0, err
	}
	return val, nil
}

// getSortClause 根据排序参数返回对应的ORDER BY子句
func getSortClause(sort string) string {
	switch sort {
	case "likesCount":
		return "ARRAY_LENGTH(liked_by, 1) DESC NULLS LAST"
	case "oldest":
		return "id ASC"
	default:
		return "id DESC" // 默认最新发布
	}
}

代码说明

  1. 参数解析封装:parseQueryInt函数统一处理整数类型的查询参数,包含默认值返回和合法性校验,避免重复的strconv代码。
  2. 动态SQL构建:通过拼接字符串的方式,根据是否有用户ID、排序参数动态生成SQL,所有参数都通过占位符传递,避免SQL注入风险。
  3. 排序逻辑解耦:getSortClause函数单独处理排序规则,把排序字符串的生成和主逻辑分离,后续新增排序规则只需修改这个函数。
  4. 修正总计数逻辑:总计数查询会同步应用用户ID的过滤条件,保证分页计算的准确性。
  5. 错误处理增强:统一处理Gorm的查询错误,返回更友好的服务器错误信息。

内容的提问来源于stack exchange,提问作者user17402771

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:39:19