252 lines
5.0 KiB
Markdown
252 lines
5.0 KiB
Markdown
# 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)
|
||
}
|
||
```
|