Files
digital-psychology/apps/api/internal/service/admin/image_card_deck_write.go
T
jackyu66gitandCursor 0158036341 feat(ECR-045): ImageCardDeck 写面闭环并 Closed
复用 admin.explore.write、POST/PUT+审计、C端 GET /cards/decks、
H5 无 active 空态/失败回退;migration 000054。无牌面内容编辑。
ExploreConfig Loop STOP,禁自动 ECR-046。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 21:29:43 +08:00

72 lines
2.3 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 (
ErrInvalidImageCardDeck = errors.New("invalid image card deck")
ErrImageCardDeckConflict = errors.New("image card deck code conflict")
imageCardDeckCodeRe = regexp.MustCompile(`^[a-z][a-z0-9_]{1,62}$`)
)
// ImageCardDeckWriteBody is JSON for create/update.
type ImageCardDeckWriteBody struct {
Code string `json:"code"`
Title string `json:"title"`
Active bool `json:"active"`
}
// CreateImageCardDeck validates, inserts, audits.
func (s *Service) CreateImageCardDeck(ctx context.Context, adminID uuid.UUID, body ImageCardDeckWriteBody) (*repository.ImageCardDeckRow, error) {
in, err := normalizeImageCardDeckWrite(body)
if err != nil {
return nil, err
}
meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active})
row, err := s.Repo.CreateImageCardDeckWithAudit(ctx, adminID, in, meta)
if repository.ImageCardDeckCodeConflict(err) {
return nil, ErrImageCardDeckConflict
}
return row, err
}
// UpdateImageCardDeck validates, updates, audits.
func (s *Service) UpdateImageCardDeck(ctx context.Context, adminID, id uuid.UUID, body ImageCardDeckWriteBody) (*repository.ImageCardDeckRow, error) {
in, err := normalizeImageCardDeckWrite(body)
if err != nil {
return nil, err
}
meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active})
row, err := s.Repo.UpdateImageCardDeckWithAudit(ctx, adminID, id, in, meta)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrImageCardDeckNotFound
}
if repository.ImageCardDeckCodeConflict(err) {
return nil, ErrImageCardDeckConflict
}
return row, err
}
func normalizeImageCardDeckWrite(body ImageCardDeckWriteBody) (repository.ImageCardDeckWriteInput, error) {
code := strings.TrimSpace(body.Code)
title := strings.TrimSpace(body.Title)
if !imageCardDeckCodeRe.MatchString(code) {
return repository.ImageCardDeckWriteInput{}, ErrInvalidImageCardDeck
}
if title == "" || utf8.RuneCountInString(title) > 128 {
return repository.ImageCardDeckWriteInput{}, ErrInvalidImageCardDeck
}
return repository.ImageCardDeckWriteInput{Code: code, Title: title, Active: body.Active}, nil
}