140 lines
4.3 KiB
Go
140 lines
4.3 KiB
Go
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)
|
||
})
|
||
}
|