// Package resp 统一 API 响应助手(渐进式契约统一): // - 成功:直接返回业务数据;分页统一 {items, total, page, page_size} 信封 // - 失败:HTTP 状态码语义保持不变,错误体 {detail, code?}(兼容既有前端与测试) // - 内部错误:服务端记录完整 error,客户端只收通用文案,杜绝 DB 错误原文泄漏 // // 新代码禁止直接 c.JSON 错误响应;存量调用点在迭代中逐步迁移到本包。 package resp import ( "log" "net/http" "github.com/gin-gonic/gin" ) // OK 200:直接返回业务数据 func OK(c *gin.Context, data any) { c.JSON(http.StatusOK, data) } // Created 201:资源创建成功 func Created(c *gin.Context, data any) { c.JSON(http.StatusCreated, data) } // Page 200:统一分页信封 {items, total, page, page_size} func Page(c *gin.Context, items any, total int64, page, pageSize int) { c.JSON(http.StatusOK, gin.H{ "items": items, "total": total, "page": page, "page_size": pageSize, }) } // Message 200:仅含 {message} 的轻量确认(删除/测试类端点) func Message(c *gin.Context, msg string) { c.JSON(http.StatusOK, gin.H{"message": msg}) } // Fail 通用失败:{detail} func Fail(c *gin.Context, status int, detail string) { c.JSON(status, gin.H{"detail": detail}) } // FailCode 失败并携带机器可读错误码:{detail, code} func FailCode(c *gin.Context, status int, code, detail string) { c.JSON(status, gin.H{"detail": detail, "code": code}) } func BadRequest(c *gin.Context, detail string) { Fail(c, http.StatusBadRequest, detail) } func Unauthorized(c *gin.Context, detail string) { Fail(c, http.StatusUnauthorized, detail) } func Forbidden(c *gin.Context, detail string) { Fail(c, http.StatusForbidden, detail) } func NotFound(c *gin.Context, detail string) { Fail(c, http.StatusNotFound, detail) } // InternalErr 500:服务端 log 完整错误,客户端只收通用文案(防 DB/内部错误原文泄漏) func InternalErr(c *gin.Context, err error) { log.Printf("[resp] internal error on %s %s: %v", c.Request.Method, c.Request.URL.Path, err) c.JSON(http.StatusInternalServerError, gin.H{"detail": "服务器内部错误,请稍后重试", "code": "internal"}) } // InternalMsg 500:无底层 error、需要给用户可读文案时使用 func InternalMsg(c *gin.Context, userMsg string) { c.JSON(http.StatusInternalServerError, gin.H{"detail": userMsg}) } // TooManyRequests 429:携带 retry_after_minutes func TooManyRequests(c *gin.Context, detail string, retryAfterMinutes int) { c.JSON(http.StatusTooManyRequests, gin.H{"detail": detail, "retry_after_minutes": retryAfterMinutes}) }