You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
2.2 KiB
92 lines
2.2 KiB
1 month ago
|
package middleware
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
1 month ago
|
"gofaster/internal/shared/model"
|
||
1 month ago
|
"io"
|
||
1 month ago
|
"strconv"
|
||
|
"strings"
|
||
1 month ago
|
"time"
|
||
|
|
||
|
"github.com/gin-gonic/gin"
|
||
|
"go.uber.org/zap"
|
||
|
"gorm.io/gorm"
|
||
|
)
|
||
|
|
||
1 month ago
|
// 从 Gin 上下文中获取用户ID(假设 JWT 中间件已将用户信息存入上下文)
|
||
|
func getUserIdFromContext(c *gin.Context) uint {
|
||
|
if userId, exists := c.Get("userID"); exists {
|
||
|
if uid, ok := userId.(uint); ok {
|
||
|
return uid
|
||
|
}
|
||
|
}
|
||
|
return 0 // 0 表示未登录用户或获取失败
|
||
|
}
|
||
|
|
||
|
// 从请求路径解析操作类型(根据 RESTful 路径映射)
|
||
|
func getActionFromPath(path string) string {
|
||
|
// 移除查询参数和版本前缀(如 /api/v1/users -> /users)
|
||
|
cleanPath := strings.Split(path, "?")[0]
|
||
|
segments := strings.Split(cleanPath, "/")
|
||
|
lastSegment := segments[len(segments)-1]
|
||
|
|
||
|
// 常见 RESTful 操作映射
|
||
|
switch lastSegment {
|
||
|
case "login":
|
||
|
return "用户登录"
|
||
|
case "logout":
|
||
|
return "用户登出"
|
||
|
case "users":
|
||
|
return "用户管理"
|
||
|
case "roles":
|
||
|
return "角色管理"
|
||
|
case "permissions":
|
||
|
return "权限管理"
|
||
|
default:
|
||
|
// 如果是数字ID(如 /users/123),取上一级路径
|
||
|
if _, err := strconv.Atoi(lastSegment); err == nil && len(segments) > 1 {
|
||
|
return getActionFromPath(strings.Join(segments[:len(segments)-1], "/"))
|
||
|
}
|
||
|
return "未知操作"
|
||
|
}
|
||
|
}
|
||
|
|
||
1 month ago
|
func LoggerMiddleware(logger *zap.Logger, db *gorm.DB) gin.HandlerFunc {
|
||
|
return func(c *gin.Context) {
|
||
|
start := time.Now()
|
||
|
path := c.Request.URL.Path
|
||
|
|
||
|
// 记录请求体
|
||
|
var requestBody string
|
||
|
if c.Request.Body != nil {
|
||
|
bodyBytes, _ := io.ReadAll(c.Request.Body)
|
||
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||
|
requestBody = string(bodyBytes)
|
||
|
}
|
||
|
|
||
|
// 处理请求
|
||
|
c.Next()
|
||
|
|
||
|
// 记录日志
|
||
|
latency := time.Since(start).Milliseconds()
|
||
|
actionLog := model.ActionLog{
|
||
|
UserID: getUserIdFromContext(c),
|
||
|
Action: getActionFromPath(path),
|
||
|
IP: c.ClientIP(),
|
||
|
UserAgent: c.Request.UserAgent(),
|
||
|
Path: path,
|
||
|
Method: c.Request.Method,
|
||
|
Request: requestBody,
|
||
|
Status: c.Writer.Status(),
|
||
|
Latency: latency,
|
||
|
}
|
||
|
|
||
|
// 异步保存日志
|
||
|
go func() {
|
||
|
if err := db.Create(&actionLog).Error; err != nil {
|
||
|
logger.Error("保存操作日志失败", zap.Error(err))
|
||
|
}
|
||
|
}()
|
||
|
}
|
||
|
}
|