package persona import ( "context" "strings" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" libthreads "haixun-backend/internal/library/threadsapi" "haixun-backend/internal/library/threadspost" copydraftdomain "haixun-backend/internal/model/copy_draft/domain/usecase" "haixun-backend/internal/svc" "haixun-backend/internal/types" ) type PublishPersonaCopyDraftLogic struct { ctx context.Context svcCtx *svc.ServiceContext } func NewPublishPersonaCopyDraftLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PublishPersonaCopyDraftLogic { return &PublishPersonaCopyDraftLogic{ctx: ctx, svcCtx: svcCtx} } func (l *PublishPersonaCopyDraftLogic) PublishPersonaCopyDraft( req *types.PublishCopyDraftHandlerReq, ) (*types.PublishCopyDraftData, error) { tenantID, uid, err := actorFrom(l.ctx) if err != nil { return nil, err } personaID := strings.TrimSpace(req.ID) draftID := strings.TrimSpace(req.DraftID) if !req.Confirm { return nil, app.For(code.Persona).InputMissingRequired("請確認 confirm=true 後再發布貼文") } if _, err := l.svcCtx.Persona.Get(l.ctx, tenantID, uid, personaID); err != nil { return nil, err } draftItem, err := l.svcCtx.CopyDraft.Get(l.ctx, tenantID, uid, personaID, draftID) if err != nil { return nil, err } draft := draftItem if draft.Status == "published" { return nil, app.For(code.Persona).ResInvalidState("此草稿已發布") } text := strings.TrimSpace(req.Text) if text == "" { text = strings.TrimSpace(draft.Text) } if text == "" { return nil, app.For(code.Persona).InputMissingRequired("text is required") } if err := threadspost.ValidatePublish(text); err != nil { return nil, app.For(code.Persona).InputInvalidFormat(err.Error()) } if strings.TrimSpace(req.Text) != "" && req.Text != draft.Text { updatedText, err := l.svcCtx.CopyDraft.Update(l.ctx, copydraftdomain.UpdateRequest{ TenantID: tenantID, OwnerUID: uid, PersonaID: personaID, DraftID: draftID, Patch: copydraftdomain.CopyDraftPatch{Text: &req.Text}, }) if err != nil { return nil, err } draft = updatedText text = strings.TrimSpace(updatedText.Text) } cred, err := l.svcCtx.ThreadsAccount.ResolveMemberThreadsPublishCredential(l.ctx, tenantID, uid) if err != nil { return nil, err } result, err := libthreads.PublishText(l.ctx, libthreads.PublishTextInput{ ThreadsUserID: cred.ThreadsUserID, AccessToken: cred.AccessToken, Text: text, }) if err != nil { return nil, app.For(code.ThreadsAccount).SvcThirdParty("Threads API 發布貼文失敗:" + err.Error()) } updated, err := l.svcCtx.CopyDraft.MarkPublished(l.ctx, copydraftdomain.MarkPublishedRequest{ TenantID: tenantID, OwnerUID: uid, PersonaID: personaID, DraftID: draftID, MediaID: result.MediaID, Permalink: result.Permalink, }) if err != nil { return nil, err } return &types.PublishCopyDraftData{ DraftID: draftID, MediaID: result.MediaID, Permalink: result.Permalink, Status: updated.Status, Message: "仿寫貼文已透過 Threads API 發布", }, nil }