package system import ( "context" "time" "kra/internal/biz" "kra/pkg/database/pagination" "gorm.io/gorm" ) type operationPO struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` IP, Method, Path string Status int LatencyMS int64 `gorm:"column:latency_ms"` Agent string `gorm:"type:text"` ErrorMessage string Body string `gorm:"type:text"` Response string `gorm:"column:resp;type:text"` UserID uint RequestID string `gorm:"index"` TraceID string `gorm:"index"` DeviceID string } func (operationPO) TableName() string { return "sys_operation_records" } func (r *auditRecorderRepo) RecordOperation(ctx context.Context, v *biz.OperationRecord) error { return r.data.DB().WithContext(ctx).Create(&operationPO{IP: v.IP, Method: v.Method, Path: v.Path, Status: v.Status, LatencyMS: v.LatencyMS, Agent: v.Agent, ErrorMessage: v.ErrorMessage, Body: v.Body, Response: v.Response, UserID: v.UserID, RequestID: v.RequestID, TraceID: v.TraceID, DeviceID: v.DeviceID}).Error } func opFromPO(v operationPO) *biz.OperationRecord { return &biz.OperationRecord{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, IP: v.IP, Method: v.Method, Path: v.Path, Status: v.Status, LatencyMS: v.LatencyMS, Agent: v.Agent, ErrorMessage: v.ErrorMessage, Body: v.Body, Response: v.Response, UserID: v.UserID, RequestID: v.RequestID, TraceID: v.TraceID, DeviceID: v.DeviceID} } func (r *auditQueryRepo) ListOperations(ctx context.Context, page, size int, q *biz.OperationRecord) ([]*biz.OperationRecord, int64, error) { db := r.data.DB().WithContext(ctx).Model(&operationPO{}) if q != nil { if q.Path != "" { db = db.Where("path LIKE ?", "%"+q.Path+"%") } if q.Method != "" { db = db.Where("method = ?", q.Method) } if q.Status != 0 { db = db.Where("status = ?", q.Status) } } var total int64 if err := db.Count(&total).Error; err != nil { return nil, 0, err } var pos []operationPO if err := pagination.ApplyRequired(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil { return nil, 0, err } users := auditUsers(ctx, r.data.DB().WithContext(ctx), operationUserIDs(pos)) out := make([]*biz.OperationRecord, 0, len(pos)) for _, po := range pos { value := opFromPO(po) value.User = users[po.UserID] out = append(out, value) } return out, total, nil } func operationUserIDs(values []operationPO) []uint { ids := make([]uint, 0, len(values)) for _, value := range values { if value.UserID != 0 { ids = append(ids, value.UserID) } } return ids } func auditUsers(ctx context.Context, db *gorm.DB, ids []uint) map[uint]*biz.User { result := make(map[uint]*biz.User) if len(ids) == 0 { return result } var users []userPO if db.WithContext(ctx).Where("id IN ?", ids).Find(&users).Error != nil { return result } for _, value := range users { result[value.ID] = &biz.User{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, UUID: value.UUID, Username: value.Username, NickName: value.NickName, HeaderImg: value.HeaderImg, AuthorityID: value.AuthorityID, DeptID: value.DeptID, Phone: value.Phone, Email: value.Email, Enable: value.Enable} } return result } func (r *auditQueryRepo) FindOperation(ctx context.Context, id uint) (*biz.OperationRecord, error) { var po operationPO if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil { return nil, err } return opFromPO(po), nil } func (r *auditQueryRepo) DeleteOperations(ctx context.Context, ids []int) error { if len(ids) == 1 && ids[0] == 0 { return r.data.DB().WithContext(ctx).Delete(&operationPO{}).Error } return r.data.DB().WithContext(ctx).Delete(&operationPO{}, "id IN ?", ids).Error }