51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

设计一个课程预约的API接口,要求支持分页、状态过滤(待预约、已预约),请用Golang(如Gin框架)实现路由和业务逻辑,并说明如何处理请求参数验证和错误响应。

好未来Golang难度:中等

答案

1) 【一句话结论】
核心是通过Gin框架设计课程预约API,实现分页(支持offset和cursor方案)、状态过滤(待/已预约),结合结构体参数验证(含空值/非枚举值处理),并确保错误响应安全(敏感信息过滤),同时考虑缓存(TTL、分布式一致性)和事务(分页查询一致性)。

2) 【原理/概念讲解】

  • 分页方案:
    • offset-limit(偏移量+条数):通过(页码-1)×每页大小定位数据片段,适合小数据量、分页次数少场景(如偏移量100时查询慢);
    • cursor(游标):用上一页最后一条记录ID作为游标,更新逻辑为WHERE id > last_id,适合大数据量、频繁分页(如电商商品列表)。
  • 参数验证:需覆盖空值(如状态为空返回400)、非枚举值(如状态传"invalid"),通过结构体标签(如binding:"required,oneof=waiting booked")实现强校验。
  • 错误响应安全:避免暴露数据库错误详情(如err.Error()),仅返回通用错误(如"服务器内部错误"),遵循HTTP状态码规范(400/500)。
  • 缓存机制:分页结果缓存(如Redis,TTL设5分钟),状态变更时通过分布式锁(Redis锁)清理缓存,保证一致性。

3) 【对比与适用场景】

对比项定义/特性使用场景注意点
分页方法offset-limit小数据量、简单场景偏移量大时查询慢,适合分页次数少
cursor大数据量、频繁分页需维护游标状态,性能更好
参数验证库govalidator(轻量)简单场景,快速验证嵌套结构体验证复杂
自定义结构体验证(标签规则)复杂业务逻辑,强绑定代码验证规则与代码强关联,更灵活
错误响应安全敏感信息过滤(如数据库错误)所有场景遵循安全规范,避免信息泄露
缓存策略TTL+状态变更清理高频分页场景需分布式一致性(如Redis布隆过滤器)

4) 【示例】

  • 路由定义:
    r.GET("/api/v1/courses/bookings", bookingHandler)
    
  • 参数结构体(带验证):
    type BookingQuery struct {
        Page     int    `form:"page" binding:"min=1"` // 页码,默认1
        PageSize int    `form:"pageSize" binding:"min=1,max=100"` // 每页大小,默认10
        Status   string `form:"status" binding:"required,oneof=waiting booked"` // 状态,默认all(或根据业务调整)
    }
    
  • 业务逻辑(含事务与缓存):
    func bookingHandler(c *gin.Context) {
        var query BookingQuery
        if err := c.ShouldBindQuery(&query); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": "invalid query parameters", "details": "请检查参数格式"})
            return
        }
    
        // 事务处理:确保总记录数与分页数据同步更新
        tx, err := db.Begin()
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
            return
        }
        defer tx.Rollback()
    
        // 查询总记录数(事务内)
        total, err := service.GetTotalBookingsByStatus(tx, query.Status)
        if err != nil {
            tx.Rollback()
            c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
            return
        }
    
        // 缓存key(带分页参数)
        cacheKey := fmt.Sprintf("bookings:%s:%d:%d", query.Status, (query.Page-1)*query.PageSize, query.PageSize)
        // 尝试从缓存获取
        cachedData, err := redisClient.Get(cacheKey).Result()
        if err == nil {
            c.JSON(http.StatusOK, gin.H{"data": json.Unmarshal(cachedData, &[]Booking{}), "total": total})
            tx.Commit()
            return
        }
    
        // 缓存未命中,查询分页数据
        bookings, err := service.GetBookingsByStatusAndPagination(
            tx, query.Status, (query.Page-1)*query.PageSize, query.PageSize,
        )
        if err != nil {
            tx.Rollback()
            c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
            return
        }
    
        // 缓存结果(TTL=300秒)
        redisClient.Set(cacheKey, json.Marshal(bookings), time.Minute*5)
    
        tx.Commit()
        c.JSON(http.StatusOK, gin.H{
            "data":    bookings,
            "page":    query.Page,
            "pageSize": query.PageSize,
            "total":   total,
        })
    }
    
  • 请求示例:
    GET /api/v1/courses/bookings?page=1&pageSize=10&status=waiting

5) 【面试口播版答案】
面试官,您好。我来设计一个课程预约的API接口,支持分页和状态过滤。首先,路由用Gin的GET方法定义,路径是/api/v1/courses/bookings。然后处理请求参数,比如页码(page)、每页大小(pageSize)、状态(status)。参数验证方面,用结构体绑定验证,比如页码和每页大小必须是正整数(默认页码1,每页10条),状态必须是waiting或booked,否则返回400错误。业务逻辑里,通过事务确保分页查询(总记录数与数据列表)的一致性,先查询总记录数,再根据分页参数查询数据。错误处理方面,如果参数验证失败,返回400;如果数据库查询出错,返回500,且不暴露敏感信息(如仅返回“服务器内部错误”)。分页支持两种方案:offset(偏移量+条数,适合小数据)和cursor(游标,用最后一条ID,适合大数据频繁分页)。缓存分页结果(如Redis,TTL5分钟),状态变更时通过分布式锁清理缓存,保证一致性。整体逻辑是路由接收参数,验证后调用服务层,最后返回分页结果和错误信息。

6) 【追问清单】

  • 问:如何处理分页参数的默认值?
    回答:默认值在结构体中通过标签设置,比如Page默认1,PageSize默认10,用户不传参数时自动使用默认值。
  • 问:状态过滤的枚举值是否需要扩展?
    回答:可以通过修改结构体中的枚举值,或用动态枚举,但通常用固定枚举,未来扩展时更新代码。
  • 问:参数验证是否覆盖空值场景?
    回答:结构体绑定会校验空值(如状态为空),此时返回400错误,错误信息明确。
  • 问:错误响应是否过滤了敏感信息?
    回答:是的,数据库错误时仅返回“服务器内部错误”,不暴露具体错误详情。
  • 问:缓存分页结果时如何保证一致性?
    回答:通过Redis分布式锁(如SETNX)保证状态变更时缓存清理的原子性,避免数据不一致。

7) 【常见坑/雷区】

  • 坑1:分页偏移量过大导致性能问题(如页码100时查询慢),应限制偏移量或用cursor分页。
  • 坑2:状态过滤参数值未校验(如传入无效状态),导致查询错误,应校验枚举值。
  • 坑3:参数验证未处理空值(如状态为空),导致业务逻辑异常,应覆盖空值场景。
  • 坑4:错误响应暴露敏感信息(如数据库错误),违反安全规范。
  • 坑5:分页逻辑未返回总记录数,导致前端计算总页数错误,应包含总记录数。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1