bindbox-game/docs/优化抖音定时任务/DESIGN_优化抖音定时任务.md
2026-02-18 23:23:34 +08:00

252 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DESIGN - 优化抖音定时任务
## 整体架构图
```mermaid
graph TB
subgraph "定时任务层"
T1[5分钟定时器<br/>直播奖品发放]
T2[1小时定时器<br/>全量订单同步]
T3[2小时定时器<br/>退款状态同步]
end
subgraph "服务层"
DS[DouyinService]
DS --> |GrantLivestreamPrizes| DB[(MySQL)]
DS --> |SyncAllOrders| API[抖音API<br/>代理IP]
DS --> |SyncRefundStatus| API
end
subgraph "接口层"
A1[管理后台<br/>手动同步]
A2[前端按需<br/>5秒限流]
end
T1 --> DS
T2 --> DS
T3 --> DS
A1 --> DS
A2 --> DS
style T1 fill:#90EE90
style T2 fill:#FFB6C1
style T3 fill:#FFB6C1
style API fill:#FF6B6B
```
## 核心组件设计
### 1. 定时任务调度器 (scheduler.go)
**修改前**:
```go
// 单一定时器,每 5 分钟执行所有任务
for {
FetchAndSyncOrders() // 冗余
SyncAllOrders()
GrantLivestreamPrizes()
SyncRefundStatus()
time.Sleep(5 * time.Minute)
}
```
**修改后**:
```go
// 多定时器,分频执行
ticker5min := time.NewTicker(5 * time.Minute)
ticker1h := time.NewTicker(1 * time.Hour)
ticker2h := time.NewTicker(2 * time.Hour)
for {
select {
case <-ticker5min.C:
GrantLivestreamPrizes() // 不调用API
case <-ticker1h.C:
SyncAllOrders() // 调用API
case <-ticker2h.C:
SyncRefundStatus() // 调用API
}
}
```
### 2. 管理后台接口 (douyin_admin.go)
**新增文件结构**:
```go
package admin
type douyinHandler struct {
logger logger.CustomLogger
douyin douyinsvc.Service
}
// 手动全量同步
func (h *douyinHandler) ManualSyncAll() core.HandlerFunc
// 手动退款同步
func (h *douyinHandler) ManualSyncRefund() core.HandlerFunc
// 手动发放奖品
func (h *douyinHandler) ManualGrantPrizes() core.HandlerFunc
```
### 3. 路由注册 (router.go)
**新增路由**:
```go
adminGroup := r.Group("/api/admin")
adminGroup.Use(middleware.AdminAuth())
{
douyin := adminGroup.Group("/douyin")
{
douyin.POST("/sync-all", douyinHandler.ManualSyncAll())
douyin.POST("/sync-refund", douyinHandler.ManualSyncRefund())
douyin.POST("/grant-prizes", douyinHandler.ManualGrantPrizes())
}
}
```
## 模块依赖关系图
```mermaid
graph LR
A[scheduler.go] --> B[douyin.Service]
C[douyin_admin.go] --> B
D[livestream_public.go] --> B
B --> E[order_sync.go]
B --> F[reward_dispatcher.go]
E --> G[MySQL]
E --> H[抖音API]
F --> G
```
## 接口契约定义
### 管理后台接口
#### 1. 手动全量同步
```
POST /api/admin/douyin/sync-all
Headers: Authorization: Bearer {admin_token}
Body: {
"duration_hours": 1 // 可选,默认1小时
}
Response: {
"total_fetched": 100,
"new_orders": 5,
"matched_users": 3
}
```
#### 2. 手动退款同步
```
POST /api/admin/douyin/sync-refund
Headers: Authorization: Bearer {admin_token}
Response: {
"refunded_count": 2
}
```
#### 3. 手动发放奖品
```
POST /api/admin/douyin/grant-prizes
Headers: Authorization: Bearer {admin_token}
Response: {
"granted_count": 5
}
```
## 数据流向图
```mermaid
sequenceDiagram
participant T as 定时器
participant S as DouyinService
participant A as 抖音API
participant D as MySQL
Note over T: 每1小时触发
T->>S: SyncAllOrders(1h)
S->>A: GET /api/order/searchlist
A-->>S: 订单列表
S->>D: 批量更新订单
S->>D: 自动发放奖励
Note over T: 每5分钟触发
T->>S: GrantLivestreamPrizes()
S->>D: 查询未发放记录
S->>D: 创建订单+发货
```
## 异常处理策略
### 1. API 调用失败
```go
// 重试机制 (已有)
for i := 0; i < 3; i++ {
resp, err := client.Do(req)
if err == nil {
break
}
time.Sleep(1 * time.Second)
}
```
### 2. 定时器异常
```go
// 使用 defer + recover 防止 panic
defer func() {
if r := recover(); r != nil {
logger.Error("定时任务异常", zap.Any("panic", r))
}
}()
```
### 3. 并发控制
```go
// 使用 singleflight 防止重复执行 (已有)
v, err, _ := s.sfGroup.Do("SyncAllOrders", func() {...})
```
## 性能优化
### API 调用频率对比
```
优化前: 每 5 分钟 × 4 个任务 = 12 次/小时
优化后: 每 1 小时 × 1 次 + 每 2 小时 × 1 次 = 1.5 次/小时
降低: 87.5%
```
### 响应时间预估
```
定时任务: 1-60 秒 (取决于代理IP速度)
手动触发: 1-60 秒 (同上)
前端按需: <5 秒 (限流跳过) 或 1-60 秒
```
## 质量保证
### 单元测试
- 测试定时器触发逻辑
- 测试管理接口权限控制
- 测试 Service 方法调用
### 集成测试
- 验证定时任务正常执行
- 验证手动触发接口可用
- 验证前端按需同步不受影响
## 回滚方案
如果出现问题,可快速回滚:
```go
// 恢复单一定时器
for {
svc.SyncAllOrders(ctx, 1*time.Hour)
svc.GrantLivestreamPrizes(ctx)
svc.SyncRefundStatus(ctx)
time.Sleep(5 * time.Minute)
}
```