复用 admin.explore.write、POST/PUT+审计、C端 GET /rhythm/configs、 H5 无 active 空态/失败回退;migration 000053。ExploreConfig Loop STOP,禁自动 ECR-045。 Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.2 KiB
Go
72 lines
2.2 KiB
Go
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 (
|
|
ErrInvalidRhythmConfig = errors.New("invalid rhythm config")
|
|
ErrRhythmConfigConflict = errors.New("rhythm config code conflict")
|
|
rhythmConfigCodeRe = regexp.MustCompile(`^[a-z][a-z0-9_]{1,62}$`)
|
|
)
|
|
|
|
// RhythmConfigWriteBody is JSON for create/update.
|
|
type RhythmConfigWriteBody struct {
|
|
Code string `json:"code"`
|
|
Title string `json:"title"`
|
|
Active bool `json:"active"`
|
|
}
|
|
|
|
// CreateRhythmConfig validates, inserts, audits.
|
|
func (s *Service) CreateRhythmConfig(ctx context.Context, adminID uuid.UUID, body RhythmConfigWriteBody) (*repository.RhythmConfigRow, error) {
|
|
in, err := normalizeRhythmConfigWrite(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active})
|
|
row, err := s.Repo.CreateRhythmConfigWithAudit(ctx, adminID, in, meta)
|
|
if repository.RhythmConfigCodeConflict(err) {
|
|
return nil, ErrRhythmConfigConflict
|
|
}
|
|
return row, err
|
|
}
|
|
|
|
// UpdateRhythmConfig validates, updates, audits.
|
|
func (s *Service) UpdateRhythmConfig(ctx context.Context, adminID, id uuid.UUID, body RhythmConfigWriteBody) (*repository.RhythmConfigRow, error) {
|
|
in, err := normalizeRhythmConfigWrite(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active})
|
|
row, err := s.Repo.UpdateRhythmConfigWithAudit(ctx, adminID, id, in, meta)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrRhythmConfigNotFound
|
|
}
|
|
if repository.RhythmConfigCodeConflict(err) {
|
|
return nil, ErrRhythmConfigConflict
|
|
}
|
|
return row, err
|
|
}
|
|
|
|
func normalizeRhythmConfigWrite(body RhythmConfigWriteBody) (repository.RhythmConfigWriteInput, error) {
|
|
code := strings.TrimSpace(body.Code)
|
|
title := strings.TrimSpace(body.Title)
|
|
if !rhythmConfigCodeRe.MatchString(code) {
|
|
return repository.RhythmConfigWriteInput{}, ErrInvalidRhythmConfig
|
|
}
|
|
if title == "" || utf8.RuneCountInString(title) > 128 {
|
|
return repository.RhythmConfigWriteInput{}, ErrInvalidRhythmConfig
|
|
}
|
|
return repository.RhythmConfigWriteInput{Code: code, Title: title, Active: body.Active}, nil
|
|
}
|