thread-master/backend/internal/model/style_preset/usecase/usecase.go

136 lines
4.2 KiB
Go
Raw Normal View History

package usecase
import (
"context"
"fmt"
"strings"
"haixun-backend/internal/library/clock"
app "haixun-backend/internal/library/errors"
"haixun-backend/internal/library/errors/code"
personadomain "haixun-backend/internal/model/persona/domain/usecase"
"haixun-backend/internal/model/style_preset/domain/entity"
domrepo "haixun-backend/internal/model/style_preset/domain/repository"
domusecase "haixun-backend/internal/model/style_preset/domain/usecase"
"github.com/google/uuid"
)
type stylePresetUseCase struct {
repo domrepo.Repository
persona personadomain.UseCase
}
func NewUseCase(repo domrepo.Repository, persona personadomain.UseCase) domusecase.UseCase {
return &stylePresetUseCase{repo: repo, persona: persona}
}
func (u *stylePresetUseCase) List(ctx context.Context, tenantID, ownerUID, personaID string) ([]domusecase.PresetSummary, error) {
if err := u.require(ctx, tenantID, ownerUID, personaID); err != nil {
return nil, err
}
items, err := u.repo.List(ctx, tenantID, ownerUID, personaID)
if err != nil {
return nil, err
}
out := make([]domusecase.PresetSummary, 0, len(items))
for _, item := range items {
out = append(out, toSummary(item))
}
return out, nil
}
func (u *stylePresetUseCase) Upsert(ctx context.Context, req domusecase.UpsertRequest) (*domusecase.PresetSummary, error) {
if err := u.require(ctx, req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
return nil, err
}
name := strings.TrimSpace(req.Name)
if name == "" {
return nil, app.For(code.Persona).InputMissingRequired("name is required")
}
now := clock.NowUnixNano()
id := strings.TrimSpace(req.PresetID)
item := &entity.Preset{ID: id}
if id == "" || id == "new" {
item.ID = uuid.NewString()
item.CreateAt = now
} else if existing, err := u.repo.Get(ctx, req.TenantID, req.OwnerUID, req.PersonaID, id); err == nil {
item = existing
}
item.TenantID = req.TenantID
item.OwnerUID = req.OwnerUID
item.PersonaID = req.PersonaID
item.Name = name
item.Tone = strings.TrimSpace(req.Tone)
item.CTA = cleanList(req.CTA)
item.BannedWords = cleanList(req.BannedWords)
item.Notes = strings.TrimSpace(req.Notes)
item.UpdateAt = now
if item.CreateAt == 0 {
item.CreateAt = now
}
saved, err := u.repo.Upsert(ctx, item)
if err != nil {
return nil, err
}
if req.Apply && u.persona != nil {
profile := formatPresetProfile(saved)
_, _ = u.persona.Update(ctx, personadomain.UpdateRequest{
TenantID: req.TenantID, OwnerUID: req.OwnerUID, PersonaID: req.PersonaID,
Patch: personadomain.PersonaPatch{StyleProfile: &profile},
})
}
out := toSummary(*saved)
return &out, nil
}
func (u *stylePresetUseCase) Delete(ctx context.Context, tenantID, ownerUID, personaID, presetID string) error {
if err := u.require(ctx, tenantID, ownerUID, personaID); err != nil {
return err
}
return u.repo.Delete(ctx, tenantID, ownerUID, personaID, presetID)
}
func (u *stylePresetUseCase) require(ctx context.Context, tenantID, ownerUID, personaID string) error {
if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
return app.For(code.Auth).AuthUnauthorized("missing actor")
}
if strings.TrimSpace(personaID) == "" {
return app.For(code.Persona).InputMissingRequired("persona_id is required")
}
if u.persona != nil {
_, err := u.persona.Get(ctx, tenantID, ownerUID, personaID)
return err
}
return nil
}
func toSummary(item entity.Preset) domusecase.PresetSummary {
return domusecase.PresetSummary{
ID: item.ID, PersonaID: item.PersonaID, Name: item.Name, Tone: item.Tone,
CTA: append([]string(nil), item.CTA...), BannedWords: append([]string(nil), item.BannedWords...),
Notes: item.Notes, CreateAt: item.CreateAt, UpdateAt: item.UpdateAt,
}
}
func cleanList(values []string) []string {
out := make([]string, 0, len(values))
seen := map[string]struct{}{}
for _, value := range values {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
continue
}
if _, ok := seen[trimmed]; ok {
continue
}
seen[trimmed] = struct{}{}
out = append(out, trimmed)
}
return out
}
func formatPresetProfile(item *entity.Preset) string {
return fmt.Sprintf("語調:%s\nCTA%s\n禁用詞%s\n備註%s", item.Tone, strings.Join(item.CTA, "、"), strings.Join(item.BannedWords, "、"), item.Notes)
}