package job import ( "context" "fmt" liboutreach "haixun-backend/internal/library/outreach" aiusecase "haixun-backend/internal/model/ai/usecase" branddomain "haixun-backend/internal/model/brand/domain/usecase" jobdom "haixun-backend/internal/model/job/domain/usecase" kgdomain "haixun-backend/internal/model/knowledge_graph/domain/usecase" outreachdraftdomain "haixun-backend/internal/model/outreach_draft/domain/usecase" personadomain "haixun-backend/internal/model/persona/domain/usecase" scanpostdomain "haixun-backend/internal/model/scan_post/domain/usecase" threadsaccountdomain "haixun-backend/internal/model/threads_account/domain/usecase" ) type GenerateOutreachDraftDeps struct { Jobs jobdom.UseCase Brand branddomain.UseCase Persona personadomain.UseCase ScanPost scanpostdomain.UseCase KnowledgeGraph kgdomain.UseCase ThreadsAccount threadsaccountdomain.UseCase AI aiusecase.UseCase OutreachDraft outreachdraftdomain.UseCase } func RegisterGenerateOutreachDraftHandler(runner *Runner, deps GenerateOutreachDraftDeps) { if runner == nil { return } runner.RegisterStepHandler("outreach_draft_generate", func(ctx context.Context, step StepContext) error { return runGenerateOutreachDraft(ctx, step, deps) }) } func runGenerateOutreachDraft(ctx context.Context, step StepContext, deps GenerateOutreachDraftDeps) error { payload := step.Run.Payload tenantID, ownerUID := runActorFromPayload(payload, step.Run) brandID := stringField(payload, "brand_id") topicID := stringField(payload, "topic_id") scanPostID := stringField(payload, "scan_post_id") voicePersonaID := stringField(payload, "voice_persona_id") if tenantID == "" || ownerUID == "" || brandID == "" || scanPostID == "" || voicePersonaID == "" { return fmt.Errorf("generate-outreach-draft payload missing tenant_id, owner_uid, brand_id, scan_post_id, or voice_persona_id") } updateProgress := func(summary string, percentage int) { _ = step.Heartbeat(ctx) _, _ = deps.Jobs.UpdateProgress(ctx, jobdom.UpdateProgressRequest{ JobID: step.JobID, WorkerID: step.WorkerID, Phase: "outreach_draft_generate", Summary: summary, Percentage: percentage, }) } updateProgress("讀取貼文與產品脈絡…", 15) updateProgress("呼叫 AI 生成獲客草稿…", 45) count := intField(payload, "count") if count <= 0 { count = 3 } regenerate := false if v, ok := payload["regenerate"].(bool); ok { regenerate = v } _, err := liboutreach.GenerateDraft(ctx, liboutreach.GenerateDraftDeps{ Brand: deps.Brand, Persona: deps.Persona, ScanPost: deps.ScanPost, KnowledgeGraph: deps.KnowledgeGraph, ThreadsAccount: deps.ThreadsAccount, AI: deps.AI, OutreachDraft: deps.OutreachDraft, }, liboutreach.GenerateDraftRequest{ TenantID: tenantID, OwnerUID: ownerUID, BrandID: brandID, TopicID: topicID, ScanPostID: scanPostID, Count: count, VoicePersonaID: voicePersonaID, ProductID: stringField(payload, "product_id"), Regenerate: regenerate, }) if err != nil { return err } updateProgress("草稿已寫入", 100) return nil }