2026-06-24 10:02:42 +00:00
|
|
|
package usecase
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"haixun-backend/internal/library/clock"
|
|
|
|
|
libmongo "haixun-backend/internal/library/mongo"
|
|
|
|
|
"haixun-backend/internal/model/outreach_draft/domain/entity"
|
|
|
|
|
domrepo "haixun-backend/internal/model/outreach_draft/domain/repository"
|
|
|
|
|
domusecase "haixun-backend/internal/model/outreach_draft/domain/usecase"
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type outreachDraftUseCase struct {
|
|
|
|
|
repo domrepo.Repository
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewUseCase(repo domrepo.Repository) domusecase.UseCase {
|
|
|
|
|
return &outreachDraftUseCase{repo: repo}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *outreachDraftUseCase) Create(ctx context.Context, req domusecase.CreateRequest) (*domusecase.DraftSummary, error) {
|
|
|
|
|
if err := requireActor(req.TenantID, req.OwnerUID, req.BrandID); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
scanPostID := strings.TrimSpace(req.ScanPostID)
|
|
|
|
|
if scanPostID == "" {
|
|
|
|
|
return nil, errMissingScanPost()
|
|
|
|
|
}
|
|
|
|
|
now := clock.NowUnixNano()
|
|
|
|
|
drafts := make([]entity.DraftItem, 0, len(req.Drafts))
|
|
|
|
|
for _, item := range req.Drafts {
|
|
|
|
|
drafts = append(drafts, entity.DraftItem{
|
|
|
|
|
Text: item.Text,
|
|
|
|
|
Angle: item.Angle,
|
|
|
|
|
Rationale: item.Rationale,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
record := &entity.OutreachDraft{
|
|
|
|
|
ID: uuid.NewString(),
|
|
|
|
|
TenantID: req.TenantID,
|
|
|
|
|
OwnerUID: req.OwnerUID,
|
|
|
|
|
BrandID: req.BrandID,
|
2026-06-24 17:30:47 +00:00
|
|
|
TopicID: strings.TrimSpace(req.TopicID),
|
2026-06-24 10:02:42 +00:00
|
|
|
ScanPostID: scanPostID,
|
|
|
|
|
Relevance: req.Relevance,
|
|
|
|
|
Reason: req.Reason,
|
|
|
|
|
Drafts: drafts,
|
|
|
|
|
CreateAt: now,
|
|
|
|
|
}
|
|
|
|
|
if err := u.repo.Create(ctx, record); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return toSummary(record), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *outreachDraftUseCase) GetLatestByScanPost(
|
|
|
|
|
ctx context.Context,
|
2026-06-24 17:30:47 +00:00
|
|
|
tenantID, ownerUID, brandID, topicID, scanPostID string,
|
2026-06-24 10:02:42 +00:00
|
|
|
) (*domusecase.DraftSummary, error) {
|
|
|
|
|
if err := requireActor(tenantID, ownerUID, brandID); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
scanPostID = strings.TrimSpace(scanPostID)
|
|
|
|
|
if scanPostID == "" {
|
|
|
|
|
return nil, errMissingScanPost()
|
|
|
|
|
}
|
2026-06-24 17:30:47 +00:00
|
|
|
record, err := u.repo.GetLatestByScanPost(ctx, tenantID, ownerUID, brandID, topicID, scanPostID)
|
2026-06-24 10:02:42 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if record == nil {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
return toSummary(record), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func requireActor(tenantID, ownerUID, brandID string) error {
|
|
|
|
|
if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
|
|
|
|
|
return errMissingActor()
|
|
|
|
|
}
|
|
|
|
|
if strings.TrimSpace(brandID) == "" {
|
|
|
|
|
return errMissingBrand()
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func toSummary(record *entity.OutreachDraft) *domusecase.DraftSummary {
|
|
|
|
|
if record == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
drafts := make([]domusecase.DraftItem, 0, len(record.Drafts))
|
|
|
|
|
for _, item := range record.Drafts {
|
|
|
|
|
drafts = append(drafts, domusecase.DraftItem{
|
|
|
|
|
Text: item.Text,
|
|
|
|
|
Angle: item.Angle,
|
|
|
|
|
Rationale: item.Rationale,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return &domusecase.DraftSummary{
|
|
|
|
|
ID: record.ID,
|
|
|
|
|
BrandID: libmongo.ResolveBrandID(record.BrandID, record.LegacyPersonaID),
|
|
|
|
|
ScanPostID: record.ScanPostID,
|
|
|
|
|
Relevance: record.Relevance,
|
|
|
|
|
Reason: record.Reason,
|
|
|
|
|
Drafts: drafts,
|
|
|
|
|
CreateAt: record.CreateAt,
|
|
|
|
|
}
|
|
|
|
|
}
|