package admin import ( "context" "encoding/json" "errors" "regexp" "strings" "unicode/utf8" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/yuxingu/digital-psychology/apps/api/internal/repository" ) var ( ErrInvalidStarConfig = errors.New("invalid star config") ErrStarConfigConflict = errors.New("star config code conflict") starConfigCodeRe = regexp.MustCompile(`^[a-z][a-z0-9_]{1,62}$`) ) // StarConfigWriteBody is JSON for create/update. type StarConfigWriteBody struct { Code string `json:"code"` Title string `json:"title"` Active bool `json:"active"` } // CreateStarConfig validates, inserts, audits. func (s *Service) CreateStarConfig(ctx context.Context, adminID uuid.UUID, body StarConfigWriteBody) (*repository.StarConfigRow, error) { in, err := normalizeStarConfigWrite(body) if err != nil { return nil, err } meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active}) row, err := s.Repo.CreateStarConfigWithAudit(ctx, adminID, in, meta) if repository.StarConfigCodeConflict(err) { return nil, ErrStarConfigConflict } return row, err } // UpdateStarConfig validates, updates, audits. func (s *Service) UpdateStarConfig(ctx context.Context, adminID, id uuid.UUID, body StarConfigWriteBody) (*repository.StarConfigRow, error) { in, err := normalizeStarConfigWrite(body) if err != nil { return nil, err } meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active}) row, err := s.Repo.UpdateStarConfigWithAudit(ctx, adminID, id, in, meta) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrStarConfigNotFound } if repository.StarConfigCodeConflict(err) { return nil, ErrStarConfigConflict } return row, err } func normalizeStarConfigWrite(body StarConfigWriteBody) (repository.StarConfigWriteInput, error) { code := strings.TrimSpace(body.Code) title := strings.TrimSpace(body.Title) if !starConfigCodeRe.MatchString(code) { return repository.StarConfigWriteInput{}, ErrInvalidStarConfig } if title == "" || utf8.RuneCountInString(title) > 128 { return repository.StarConfigWriteInput{}, ErrInvalidStarConfig } return repository.StarConfigWriteInput{Code: code, Title: title, Active: body.Active}, nil }