5.0 KiB
Executable File
5.0 KiB
Executable File
DESIGN - 优化抖音定时任务
整体架构图
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)
修改前:
// 单一定时器,每 5 分钟执行所有任务
for {
FetchAndSyncOrders() // 冗余
SyncAllOrders()
GrantLivestreamPrizes()
SyncRefundStatus()
time.Sleep(5 * time.Minute)
}
修改后:
// 多定时器,分频执行
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)
新增文件结构:
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)
新增路由:
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())
}
}
模块依赖关系图
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
}
数据流向图
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 调用失败
// 重试机制 (已有)
for i := 0; i < 3; i++ {
resp, err := client.Do(req)
if err == nil {
break
}
time.Sleep(1 * time.Second)
}
2. 定时器异常
// 使用 defer + recover 防止 panic
defer func() {
if r := recover(); r != nil {
logger.Error("定时任务异常", zap.Any("panic", r))
}
}()
3. 并发控制
// 使用 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 方法调用
集成测试
- 验证定时任务正常执行
- 验证手动触发接口可用
- 验证前端按需同步不受影响
回滚方案
如果出现问题,可快速回滚:
// 恢复单一定时器
for {
svc.SyncAllOrders(ctx, 1*time.Hour)
svc.GrantLivestreamPrizes(ctx)
svc.SyncRefundStatus(ctx)
time.Sleep(5 * time.Minute)
}