package repository import ( "context" "encoding/json" "errors" "strings" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" ) // ScaleDefinitionWriteInput is explore-side metadata payload (no status). type ScaleDefinitionWriteInput struct { Slug string Title string Description string } // CreateScaleDefinitionWithAudit inserts draft scale metadata + audit. func (r *ScaleRepo) CreateScaleDefinitionWithAudit( ctx context.Context, adminID uuid.UUID, in ScaleDefinitionWriteInput, meta json.RawMessage, ) (*ScaleAdminItem, error) { tx, err := r.Pool.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) var it ScaleAdminItem err = tx.QueryRow(ctx, ` INSERT INTO scales(slug, title, description, status) VALUES ($1,$2,$3,'draft') RETURNING id, slug, title, description, status`, in.Slug, in.Title, in.Description, ).Scan(&it.ID, &it.Slug, &it.Title, &it.Description, &it.Status) if err != nil { return nil, mapScaleDefinitionWriteErr(err) } if meta == nil { meta = json.RawMessage(`{}`) } if _, err := tx.Exec(ctx, ` INSERT INTO admin_audit_logs(admin_id, action, target_type, target_id, meta) VALUES ($1,'explore.scale_definition.create','scale',$2,$3)`, adminID, it.ID.String(), meta, ); err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } return &it, nil } // UpdateScaleDefinitionWithAudit updates metadata only (never status). func (r *ScaleRepo) UpdateScaleDefinitionWithAudit( ctx context.Context, adminID, id uuid.UUID, in ScaleDefinitionWriteInput, meta json.RawMessage, ) (*ScaleAdminItem, error) { tx, err := r.Pool.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) var it ScaleAdminItem err = tx.QueryRow(ctx, ` UPDATE scales SET slug=$2, title=$3, description=$4, updated_at=now() WHERE id=$1 AND deleted_at IS NULL RETURNING id, slug, title, description, status`, id, in.Slug, in.Title, in.Description, ).Scan(&it.ID, &it.Slug, &it.Title, &it.Description, &it.Status) if errors.Is(err, pgx.ErrNoRows) { return nil, pgx.ErrNoRows } if err != nil { return nil, mapScaleDefinitionWriteErr(err) } if meta == nil { meta = json.RawMessage(`{}`) } if _, err := tx.Exec(ctx, ` INSERT INTO admin_audit_logs(admin_id, action, target_type, target_id, meta) VALUES ($1,'explore.scale_definition.update','scale',$2,$3)`, adminID, id.String(), meta, ); err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } return &it, nil } func mapScaleDefinitionWriteErr(err error) error { var pgErr *pgconn.PgError if errors.As(err, &pgErr) && pgErr.Code == "23505" { return errString("scale slug conflict") } return err } // ScaleSlugConflict reports unique violation. func ScaleSlugConflict(err error) bool { return err != nil && strings.Contains(err.Error(), "scale slug conflict") }