middleware.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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. if !logSvc.ShouldPersistLevel(level) {
  58. return
  59. }
  60. var userID *uint
  61. if v := c.GetHeader("X-User-Id"); v != "" {
  62. if id, err := strconv.ParseUint(v, 10, 64); err == nil && id > 0 {
  63. t := uint(id)
  64. userID = &t
  65. }
  66. }
  67. traceID := ""
  68. if v, ok := c.Get("trace_id"); ok {
  69. if s, ok2 := v.(string); ok2 {
  70. traceID = s
  71. }
  72. }
  73. _ = logSvc.Create(service.CreateSystemLogInput{
  74. Level: level,
  75. Category: "http",
  76. Event: "http_request",
  77. Source: "backend",
  78. TraceID: traceID,
  79. UserID: userID,
  80. Message: c.Request.Method + " " + c.Request.URL.Path,
  81. Meta: map[string]interface{}{
  82. "status": status,
  83. "latency_ms": latencyMs,
  84. "path": c.Request.URL.Path,
  85. "method": c.Request.Method,
  86. "query": c.Request.URL.RawQuery,
  87. },
  88. })
  89. }
  90. }
  91. func CORS() gin.HandlerFunc {
  92. return cors.New(cors.Config{
  93. AllowOrigins: []string{"*"},
  94. AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
  95. AllowHeaders: []string{"Origin", "Content-Type", "Accept", "X-User-Id", "X-Trace-Id"},
  96. AllowCredentials: false,
  97. })
  98. }
  99. // RequireAuth 认证中间件:要求请求头中包含有效的 X-User-Id
  100. func RequireAuth() gin.HandlerFunc {
  101. return func(c *gin.Context) {
  102. userIDStr := c.GetHeader("X-User-Id")
  103. if userIDStr == "" {
  104. c.JSON(http.StatusUnauthorized, gin.H{"error": "未授权访问,请提供 X-User-Id 请求头"})
  105. c.Abort()
  106. return
  107. }
  108. userID, err := strconv.ParseUint(userIDStr, 10, 64)
  109. if err != nil || userID == 0 {
  110. c.JSON(http.StatusUnauthorized, gin.H{"error": "用户ID不合法"})
  111. c.Abort()
  112. return
  113. }
  114. // 将用户ID存储到上下文中,供后续使用
  115. c.Set("user_id", uint(userID))
  116. c.Next()
  117. }
  118. }