Files
fm_be/internal/repository/mysql/audit_repo.go
2026-07-16 22:16:45 +07:00

167 lines
4.2 KiB
Go

package mysql
import (
"context"
"fmt"
"strings"
"gorm.io/gorm"
"wucher/internal/domain/audit"
)
type AuditRepository struct {
db *gorm.DB
}
func NewAuditRepository(db *gorm.DB) *AuditRepository {
return &AuditRepository{db: db}
}
func (r *AuditRepository) Create(ctx context.Context, entry *audit.AuditLog) error {
return r.db.WithContext(ctx).Create(entry).Error
}
func (r *AuditRepository) List(ctx context.Context, filter string, methods []string, modules []string, actions []string, sort string, limit, offset int) ([]audit.AuditLog, int64, error) {
q := r.db.WithContext(ctx).Model(&audit.AuditLog{})
filter = strings.TrimSpace(filter)
if filter != "" {
like := "%" + strings.ToLower(filter) + "%"
q = q.Where(
`LOWER(request_id) LIKE ? OR LOWER(layer) LIKE ? OR LOWER(action) LIKE ? OR LOWER(method) LIKE ? OR LOWER(path) LIKE ? OR LOWER(message) LIKE ?`,
like, like, like, like, like, like,
)
}
if len(methods) > 0 {
q = q.Where("UPPER(method) IN ?", methods)
}
if len(modules) > 0 {
pathPrefixes := modulePathPrefixes(modules)
if len(pathPrefixes) == 0 {
q = q.Where("LOWER(module) IN ?", modules)
} else {
pathConds := make([]string, 0, len(pathPrefixes))
pathArgs := make([]any, 0, len(pathPrefixes))
for _, prefix := range pathPrefixes {
pathConds = append(pathConds, "LOWER(path) LIKE ?")
pathArgs = append(pathArgs, strings.ToLower(prefix)+"%")
}
cond := fmt.Sprintf("(LOWER(module) IN ? OR ((module IS NULL OR module = '') AND (%s)))", strings.Join(pathConds, " OR "))
args := make([]any, 0, 2+len(pathArgs))
args = append(args, modules)
args = append(args, pathArgs...)
q = q.Where(cond, args...)
}
}
if len(actions) > 0 {
q = q.Where("LOWER(action) IN ?", actions)
}
var total int64
if err := q.Count(&total).Error; err != nil {
return nil, 0, err
}
if strings.TrimSpace(sort) == "" {
sort = "created_at DESC"
}
if limit < 0 {
limit = 0
}
if limit > 200 {
limit = 200
}
if offset < 0 {
offset = 0
}
var out []audit.AuditLog
query := q.Order(sort)
if limit > 0 {
query = query.Limit(limit).Offset(offset)
}
if err := query.Find(&out).Error; err != nil {
return nil, 0, err
}
return out, total, nil
}
func modulePathPrefixes(modules []string) []string {
seen := map[string]struct{}{}
out := make([]string, 0, len(modules))
for _, module := range modules {
var prefix string
switch strings.ToLower(strings.TrimSpace(module)) {
case "air_rescuer_checklist":
prefix = "/api/v1/air-rescuer-checklist/"
case "audit_log":
prefix = "/api/v1/audit-logs/"
case "auth":
prefix = "/api/v1/auth/"
case "base":
prefix = "/api/v1/bases/"
case "branding":
prefix = "/api/v1/branding/"
case "contact":
prefix = "/api/v1/contacts/"
case "duty_roster":
prefix = "/api/v1/duty-roster/"
case "dul":
prefix = "/api/v1/dul/"
case "facility":
prefix = "/api/v1/facilities/"
case "federal_state":
prefix = "/api/v1/federal-state/"
case "file_manager":
prefix = "/api/v1/file-manager/"
case "flight_data":
prefix = "/api/v1/flight-data/"
case "flight":
prefix = "/api/v1/flights/"
case "forces_present":
prefix = "/api/v1/forces-present/"
case "health_insurance_company":
prefix = "/api/v1/health-insurance-companies/"
case "helicopter_file":
prefix = "/api/v1/helicopter-files/"
case "helicopter":
prefix = "/api/v1/helicopters/"
case "hospital":
prefix = "/api/v1/hospital/"
case "icao":
prefix = "/api/v1/icao/"
case "insurance_patient_data":
prefix = "/api/v1/insurance-patient-data/"
case "land":
prefix = "/api/v1/land/"
case "master_setting":
prefix = "/api/v1/master-settings/"
case "medicine":
prefix = "/api/v1/medicine/"
case "mission":
prefix = "/api/v1/mission/"
case "opc":
prefix = "/api/v1/opc/"
case "patient_data":
prefix = "/api/v1/patient-data/"
case "reserve_ac":
prefix = "/api/v1/reserve-acs/"
case "role":
prefix = "/api/v1/roles/"
case "user":
prefix = "/api/v1/users/"
case "vocation":
prefix = "/api/v1/vocation/"
}
if prefix == "" {
continue
}
if _, ok := seen[prefix]; ok {
continue
}
seen[prefix] = struct{}{}
out = append(out, prefix)
}
return out
}