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

136 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}