middleware.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package middleware
  2. import (
  3. "crypto/rand"
  4. "encoding/hex"
  5. "log"
  6. "net/http"
  7. "strconv"
  8. "time"
  9. "github.com/2930134478/AI-CS/backend/service"
  10. "github.com/gin-contrib/cors"
  11. "github.com/gin-gonic/gin"
  12. )
  13. func newTraceID() string {
  14. var b [8]byte
  15. if _, err := rand.Read(b[:]); err != nil {
  16. return strconv.FormatInt(time.Now().UnixNano(), 10)
  17. }
  18. return hex.EncodeToString(b[:])
  19. }
  20. // TraceID 为每个请求注入 trace_id,便于链路排障。
  21. func TraceID() gin.HandlerFunc {
  22. return func(c *gin.Context) {
  23. traceID := c.GetHeader("X-Trace-Id")
  24. if traceID == "" {
  25. traceID = newTraceID()
  26. }
  27. c.Set("trace_id", traceID)
  28. c.Writer.Header().Set("X-Trace-Id", traceID)
  29. c.Next()
  30. }
  31. }
  32. func Logger() gin.HandlerFunc {
  33. return func(c *gin.Context) {
  34. start := time.Now()
  35. //继续调用后续的中间件处理函数
  36. c.Next()
  37. log.Printf("[GIN] %s %s %d %s",
  38. c.Request.Method, c.Request.URL.Path, c.Writer.Status(), time.Since(start))
  39. }
  40. }
  41. // StructuredHTTPLogger 将 HTTP 请求结构化落库(分类: http)。
  42. func StructuredHTTPLogger(logSvc *service.SystemLogService) gin.HandlerFunc {
  43. return func(c *gin.Context) {
  44. start := time.Now()
  45. c.Next()
  46. if logSvc == nil {
  47. return
  48. }
  49. latencyMs := time.Since(start).Milliseconds()
  50. status := c.Writer.Status()
  51. level := "info"
  52. if status >= 500 {
  53. level = "error"
  54. } else if status >= 400 || latencyMs >= 2000 {
  55. level = "warn"
  56. }
  57. var userID *uint
  58. if v := c.GetHeader("X-User-Id"); v != "" {
  59. if id, err := strconv.ParseUint(v, 10, 64); err == nil && id > 0 {
  60. t := uint(id)
  61. userID = &t
  62. }
  63. }
  64. traceID := ""
  65. if v, ok := c.Get("trace_id"); ok {
  66. if s, ok2 := v.(string); ok2 {
  67. traceID = s
  68. }
  69. }
  70. _ = logSvc.Create(service.CreateSystemLogInput{
  71. Level: level,
  72. Category: "http",
  73. Event: "http_request",
  74. Source: "backend",
  75. TraceID: traceID,
  76. UserID: userID,
  77. Message: c.Request.Method + " " + c.Request.URL.Path,
  78. Meta: map[string]interface{}{
  79. "status": status,
  80. "latency_ms": latencyMs,
  81. "path": c.Request.URL.Path,
  82. "method": c.Request.Method,
  83. "query": c.Request.URL.RawQuery,
  84. },
  85. })
  86. }
  87. }
  88. func CORS() gin.HandlerFunc {
  89. return cors.New(cors.Config{
  90. AllowOrigins: []string{"*"},
  91. AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
  92. AllowHeaders: []string{"Origin", "Content-Type", "Accept", "X-User-Id", "X-Trace-Id"},
  93. AllowCredentials: false,
  94. })
  95. }
  96. // RequireAuth 认证中间件:要求请求头中包含有效的 X-User-Id
  97. func RequireAuth() gin.HandlerFunc {
  98. return func(c *gin.Context) {
  99. userIDStr := c.GetHeader("X-User-Id")
  100. if userIDStr == "" {
  101. c.JSON(http.StatusUnauthorized, gin.H{"error": "未授权访问,请提供 X-User-Id 请求头"})
  102. c.Abort()
  103. return
  104. }
  105. userID, err := strconv.ParseUint(userIDStr, 10, 64)
  106. if err != nil || userID == 0 {
  107. c.JSON(http.StatusUnauthorized, gin.H{"error": "用户ID不合法"})
  108. c.Abort()
  109. return
  110. }
  111. // 将用户ID存储到上下文中,供后续使用
  112. c.Set("user_id", uint(userID))
  113. c.Next()
  114. }
  115. }