thread-master/backend/internal/logic/persona/publish_persona_copy_draft_...

137 lines
3.9 KiB
Go
Raw Normal View History

2026-06-26 08:37:04 +00:00
package persona
import (
"context"
"strings"
2026-06-30 07:09:44 +00:00
"time"
2026-06-26 08:37:04 +00:00
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"
2026-06-30 07:09:44 +00:00
publishqueuedomain "haixun-backend/internal/model/publish_queue/domain/usecase"
2026-06-26 08:37:04 +00:00
"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())
}
2026-07-02 14:52:41 +00:00
topicTag := firstNonEmpty(req.TopicTag, draft.TopicTag)
patch := copydraftdomain.CopyDraftPatch{}
needsUpdate := false
2026-06-26 08:37:04 +00:00
if strings.TrimSpace(req.Text) != "" && req.Text != draft.Text {
2026-07-02 14:52:41 +00:00
patch.Text = &req.Text
needsUpdate = true
}
if strings.TrimSpace(req.TopicTag) != "" && req.TopicTag != draft.TopicTag {
patch.TopicTag = &req.TopicTag
needsUpdate = true
}
if needsUpdate {
updated, err := l.svcCtx.CopyDraft.Update(l.ctx, copydraftdomain.UpdateRequest{
2026-06-26 08:37:04 +00:00
TenantID: tenantID,
OwnerUID: uid,
PersonaID: personaID,
DraftID: draftID,
2026-07-02 14:52:41 +00:00
Patch: patch,
2026-06-26 08:37:04 +00:00
})
if err != nil {
return nil, err
}
2026-07-02 14:52:41 +00:00
draft = updated
text = strings.TrimSpace(updated.Text)
topicTag = firstNonEmpty(updated.TopicTag, req.TopicTag)
2026-06-26 08:37:04 +00:00
}
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,
2026-07-02 14:52:41 +00:00
TopicTag: topicTag,
2026-06-26 08:37:04 +00:00
})
if err != nil {
return nil, app.For(code.ThreadsAccount).SvcThirdParty("Threads API 發布貼文失敗:" + err.Error())
}
2026-06-30 07:09:44 +00:00
now := time.Now().UnixNano()
if cred.AccountID != "" {
_, _ = l.svcCtx.PublishQueue.RecordPublished(l.ctx, publishqueuedomain.RecordPublishedRequest{
TenantID: tenantID,
OwnerUID: uid,
AccountID: cred.AccountID,
Text: text,
2026-07-02 14:52:41 +00:00
TopicTag: topicTag,
2026-06-30 07:09:44 +00:00
MediaID: result.MediaID,
Permalink: result.Permalink,
PublishedAt: now,
})
}
2026-06-26 08:37:04 +00:00
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
}