package usecase import ( "context" "strings" "time" "haixun-backend/internal/library/clock" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" libownpost "haixun-backend/internal/library/ownpost" "haixun-backend/internal/model/own_post_formula/domain/entity" domrepo "haixun-backend/internal/model/own_post_formula/domain/repository" ) const FormulaCacheTTL = 5 * time.Minute type UseCase interface { Get(ctx context.Context, tenantID, ownerUID, accountID, mediaID string) (*entity.Review, error) SaveGenerated(ctx context.Context, tenantID, ownerUID, accountID, mediaID, personaID, postText string, review *libownpost.PostFormulaReview) (*entity.Review, error) ListByAccount(ctx context.Context, tenantID, ownerUID, accountID string) ([]entity.Review, error) IsFresh(review *entity.Review, now int64) bool } func (u *useCase) Get(ctx context.Context, tenantID, ownerUID, accountID, mediaID string) (*entity.Review, error) { return u.repo.Get(ctx, tenantID, ownerUID, accountID, mediaID) } func (u *useCase) IsFresh(review *entity.Review, now int64) bool { if review == nil || review.UpdateAt <= 0 { return false } if now <= 0 { now = clock.NowUnixNano() } return now-review.UpdateAt < int64(FormulaCacheTTL) } type useCase struct { repo domrepo.Repository } func NewUseCase(repo domrepo.Repository) UseCase { return &useCase{repo: repo} } func (u *useCase) SaveGenerated( ctx context.Context, tenantID, ownerUID, accountID, mediaID, personaID, postText string, review *libownpost.PostFormulaReview, ) (*entity.Review, error) { if review == nil { return nil, app.For(code.ThreadsAccount).InputMissingRequired("review is required") } accountID = strings.TrimSpace(accountID) mediaID = strings.TrimSpace(mediaID) if accountID == "" || mediaID == "" { return nil, app.For(code.ThreadsAccount).InputMissingRequired("account_id and media_id are required") } now := clock.NowUnixNano() doc := &entity.Review{ ID: entity.ReviewDocID(accountID, mediaID), TenantID: strings.TrimSpace(tenantID), OwnerUID: strings.TrimSpace(ownerUID), AccountID: accountID, MediaID: mediaID, PersonaID: strings.TrimSpace(personaID), PostText: strings.TrimSpace(postText), Summary: review.Summary, Wins: review.Wins, Improvements: review.Improvements, Formula: review.Formula, PostTemplate: review.PostTemplate, HookPattern: review.HookPattern, Structure: review.Structure, ReplicationTips: review.ReplicationTips, Avoid: review.Avoid, CreateAt: now, UpdateAt: now, } if existing, err := u.repo.Get(ctx, tenantID, ownerUID, accountID, mediaID); err == nil && existing != nil { doc.CreateAt = existing.CreateAt } return u.repo.Upsert(ctx, doc) } func (u *useCase) ListByAccount(ctx context.Context, tenantID, ownerUID, accountID string) ([]entity.Review, error) { accountID = strings.TrimSpace(accountID) if accountID == "" { return nil, app.For(code.ThreadsAccount).InputMissingRequired("account_id is required") } return u.repo.ListByAccount(ctx, tenantID, ownerUID, accountID) }