This commit is contained in:
Joe
2026-01-16 15:49:34 +08:00
commit 550d3e1f42
380 changed files with 62024 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
package middleware
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"goravel/app/http/helpers"
"goravel/app/models"
"goravel/app/services"
"goravel/app/utils"
"goravel/app/utils/logger"
"goravel/app/utils/traceid"
)
// OperationLog 操作日志中间件
func OperationLog() http.Middleware {
return func(ctx http.Context) {
systemLogService := services.NewSystemLogService()
startTime := time.Now()
// 获取请求信息
method := ctx.Request().Method()
path := ctx.Request().Path()
ip := ctx.Request().Ip()
userAgent := ctx.Request().Header("User-Agent", "")
// 获取请求参数(排除敏感信息)
var requestBody string
if method == "POST" || method == "PUT" || method == "PATCH" {
// 获取所有输入参数
inputs := make(map[string]any)
// 记录所有非敏感参数
allInputs := ctx.Request().All()
for key, value := range allInputs {
// 使用工具函数检查是否是敏感字段
if utils.IsSensitiveField(key) {
inputs[key] = "***"
} else {
inputs[key] = value
}
}
if data, err := json.Marshal(inputs); err == nil {
requestBody = string(data)
}
}
// 获取管理员ID(从JWT中间件设置的context中获取)
var adminID uint
if admin, err := helpers.GetAdminFromContext(ctx); err == nil {
adminID = admin.ID
}
// 继续处理请求
ctx.Request().Next()
// 计算耗时
duration := int(time.Since(startTime).Milliseconds())
// 只记录新增、修改、删除操作(POST、PUT、PATCH、DELETE),排除 GET 请求
// 同时排除登录和info接口,以及分片上传的进度查询(GET请求)
// 排除代码生成器相关操作
// 对于分片上传,只记录 merge 操作(最终完成上传),排除 init 和 upload 操作
if (method == "POST" || method == "PUT" || method == "PATCH" || method == "DELETE") &&
path != "/api/admin/login" && path != "/api/admin/info" &&
!strings.HasPrefix(path, "/api/admin/code-generator/") {
// 排除分片上传的中间操作(init 和 upload),只记录 merge(最终完成上传)
if path == "/api/admin/attachments/chunk" {
action := ctx.Request().Input("action", "")
if action == "" {
action = ctx.Request().Query("action", "")
}
// 只记录 merge 操作,排除 init、upload 和 progress
if action != "merge" {
return
}
}
// 在请求处理后再获取一次管理员ID(确保JWT中间件已执行)
// 如果之前没有获取到,再次尝试从context获取
if adminID == 0 {
if admin, err := helpers.GetAdminFromContext(ctx); err == nil {
adminID = admin.ID
}
}
// 默认状态为成功
status := uint8(1)
var errorMsg string
// 在goroutine之前保存所有需要的数据,避免context问题
savedAdminID := adminID
savedMethod := method
savedPath := path
savedIP := ip
savedUserAgent := userAgent
savedRequestBody := requestBody
savedDuration := duration
// 提前获取 traceCtx,用于日志记录
traceCtx := traceid.DeriveContextFromHTTP(ctx)
// 生成操作标题(只使用权限标识)
title := utils.GetOperationTitleFromContext(ctx)
if title == "operation.unknown" {
// 如果无法生成标题,记录调试日志
logger.ErrorfContext(traceCtx, "Failed to generate operation title, method: %s, path: %s", savedMethod, savedPath)
}
operationLog := models.OperationLog{
AdminID: savedAdminID,
Method: savedMethod,
Path: savedPath,
Title: title,
IP: savedIP,
UserAgent: savedUserAgent,
Request: savedRequestBody,
Status: status,
ErrorMsg: errorMsg,
Duration: savedDuration,
}
// 异步记录日志,避免影响响应速度
go func(ctx context.Context) {
if err := facades.Orm().Query().Create(&operationLog); err != nil {
_ = systemLogService.Record(ctx, "error", "operation-log", "failed to persist operation log", map[string]any{
"error": err.Error(),
"path": savedPath,
})
logger.ErrorfContext(ctx, "Failed to create operation log: %v", err)
}
}(traceCtx)
}
}
}