thread-master/backend/docs/phase1-methods.md

177 lines
5.4 KiB
Markdown
Raw Normal View History

2026-06-27 16:03:28 +00:00
# 全 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` | 語意相似度分數0100 |
| `EngagementPredicted` | `int` | 預測互動率 |
| `AudienceQualityScore` | `int` | 受眾品質分數0100 |
| `AuthorID` | `string` | 作者 IDThreads user ID |
| `AuthorAvatar` | `string` | 作者頭像 URL |
| `AuthorFollowers` | `int` | 作者粉絲數 |
`ScanCandidate` struct 同步新增對應欄位(`internal/library/placement/dual_track.go`)。
### 2. Embedding Client
```go
// 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
```go
// internal/library/placement/dual_track.go
func computePlacementScore(post *ScanCandidate, member productContext, brand productMeta,
semanticScore, predictedEngagement, audienceQuality *int) int
```
整合三項 pointer 參數nil 表示欄位不存在,跳過該項):
- **語意相似度**`SemanticScore`Embedding cosine → 0100
- **預測互動率**`EngagementPredicted`like+reply → 對數正規化 0100
- **受眾品質**`AudienceQualityScore``computeAudienceQuality()` heuristic
權重:語意 20% / 互動 30% / 品質 10% / 舊有 productFit + solvedBy + recency 40%。
```go
func computeAudienceQuality(followers int) int
```
Heuristicfollowers 在 50050k 之間品質最佳,過低或過高遞減。
### 4. Creator Lookalike
**端點**: `POST /api/v1/scan-post/lookalike`(需 JWT
```json
{ "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`
**方法**:
```go
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`:
```go
RefreshAccessToken(ctx, currentToken) (*TokenRefreshResult, error)
// GET /v1.0/refresh_access_token?grant_type=th_refresh_token
```
**儲存 Token**:
```go
// 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
```go
// 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非 Redisthread-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` 中註冊:
```go
jobworker.RegisterRefreshThreadsTokenHandler(runner, RefreshThreadsTokenDeps{...})
jobworker.RegisterPublishAnalyticsHandler(runner, PublishAnalyticsDeps{...})
```
### Worker Helper
```go
// internal/worker/job/expand_graph.go
func int64Field(payload map[string]any, key string) int64
```
從 job payload 讀 `int64`,支援 `float64` / `int64` / `int` / `json.Number`