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