Files
pig-farm-controller/internal/app/middleware/audit.go
2025-09-28 00:13:47 +08:00

99 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package middleware 存放 gin 中间件
package middleware
import (
"bytes"
"encoding/json"
"io"
"strconv"
"git.huangwc.com/pig/pig-farm-controller/internal/app/service/audit"
"github.com/gin-gonic/gin"
)
// --- 审计日志相关上下文键 ---
const (
ContextAuditActionType = "auditActionType"
ContextAuditTargetResource = "auditTargetResource"
ContextAuditDescription = "auditDescription"
)
// AuditLogMiddleware 创建一个Gin中间件用于在请求结束后记录用户操作审计日志
func AuditLogMiddleware(auditService audit.Service) gin.HandlerFunc {
return func(c *gin.Context) {
// 使用自定义的 response body writer 来捕获响应体
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
// 首先执行请求链中的后续处理程序(即业务控制器)
c.Next()
// --- 在这里,请求已经处理完毕 ---
// 从上下文中尝试获取由控制器设置的业务审计信息
actionType, exists := c.Get(ContextAuditActionType)
if !exists {
// 如果上下文中没有 actionType说明此接口无需记录审计日志直接返回
return
}
// 获取其他审计信息
description, _ := c.Get(ContextAuditDescription)
targetResource, _ := c.Get(ContextAuditTargetResource)
// 判断操作状态
status := "success"
resultDetails := ""
if c.Writer.Status() >= 400 {
status = "failed"
// 尝试从捕获的响应体中解析错误信息
var errResp struct {
Error string `json:"error"`
}
if err := json.Unmarshal(blw.body.Bytes(), &errResp); err == nil {
resultDetails = errResp.Error
} else {
resultDetails = "HTTP Status: " + strconv.Itoa(c.Writer.Status())
}
}
// 调用审计服务记录日志(异步)
auditService.LogAction(
c,
actionType.(string),
description.(string),
targetResource,
status,
resultDetails,
)
}
}
// bodyLogWriter 是一个自定义的 gin.ResponseWriter用于捕获响应体
// 这对于在操作失败时记录详细的错误信息非常有用
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w bodyLogWriter) WriteString(s string) (int, error) {
w.body.WriteString(s)
return w.ResponseWriter.WriteString(s)
}
// ReadBody 用于安全地读取请求体,并防止其被重复读取
func ReadBody(c *gin.Context) ([]byte, error) {
bodyBytes, err := io.ReadAll(c.Request.Body)
if err != nil {
return nil, err
}
// 将读取的内容放回 Body 中,以便后续的处理函数可以再次读取
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
return bodyBytes, nil
}