Files
AI-CS/backend/middleware/middleware.go
T
2026-03-25 18:50:58 +08:00

126 lines
3.0 KiB
Go

package middleware
import (
"crypto/rand"
"encoding/hex"
"log"
"net/http"
"strconv"
"time"
"github.com/2930134478/AI-CS/backend/service"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func newTraceID() string {
var b [8]byte
if _, err := rand.Read(b[:]); err != nil {
return strconv.FormatInt(time.Now().UnixNano(), 10)
}
return hex.EncodeToString(b[:])
}
// TraceID 为每个请求注入 trace_id,便于链路排障。
func TraceID() gin.HandlerFunc {
return func(c *gin.Context) {
traceID := c.GetHeader("X-Trace-Id")
if traceID == "" {
traceID = newTraceID()
}
c.Set("trace_id", traceID)
c.Writer.Header().Set("X-Trace-Id", traceID)
c.Next()
}
}
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
//继续调用后续的中间件处理函数
c.Next()
log.Printf("[GIN] %s %s %d %s",
c.Request.Method, c.Request.URL.Path, c.Writer.Status(), time.Since(start))
}
}
// StructuredHTTPLogger 将 HTTP 请求结构化落库(分类: http)。
func StructuredHTTPLogger(logSvc *service.SystemLogService) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
if logSvc == nil {
return
}
latencyMs := time.Since(start).Milliseconds()
status := c.Writer.Status()
level := "info"
if status >= 500 {
level = "error"
} else if status >= 400 || latencyMs >= 2000 {
level = "warn"
}
var userID *uint
if v := c.GetHeader("X-User-Id"); v != "" {
if id, err := strconv.ParseUint(v, 10, 64); err == nil && id > 0 {
t := uint(id)
userID = &t
}
}
traceID := ""
if v, ok := c.Get("trace_id"); ok {
if s, ok2 := v.(string); ok2 {
traceID = s
}
}
_ = logSvc.Create(service.CreateSystemLogInput{
Level: level,
Category: "http",
Event: "http_request",
Source: "backend",
TraceID: traceID,
UserID: userID,
Message: c.Request.Method + " " + c.Request.URL.Path,
Meta: map[string]interface{}{
"status": status,
"latency_ms": latencyMs,
"path": c.Request.URL.Path,
"method": c.Request.Method,
"query": c.Request.URL.RawQuery,
},
})
}
}
func CORS() gin.HandlerFunc {
return cors.New(cors.Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "X-User-Id", "X-Trace-Id"},
AllowCredentials: false,
})
}
// RequireAuth 认证中间件:要求请求头中包含有效的 X-User-Id
func RequireAuth() gin.HandlerFunc {
return func(c *gin.Context) {
userIDStr := c.GetHeader("X-User-Id")
if userIDStr == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未授权访问,请提供 X-User-Id 请求头"})
c.Abort()
return
}
userID, err := strconv.ParseUint(userIDStr, 10, 64)
if err != nil || userID == 0 {
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户ID不合法"})
c.Abort()
return
}
// 将用户ID存储到上下文中,供后续使用
c.Set("user_id", uint(userID))
c.Next()
}
}