项目文件夹

文件
2026-07-14 10:24:10 +08:00

111 行
3.5 KiB
Go

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
package response
import (
"net/http"
"sync/atomic"
"github.com/gin-gonic/gin"
)
// Mode 响应模式
type Mode int32
const (
// ModeBusiness 业务码模式(默认):所有响应 HTTP 200,错误信息通过 body 中的 code 表达。
// 兼容存量"业务码 in body"玩法。
ModeBusiness Mode = iota
// ModeREST REST 模式:失败响应按业务码映射对应的 HTTP status401/404/500...),
// body 仍带业务码。便于 APM / Prometheus / 网关 / Sentry 按 status 区分异常。
ModeREST
)
// currentMode 当前响应模式,默认 ModeBusiness。原子读写,可在运行时切换。
var currentMode atomic.Int32
// SetMode 设置全局响应模式。
func SetMode(m Mode) { currentMode.Store(int32(m)) }
// GetMode 返回当前响应模式。
func GetMode() Mode { return Mode(currentMode.Load()) }
// exposeDetail 控制是否把 Error.Detail 写入发给客户端的响应体(P1 #15)。
// 默认 true(保持存量行为:直接使用 response 包的项目不受影响)。
// App 在 Init 时按环境设置:生产环境置为 false,避免调用方误把内部错误(SQL 报错、
// 堆栈上下文等)塞进 Detail 而泄露给客户端;开发环境保持 true 便于排查。
// 1 表示暴露,0 表示隐藏。
var exposeDetail atomic.Int32
func init() {
exposeDetail.Store(1) // 默认暴露,保持存量行为
}
// SetExposeDetail 设置是否向客户端暴露 Error.DetailP1 #15)。
// 生产环境建议置为 false,防止内部错误细节泄露。并发安全。
func SetExposeDetail(expose bool) {
if expose {
exposeDetail.Store(1)
} else {
exposeDetail.Store(0)
}
}
// detailExposed 返回当前是否暴露 Detail。
func detailExposed() bool { return exposeDetail.Load() == 1 }
// statusForCode 按业务码推断 HTTP status(用于 ModeREST)。
// 已知框架错误码显式映射;用户/文件/数据模块的业务错误码(1xxxx~3xxxx
// 属业务语义而非 HTTP 错误,保持 200;操作失败类(4xxxx)映射 400。
func statusForCode(code int) int {
switch code {
case CodeSuccess:
return http.StatusOK
case CodeUnauthorized, CodeTokenExpired, CodeTokenInvalid:
return http.StatusUnauthorized
case CodeForbidden:
return http.StatusForbidden
case CodeNotFound, CodeUserNotFound, CodeFileNotFound, CodeDataNotFound:
return http.StatusNotFound
case CodeDataConflict, CodeDataAlreadyExists:
return http.StatusConflict
case CodeRateLimit:
return http.StatusTooManyRequests
case CodeServerError:
return http.StatusInternalServerError
case CodeServiceUnavailable:
return http.StatusServiceUnavailable
}
if code >= 40000 && code < 50000 {
return http.StatusBadRequest
}
return http.StatusOK
}
// httpStatusFor 返回当前模式下失败响应对应的 HTTP status。
func httpStatusFor(code int) int {
if GetMode() == ModeREST {
return statusForCode(code)
}
return http.StatusOK
}
// writeResp 统一写入响应,按当前 Mode 决定 HTTP status。
func writeResp(c *gin.Context, code int, msg string, data any) {
c.JSON(httpStatusFor(code), Response{
Code: code,
Msg: msg,
Data: data,
RequestID: getRequestID(c),
})
}
// Custom 显式指定 HTTP status 与业务码的响应,不受 Mode 影响。
// 适用于需要精确控制 HTTP status 的场景(如 REST 模式下的特殊端点)。
func Custom(c *gin.Context, httpStatus, code int, msg string, data any) {
c.JSON(httpStatus, Response{
Code: code,
Msg: msg,
Data: data,
RequestID: getRequestID(c),
})
}