142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
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"
|
|
}
|