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

84 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 audit 提供了用户操作审计相关的功能
package audit
import (
"encoding/json"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
"github.com/gin-gonic/gin"
"gorm.io/datatypes"
)
const (
// ContextUserKey 是存储在 gin.Context 中的用户对象的键名
ContextUserKey = "user"
)
// Service 定义了审计服务的接口
type Service interface {
LogAction(c *gin.Context, actionType, description string, targetResource interface{}, status string, resultDetails string)
}
// service 是 Service 接口的实现
type service struct {
userActionLogRepository repository.UserActionLogRepository
logger *logs.Logger
}
// NewService 创建一个新的审计服务实例
func NewService(repo repository.UserActionLogRepository, logger *logs.Logger) Service {
return &service{userActionLogRepository: repo, logger: logger}
}
// LogAction 记录一个用户操作。它在一个新的 goroutine 中异步执行,以避免阻塞主请求。
func (s *service) LogAction(c *gin.Context, actionType, description string, targetResource interface{}, status string, resultDetails string) {
// 从 context 中获取预加载的用户信息
userCtx, exists := c.Get(ContextUserKey)
if !exists {
// 如果上下文中没有用户信息(例如,在未认证的路由上调用了此函数),则不记录日志
s.logger.Warnw("无法记录审计日志:上下文中缺少用户信息")
return
}
user, ok := userCtx.(*models.User)
if !ok {
s.logger.Errorw("无法记录审计日志:上下文中的用户对象类型不正确")
return
}
// 将 targetResource 转换为 JSONB如果失败则记录错误但继续
var targetResourceJSON datatypes.JSON
if targetResource != nil {
bytes, err := json.Marshal(targetResource)
if err != nil {
s.logger.Errorw("无法记录审计日志:序列化 targetResource 失败", "error", err)
} else {
targetResourceJSON = bytes
}
}
log := &models.UserActionLog{
Time: time.Now(),
UserID: user.ID,
Username: user.Username, // 用户名快照
SourceIP: c.ClientIP(),
ActionType: actionType,
TargetResource: targetResourceJSON,
Description: description,
Status: status,
HTTPPath: c.Request.URL.Path,
HTTPMethod: c.Request.Method,
ResultDetails: resultDetails,
}
// 异步写入数据库,不阻塞当前请求
go func() {
if err := s.userActionLogRepository.Create(log); err != nil {
s.logger.Errorw("异步保存审计日志失败", "error", err)
}
}()
}