用gin.context而非http.request因其封装请求解析、中间件链、参数绑定与响应写入,绕过会导致bindjson失败、jwt验证失效及panic;时段冲突需服务层区间判断;jwt须校验exp及时区;并发防超订需行锁或唯一索引。

为什么用 gin.Context 而不是直接读取 http.Request
因为 gin.Context 封装了请求解析、中间件链、参数绑定和响应写入逻辑,直接操作 http.Request 会绕过 Gin 的生命周期管理,导致 BindJSON 失败、中间件(如 JWT 验证)不生效,甚至 panic:「context canceled」或「http: response already committed」。
实操建议:
- 所有参数提取必须走
c.ShouldBindJSON(&req)或c.Query("room_id"),不要用r.ParseForm() - 自定义中间件中务必调用
c.Next(),否则后续 handler 不执行 - 响应统一用
c.JSON(http.StatusOK, resp),避免手动w.WriteHeader()和json.NewEncoder().Encode()
POST /api/reserve 接口如何校验时段冲突
自习室预约的核心约束是「同一座位在相同时间段内不可重复预约」,不能只靠数据库唯一索引(因时间是范围),必须在服务层做区间重叠判断。
实操建议:
- 查询条件写成:
SELECT id FROM reservations WHERE seat_id = ? AND start_time ?,注意是和 <code>>,不是/<code>>=,否则边界时间(如 14:00 开始 vs 14:00 结束)会被误判为冲突 - 用
time.ParseInLocation("15:04", "14:00", loc)解析时间,别用Parse("h:m")——Go 默认按 UTC 解析,本地时区时间会偏移 - 数据库字段用
TIME类型存起止时间(非DATETIME),避免日期干扰;后端传参也只传时间字符串,不带日期
Gin 中 JWT 验证中间件怎么避免 token 过期后仍放行
常见错误是只校验签名和格式,没检查 exp 字段,或忽略时区导致本地时间比服务器快/慢,造成「明明过期了却还能访问」。
实操建议:
- 用
github.com/golang-jwt/jwt/v5,校验时显式传入jwt.WithValidTime(),并设置WithIssuedAt()和WithExpirationRequired() - 解析后立即调用
token.Claims.(jwt.MapClaims)["exp"]并转为time.Time,与time.Now().UTC()比较,不要用time.Now().Local() - 把用户 ID 存进
c.Set("user_id", uid),后续 handler 用c.MustGet("user_id").(int64)取值,别重复解析 token
并发预约时怎么防止超订(seat_count=1 的座位被多人同时抢到)
单纯查+插在高并发下必然出问题,MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE 或 SELECT ... FOR UPDATE 是必须的,但 Gin 层不能依赖事务自动回滚——HTTP handler 里没显式 tx.Rollback() 就会卡死连接。
实操建议:
- 用
SELECT ... FOR UPDATE加行锁,但必须包裹在事务中,且整个事务要在同一个 DB 连接上完成(Gin 中推荐用db.Begin()+defer tx.Rollback()) - 更稳妥的是用唯一索引:
UNIQUE KEY `uk_seat_time` (`seat_id`, `date`, `start_time`, `end_time`),然后用INSERT IGNORE,失败时返回http.StatusConflict - 别在 handler 里写
time.Sleep()模拟延迟来测试并发——这会阻塞 goroutine,真实压测要用ab或hey工具发起多请求
真正难的不是写接口,是时间精度、时区一致性、数据库锁粒度这三块拼在一起时,任何一个环节松动都会让预约结果不可预期。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











