# DESIGN - 优化抖音定时任务 ## 整体架构图 ```mermaid graph TB subgraph "定时任务层" T1[5分钟定时器
直播奖品发放] T2[1小时定时器
全量订单同步] T3[2小时定时器
退款状态同步] end subgraph "服务层" DS[DouyinService] DS --> |GrantLivestreamPrizes| DB[(MySQL)] DS --> |SyncAllOrders| API[抖音API
代理IP] DS --> |SyncRefundStatus| API end subgraph "接口层" A1[管理后台
手动同步] A2[前端按需
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) } ```