thread-master/old/backend/internal/library/personacopy/rewrite_draft.go

140 lines
4.3 KiB
Go
Raw Normal View History

package personacopy
import (
"context"
"fmt"
"strings"
"time"
libformula "haixun-backend/internal/library/formula"
"haixun-backend/internal/library/style8d"
domai "haixun-backend/internal/model/ai/domain/usecase"
aiusecase "haixun-backend/internal/model/ai/usecase"
cfentity "haixun-backend/internal/model/content_formula/domain/entity"
contentformuladomain "haixun-backend/internal/model/content_formula/domain/usecase"
copydraftdomain "haixun-backend/internal/model/copy_draft/domain/usecase"
personadomain "haixun-backend/internal/model/persona/domain/usecase"
placementusecase "haixun-backend/internal/model/placement/usecase"
threadsaccountdomain "haixun-backend/internal/model/threads_account/domain/usecase"
)
type RewriteDraftInput struct {
TenantID string
OwnerUID string
PersonaID string
AccountID string
ReferenceText string
Topic string
Brief string
SaveLabel string
UseWebSearch bool
DraftCount int
}
type RewriteDraftDeps struct {
Persona personadomain.UseCase
ContentFormula contentformuladomain.UseCase
CopyDraft copydraftdomain.UseCase
ThreadsAccount threadsaccountdomain.UseCase
Placement placementusecase.UseCase
AI domai.UseCase
}
func RunRewriteDraft(ctx context.Context, deps RewriteDraftDeps, in RewriteDraftInput, progress ProgressFn) (int, error) {
if progress == nil {
progress = func(string, int) {}
}
referenceText := strings.TrimSpace(in.ReferenceText)
topic := strings.TrimSpace(in.Topic)
if referenceText == "" {
return 0, fmt.Errorf("reference_text is required")
}
if topic == "" {
return 0, fmt.Errorf("topic is required")
}
progress("讀取人設…", 5)
persona, err := deps.Persona.Get(ctx, in.TenantID, in.OwnerUID, in.PersonaID)
if err != nil {
return 0, err
}
if !style8d.HasReady8D(persona.Persona, persona.StyleProfile) {
return 0, fmt.Errorf("請先完成人設 8D 對標分析")
}
credential, err := deps.ThreadsAccount.ResolveMemberAiCredential(ctx, in.TenantID, in.OwnerUID)
if err != nil {
return 0, err
}
providerID, err := aiusecase.MapWorkerProvider(credential.Provider)
if err != nil {
return 0, err
}
aiReq := domai.GenerateRequest{
Provider: providerID,
Model: credential.Model,
Credential: domai.Credential{APIKey: credential.APIKey},
}
personaBlock := style8d.ResolvePersonaBlock(persona.Persona, persona.StyleProfile, persona.Brief)
progress(fmt.Sprintf("分析參考貼文寫法(%s / %s…", credential.Provider, credential.Model), 15)
analysis, err := libformula.AnalyzePasted(ctx, deps.AI, aiReq, libformula.AnalyzePastedInput{
PostText: referenceText,
PersonaBlock: personaBlock,
})
if err != nil {
return 0, fmt.Errorf("分析參考貼文失敗:%w", err)
}
if analysis == nil {
return 0, fmt.Errorf("分析未產出有效寫法公式")
}
label := strings.TrimSpace(in.SaveLabel)
if label == "" {
label = fmt.Sprintf("貼文改寫 %s", time.Now().UTC().Format("2006-01-02"))
}
progress("存入寫法公式…", 30)
saved, err := deps.ContentFormula.Create(ctx, contentformuladomain.CreateRequest{
TenantID: in.TenantID,
OwnerUID: in.OwnerUID,
AccountID: in.AccountID,
Label: label,
SourceType: cfentity.SourcePaste,
SourceRef: "paste",
SourcePostText: referenceText,
Summary: analysis.Summary,
Wins: analysis.Wins,
Improvements: analysis.Improvements,
Formula: analysis.Formula,
PostTemplate: analysis.PostTemplate,
HookPattern: analysis.HookPattern,
Structure: analysis.Structure,
ReplicationTips: analysis.ReplicationTips,
Avoid: analysis.Avoid,
})
if err != nil {
return 0, err
}
return RunFormulaDraft(ctx, FormulaDraftDeps{
Persona: deps.Persona,
ContentFormula: deps.ContentFormula,
CopyDraft: deps.CopyDraft,
ThreadsAccount: deps.ThreadsAccount,
Placement: deps.Placement,
AI: deps.AI,
}, FormulaDraftInput{
TenantID: in.TenantID,
OwnerUID: in.OwnerUID,
PersonaID: in.PersonaID,
AccountID: in.AccountID,
FormulaID: saved.ID,
Topic: topic,
Brief: in.Brief,
UseWebSearch: in.UseWebSearch,
DraftCount: in.DraftCount,
}, func(summary string, percentage int) {
adjusted := 35 + (percentage * 65 / 100)
progress(summary, adjusted)
})
}