package repository import ( "context" "database/sql" "fmt" "tailly_back_v2/internal/domain" ) type AuditRepository interface { Save(ctx context.Context, log *domain.AuditLog) error Get(ctx context.Context, filter domain.AuditFilter) ([]*domain.AuditLog, error) } type auditRepository struct { db *sql.DB } func NewAuditRepository(db *sql.DB) AuditRepository { return &auditRepository{db: db} } func (r *auditRepository) Save(ctx context.Context, log *domain.AuditLog) error { query := ` INSERT INTO audit_logs ( user_id, action, entity_type, entity_id, ip_address, user_agent, metadata, status, created_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id ` var userID, entityID interface{} if log.UserID != nil { userID = *log.UserID } else { userID = nil } if log.EntityID != nil { entityID = *log.EntityID } else { entityID = nil } err := r.db.QueryRowContext(ctx, query, userID, log.Action, log.EntityType, entityID, log.IPAddress, log.UserAgent, log.Metadata, log.Status, log.CreatedAt, ).Scan(&log.ID) return err } func (r *auditRepository) Get(ctx context.Context, filter domain.AuditFilter) ([]*domain.AuditLog, error) { query := ` SELECT id, user_id, action, entity_type, entity_id, ip_address, user_agent, metadata, status, created_at FROM audit_logs WHERE 1=1 ` args := []interface{}{} argPos := 1 if filter.UserID != nil { query += fmt.Sprintf(" AND user_id = $%d", argPos) args = append(args, *filter.UserID) argPos++ } if filter.Action != "" { query += fmt.Sprintf(" AND action = $%d", argPos) args = append(args, filter.Action) argPos++ } if filter.EntityType != "" { query += fmt.Sprintf(" AND entity_type = $%d", argPos) args = append(args, filter.EntityType) argPos++ } if !filter.DateFrom.IsZero() { query += fmt.Sprintf(" AND created_at >= $%d", argPos) args = append(args, filter.DateFrom) argPos++ } if !filter.DateTo.IsZero() { query += fmt.Sprintf(" AND created_at <= $%d", argPos) args = append(args, filter.DateTo) argPos++ } query += " ORDER BY created_at DESC" if filter.Limit > 0 { query += fmt.Sprintf(" LIMIT $%d", argPos) args = append(args, filter.Limit) argPos++ } if filter.Offset > 0 { query += fmt.Sprintf(" OFFSET $%d", argPos) args = append(args, filter.Offset) } rows, err := r.db.QueryContext(ctx, query, args...) if err != nil { return nil, err } defer rows.Close() var logs []*domain.AuditLog for rows.Next() { var log domain.AuditLog var userID, entityID sql.NullInt64 err := rows.Scan( &log.ID, &userID, &log.Action, &log.EntityType, &entityID, &log.IPAddress, &log.UserAgent, &log.Metadata, &log.Status, &log.CreatedAt, ) if err != nil { return nil, err } if userID.Valid { uid := int(userID.Int64) log.UserID = &uid } if entityID.Valid { eid := int(entityID.Int64) log.EntityID = &eid } logs = append(logs, &log) } return logs, nil }