package middlewares import ( "bytes" "io" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" "goalfymax-admin/internal/models" "goalfymax-admin/pkg/utils" ) // API日志中间件 - 记录所有接口的调用信息 func APILogMiddleware(logger *utils.Logger) gin.HandlerFunc { return func(c *gin.Context) { // 排除健康检查接口 if c.Request.URL.Path == "/health" { c.Next() return } // 记录开始时间 startTime := time.Now() // 读取请求体(需要保存以便后续使用) var requestBody []byte if c.Request.Body != nil { requestBody, _ = io.ReadAll(c.Request.Body) c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) } // 获取用户信息 userID := 0 userEmail := "unknown" if userInfo, exists := c.Get("user"); exists { if user, ok := userInfo.(*models.UserInfo); ok && user != nil { if user.Email != "" { userEmail = user.Email } else if user.PreferredUsername != "" { userEmail = user.PreferredUsername + "@goalfy.com" } // 尝试获取用户ID if id, exists := c.Get("user_id"); exists { switch v := id.(type) { case int: userID = v case uint: userID = int(v) } } } } // 创建响应写入器包装器以捕获响应 responseWriter := &responseBodyWriter{ ResponseWriter: c.Writer, body: &bytes.Buffer{}, } c.Writer = responseWriter // 处理请求 c.Next() // 计算耗时 duration := time.Since(startTime) // 获取响应信息 responseCode := c.Writer.Status() responseBody := responseWriter.body.String() // 限制响应体大小(避免打印过大的响应) if len(responseBody) > 1000 { responseBody = responseBody[:1000] + "...(truncated)" } // 限制请求体大小 requestBodyStr := string(requestBody) if len(requestBodyStr) > 1000 { requestBodyStr = requestBodyStr[:1000] + "...(truncated)" } // 获取请求信息 method := c.Request.Method path := c.Request.URL.Path // 提取模块名称 module := extractModule(path) // 打印日志 logger.Info("📝 [API日志] 接口调用记录", zap.String("method", method), zap.String("path", path), zap.String("full_path", c.Request.URL.String()), zap.String("module", module), zap.Int("user_id", userID), zap.String("user_email", userEmail), zap.String("ip_address", c.ClientIP()), zap.String("user_agent", c.Request.UserAgent()), zap.String("request_body", requestBodyStr), zap.Int("response_code", responseCode), zap.String("response_body", responseBody), zap.Duration("duration", duration), zap.Int64("duration_ms", duration.Milliseconds()), zap.String("status", getStatus(responseCode)), ) } } // responseBodyWriter 用于捕获响应体 type responseBodyWriter struct { gin.ResponseWriter body *bytes.Buffer } func (w *responseBodyWriter) Write(b []byte) (int, error) { w.body.Write(b) return w.ResponseWriter.Write(b) } // extractModule 从路径提取模块名称 func extractModule(path string) string { parts := strings.Split(strings.Trim(path, "/"), "/") if len(parts) >= 3 { return parts[2] // /api/admin/{module} } return "unknown" } // getStatus 根据响应码获取状态 func getStatus(code int) string { if code >= 200 && code < 300 { return "success" } else if code >= 400 && code < 500 { return "client_error" } else if code >= 500 { return "server_error" } return "unknown" }