package middleware import ( "bytes" "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/go-nunu/nunu-layout-advanced/internal/model" "github.com/go-nunu/nunu-layout-advanced/internal/service" "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "io" "net/http" "strconv" ) // OperationLogMiddleware 记录操作日志到数据库 func OperationLogMiddleware(logService service.LogService) gin.HandlerFunc { return func(c *gin.Context) { var requestBody []byte if c.Request.Body != nil { requestBody, _ = io.ReadAll(c.Request.Body) c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) } blw := &bodyLogWriter{ body: bytes.NewBufferString(""), ResponseWriter: c.Writer, } c.Writer = blw c.Next() go func() { ctxCopy := c.Copy() statusCode := ctxCopy.Writer.Status() var userID uint if claims, exists := ctxCopy.Get("claims"); exists { if customClaims, ok := claims.(*jwt.MyCustomClaims); ok { userID = customClaims.UserId } } // 如果JWT和Query中都没有,尝试从请求体中获取 if userID == 0 && len(requestBody) > 0 { var bodyData map[string]interface{} if err := json.Unmarshal(requestBody, &bodyData); err == nil { if uidVal, ok := bodyData["uid"]; ok { if uidFloat, ok := uidVal.(float64); ok { userID = uint(uidFloat) } } } } if userID == 0 { if uidStr := ctxCopy.Query("uid"); uidStr != "" { if uid, err := strconv.ParseUint(uidStr, 10, 64); err == nil { userID = uint(uid) } } } if userID == 0 && (ctxCopy.Request.URL.Path == "/api/v1/login" || ctxCopy.Request.URL.Path == "/api/v1/user/login") && statusCode == http.StatusOK { var response struct { Data struct { ID uint `json:"id"` } `json:"data"` } if err := json.Unmarshal(blw.body.Bytes(), &response); err == nil { userID = response.Data.ID } } var traceID string if id, ok := ctxCopy.Get(TraceIDKey); ok { traceID, _ = id.(string) } var requestBodyForLog interface{} if len(requestBody) > 0 { if err := json.Unmarshal(requestBody, &requestBodyForLog); err != nil { // 如果解析失败, 则按原始字符串存储 requestBodyForLog = string(requestBody) } } var responseBodyForLog interface{} responseBodyBytes := blw.body.Bytes() if len(responseBodyBytes) > 0 { if err := json.Unmarshal(responseBodyBytes, &responseBodyForLog); err != nil { // 如果解析失败, 则按原始字符串存储 responseBodyForLog = blw.body.String() } } // 只记录请求体到 ExtraData extraData, _ := json.Marshal(requestBodyForLog) logEntry := &model.Log{ TraceId: traceID, Uid: int64(userID), RequestIp: ctxCopy.ClientIP(), Api: ctxCopy.Request.RequestURI, Message: fmt.Sprintf("[%s] %s - %d", ctxCopy.Request.Method, ctxCopy.Request.URL.Path, statusCode), ExtraData: extraData, UserAgent: ctxCopy.Request.UserAgent(), StatusCode: statusCode, } _ = logService.AddLog(ctxCopy, logEntry) }() } }