package middleware import ( "log/slog" "net/http" "runtime/debug" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // Recovery 捕获 panic 并返回 500(ActionState 信封,W1/W2 裁决)。 // // 响应体:{"success":false,"error":{"code":"GW_INTERNAL_ERROR","message":"..."},"request_id":""}。 // request_id 从 context 取(RequestID 中间件注入),若 Recovery 在 RequestID 之前触发 // 导致 context 无 request_id,则独立生成 uuid 保证可追溯。 func Recovery() gin.HandlerFunc { return func(c *gin.Context) { defer func() { if r := recover(); r != nil { stack := debug.Stack() // 优先从 context 取 request_id(RequestID 中间件已注入) requestID, exists := c.Get(requestIDContextKey) if !exists { requestID = uuid.New().String() } slog.Error("panic recovered", "request_id", requestID, "error", r, "stack", string(stack), ) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "success": false, "error": gin.H{ "code": "GW_INTERNAL_ERROR", "message": "internal server error", }, "request_id": requestID, }) } }() c.Next() } }