5.4 KiB
全 Phase 新增功能說明
Phase 1 — TA 精準度
1. Semantic Embedding Reranker
ScanPost entity 新增欄位(internal/model/scan_post/domain/entity/post.go):
| 欄位 | 型別 | 用途 |
|---|---|---|
Embedding |
[]float32 |
OpenAI text-embedding-3-small 向量 |
SemanticScore |
int |
語意相似度分數(0–100) |
EngagementPredicted |
int |
預測互動率 |
AudienceQualityScore |
int |
受眾品質分數(0–100) |
AuthorID |
string |
作者 ID(Threads user ID) |
AuthorAvatar |
string |
作者頭像 URL |
AuthorFollowers |
int |
作者粉絲數 |
ScanCandidate struct 同步新增對應欄位(internal/library/placement/dual_track.go)。
2. Embedding Client
// internal/library/embedding/client.go
func NewClient(apiKey, model string) *Client
func (c *Client) Generate(ctx context.Context, text string) ([]float32, error)
- 預設使用 OpenAI
text-embedding-3-small(可降級 local BGE-M3) - Scorer:
CosineSimilarity(a, b []float32) float64
3. Placement Score V2
// internal/library/placement/dual_track.go
func computePlacementScore(post *ScanCandidate, member productContext, brand productMeta,
semanticScore, predictedEngagement, audienceQuality *int) int
整合三項 pointer 參數(nil 表示欄位不存在,跳過該項):
- 語意相似度(
SemanticScore):Embedding cosine → 0–100 - 預測互動率(
EngagementPredicted):like+reply → 對數正規化 0–100 - 受眾品質(
AudienceQualityScore):computeAudienceQuality()heuristic
權重:語意 20% / 互動 30% / 品質 10% / 舊有 productFit + solvedBy + recency 40%。
func computeAudienceQuality(followers int) int
Heuristic:followers 在 500–50k 之間品質最佳,過低或過高遞減。
4. Creator Lookalike
端點: POST /api/v1/scan-post/lookalike(需 JWT)
{ "source_post_id": "uuid", "brand_id": "...", "limit": 10 }
→ { "list": [{ "post_id", "author_id", "author_name", "similarity", ... }] }
演算法: cosine similarity(門檻 ≥0.3),同 brand 內找 embedding 最接近的貼文回傳。
| 檔案 | |
|---|---|
| Handler | internal/handler/scan_post/lookalike_handler.go |
| Logic | internal/logic/scan_post/lookalike_logic.go |
cosineSimilarity |
internal/logic/scan_post/lookalike_logic.go — 全 Go 實作 |
Phase 2 — 營運流程
2.1 Creator CRM
完整四層 model crm_contact:
internal/model/crm_contact/
domain/entity/entity.go
domain/repository/repository.go
domain/usecase/usecase.go
usecase/usecase.go
repository/mongo.go
端點: /api/v1/crm/contacts(需 JWT)
| Method | Path | 用途 |
|---|---|---|
| GET | / |
List(page/pageSize/brand_id/status/tag) |
| POST | / |
新增聯絡人(author_id + author_name) |
| GET | /:id |
取得單筆 |
| PATCH | /:id |
更新(notes/tags/status) |
| DELETE | /:id |
刪除 |
CRMContact 狀態機: lead → contacted → responded → converted / archived
2.2 Publish Analytics
完整四層 model publish_analytics:
Checkpoint 常數(internal/model/publish_analytics/usecase/usecase.go): +1h、+24h、+7d
方法:
RecordCheckpoint(ctx, tenantID, ownerUID, summary) (*PublishAnalyticsSummary, error)
ListByMedia(ctx, tenantID, ownerUID, mediaID) ([]PublishAnalyticsSummary, error)
Worker handler publish-analytics(step check):
呼叫 Threads API GET /{media-id}?fields=like_count,reply_count,repost_count,quote_count → 存入 publish_analytics。
2.3 Threads API Token 刷新
底層 API(internal/library/threadsapi/media.go):
RefreshAccessToken(ctx, currentToken) (*TokenRefreshResult, error)
// GET /v1.0/refresh_access_token?grant_type=th_refresh_token
儲存 Token:
// Repository
SaveAPIAccessToken(ctx, accountID, accessToken, expiresAt) (*entity.Secrets, error)
// Usecase
SaveAPIAccessToken(ctx, accountID, accessToken, expiresAt) (*SecretsData, error)
Worker handler refresh-threads-token(step refresh):讀取 secrets → refresh → 儲存。
2.4 Rate Limiter
// internal/library/ratelimit/token_bucket.go
func NewTokenBucket(rate float64, burst int) *TokenBucket
func (tb *TokenBucket) Allow() bool
func (tb *TokenBucket) AllowN(n int) bool
- In-memory token bucket(非 Redis),thread-safe
rate= tokens/sec,burst= 最大 burst
Infrastructure
Job Templates
internal/model/job/usecase/usecase.go 新增兩個 template constants + 對應 Ensure* methods + template() functions:
| Template Type | Steps | Timeout | Retry |
|---|---|---|---|
refresh-threads-token |
refresh(go) |
120s | 3 次(30/120/300s) |
publish-analytics |
check(go) |
120s | 2 次(60s) |
Worker Handlers 註冊
internal/svc/service_context.go 中註冊:
jobworker.RegisterRefreshThreadsTokenHandler(runner, RefreshThreadsTokenDeps{...})
jobworker.RegisterPublishAnalyticsHandler(runner, PublishAnalyticsDeps{...})
Worker Helper
// internal/worker/job/expand_graph.go
func int64Field(payload map[string]any, key string) int64
從 job payload 讀 int64,支援 float64 / int64 / int / json.Number。