thread-master/backend/internal/model/content_formula/usecase/usecase.go

185 lines
5.8 KiB
Go
Raw Normal View History

2026-07-01 08:42:51 +00:00
package usecase
import (
"context"
"strings"
"haixun-backend/internal/library/clock"
app "haixun-backend/internal/library/errors"
"haixun-backend/internal/library/errors/code"
"haixun-backend/internal/model/content_formula/domain/entity"
domrepo "haixun-backend/internal/model/content_formula/domain/repository"
domusecase "haixun-backend/internal/model/content_formula/domain/usecase"
"github.com/google/uuid"
)
type useCase struct {
repo domrepo.Repository
}
func NewUseCase(repo domrepo.Repository) domusecase.UseCase {
return &useCase{repo: repo}
}
func (u *useCase) Create(ctx context.Context, req domusecase.CreateRequest) (*domusecase.FormulaSummary, error) {
if err := requireActor(req.TenantID, req.OwnerUID, req.AccountID); err != nil {
return nil, err
}
label := strings.TrimSpace(req.Label)
if label == "" {
label = "未命名公式"
}
sourceType := strings.TrimSpace(req.SourceType)
if sourceType == "" {
sourceType = entity.SourcePaste
}
formulaText := strings.TrimSpace(req.Formula)
if formulaText == "" && strings.TrimSpace(req.PostTemplate) == "" {
return nil, app.For(code.ThreadsAccount).InputMissingRequired("formula or post_template is required")
}
now := clock.NowUnixNano()
item := &entity.ContentFormula{
ID: uuid.NewString(),
TenantID: req.TenantID,
OwnerUID: req.OwnerUID,
AccountID: req.AccountID,
Label: label,
SourceType: sourceType,
SourceRef: strings.TrimSpace(req.SourceRef),
SourcePostText: strings.TrimSpace(req.SourcePostText),
SourceAuthor: strings.TrimSpace(req.SourceAuthor),
SourcePermalink: strings.TrimSpace(req.SourcePermalink),
SourceMetrics: req.SourceMetrics,
Summary: strings.TrimSpace(req.Summary),
Wins: req.Wins,
Improvements: req.Improvements,
Formula: formulaText,
PostTemplate: strings.TrimSpace(req.PostTemplate),
HookPattern: strings.TrimSpace(req.HookPattern),
Structure: strings.TrimSpace(req.Structure),
ReplicationTips: req.ReplicationTips,
Avoid: req.Avoid,
Tags: req.Tags,
CreateAt: now,
UpdateAt: now,
}
if err := u.repo.Create(ctx, item); err != nil {
return nil, err
}
summary := toSummary(*item)
return &summary, nil
}
func (u *useCase) Get(ctx context.Context, tenantID, ownerUID, accountID, formulaID string) (*domusecase.FormulaSummary, error) {
if err := requireActor(tenantID, ownerUID, accountID); err != nil {
return nil, err
}
item, err := u.repo.Get(ctx, tenantID, ownerUID, accountID, formulaID)
if err != nil {
return nil, err
}
summary := toSummary(*item)
return &summary, nil
}
func (u *useCase) Update(ctx context.Context, req domusecase.UpdateRequest) (*domusecase.FormulaSummary, error) {
if err := requireActor(req.TenantID, req.OwnerUID, req.AccountID); err != nil {
return nil, err
}
patch := map[string]interface{}{"update_at": clock.NowUnixNano()}
if req.Label != nil {
patch["label"] = strings.TrimSpace(*req.Label)
}
if req.Formula != nil {
patch["formula"] = strings.TrimSpace(*req.Formula)
}
if req.PostTemplate != nil {
patch["post_template"] = strings.TrimSpace(*req.PostTemplate)
}
if req.HookPattern != nil {
patch["hook_pattern"] = strings.TrimSpace(*req.HookPattern)
}
if req.Structure != nil {
patch["structure"] = strings.TrimSpace(*req.Structure)
}
if req.Tags != nil {
patch["tags"] = req.Tags
}
if len(patch) == 1 {
return nil, app.For(code.ThreadsAccount).InputMissingRequired("no fields to update")
}
item, err := u.repo.Update(ctx, req.TenantID, req.OwnerUID, req.AccountID, req.FormulaID, patch)
if err != nil {
return nil, err
}
summary := toSummary(*item)
return &summary, nil
}
func (u *useCase) Delete(ctx context.Context, tenantID, ownerUID, accountID, formulaID string) error {
if err := requireActor(tenantID, ownerUID, accountID); err != nil {
return err
}
return u.repo.Delete(ctx, tenantID, ownerUID, accountID, formulaID)
}
func (u *useCase) List(ctx context.Context, req domusecase.ListRequest) (*domusecase.ListResult, error) {
if err := requireActor(req.TenantID, req.OwnerUID, req.AccountID); err != nil {
return nil, err
}
result, err := u.repo.List(ctx, domrepo.ListFilter{
TenantID: req.TenantID,
OwnerUID: req.OwnerUID,
AccountID: req.AccountID,
SourceType: req.SourceType,
Tag: req.Tag,
Page: req.Page,
PageSize: req.PageSize,
})
if err != nil {
return nil, err
}
out := make([]domusecase.FormulaSummary, 0, len(result.Items))
for _, item := range result.Items {
out = append(out, toSummary(item))
}
return &domusecase.ListResult{Items: out, Total: result.Total}, nil
}
func requireActor(tenantID, ownerUID, accountID string) error {
if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
return app.For(code.Auth).AuthUnauthorized("missing actor")
}
if strings.TrimSpace(accountID) == "" {
return app.For(code.ThreadsAccount).InputMissingRequired("account_id is required")
}
return nil
}
func toSummary(item entity.ContentFormula) domusecase.FormulaSummary {
return domusecase.FormulaSummary{
ID: item.ID,
AccountID: item.AccountID,
Label: item.Label,
SourceType: item.SourceType,
SourceRef: item.SourceRef,
SourcePostText: item.SourcePostText,
SourceAuthor: item.SourceAuthor,
SourcePermalink: item.SourcePermalink,
SourceMetrics: item.SourceMetrics,
Summary: item.Summary,
Wins: item.Wins,
Improvements: item.Improvements,
Formula: item.Formula,
PostTemplate: item.PostTemplate,
HookPattern: item.HookPattern,
Structure: item.Structure,
ReplicationTips: item.ReplicationTips,
Avoid: item.Avoid,
Tags: item.Tags,
CreateAt: item.CreateAt,
UpdateAt: item.UpdateAt,
}
2026-07-03 14:42:19 +00:00
}