package repository import ( "database/sql" "fmt" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/pkg/database" ) type OrganizationAuditRepository struct{} func NewOrganizationAuditRepository() *OrganizationAuditRepository { return &OrganizationAuditRepository{} } type auditExecutor interface { Exec(string, ...interface{}) (sql.Result, error) } func writeOrganizationAudit(q auditExecutor, entry *model.OrganizationAuditLog) error { if entry == nil { return fmt.Errorf("audit entry is required") } result, err := q.Exec(`INSERT INTO organization_audit_logs(actor_user_id,organization_id,action,old_value,new_value) VALUES(?,?,?,?,?)`, entry.ActorUserID, entry.OrganizationID, entry.Action, nullableAuditJSON(entry.OldValue), nullableAuditJSON(entry.NewValue)) if err != nil { return err } entry.ID, err = result.LastInsertId() return err } func nullableAuditJSON(value []byte) interface{} { if len(value) == 0 { return nil } return string(value) } func (r *OrganizationAuditRepository) Create(entry *model.OrganizationAuditLog) error { return writeOrganizationAudit(database.DB, entry) } // The caller owns commit/rollback; audit errors must roll back its mutation. func (r *OrganizationAuditRepository) CreateTx(tx *sql.Tx, entry *model.OrganizationAuditLog) error { if tx == nil { return fmt.Errorf("audit transaction is required") } return writeOrganizationAudit(tx, entry) } func (r *OrganizationAuditRepository) ListByOrganization(organizationID int64, limit, offset int) ([]model.OrganizationAuditLog, error) { if organizationID <= 0 || limit <= 0 || limit > 500 || offset < 0 { return nil, fmt.Errorf("valid organization and pagination are required") } rows, err := database.DB.Query(`SELECT id,actor_user_id,organization_id,action,old_value,new_value,created_at FROM organization_audit_logs WHERE organization_id=? ORDER BY id DESC LIMIT ? OFFSET ?`, organizationID, limit, offset) if err != nil { return nil, err } defer rows.Close() entries := make([]model.OrganizationAuditLog, 0) for rows.Next() { var item model.OrganizationAuditLog var org sql.NullInt64 var oldValue, newValue []byte if err := rows.Scan(&item.ID, &item.ActorUserID, &org, &item.Action, &oldValue, &newValue, &item.CreatedAt); err != nil { return nil, err } if org.Valid { item.OrganizationID = &org.Int64 } item.OldValue = oldValue item.NewValue = newValue entries = append(entries, item) } return entries, rows.Err() }