simCardTaskQueue.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. package simCardQueue
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "fwh-toolbox-server/config"
  7. "fwh-toolbox-server/domain"
  8. "fwh-toolbox-server/repositories"
  9. task_core "fwh-toolbox-server/task/core"
  10. task_pg "fwh-toolbox-server/task/plugin"
  11. "sync"
  12. "time"
  13. "github.com/google/uuid"
  14. "github.com/hibiken/asynq"
  15. "go.uber.org/zap"
  16. )
  17. /***********************************************************************
  18. * 为了控制流量采用单线程队列查询
  19. * SyncOnePage Task * 1 查询单页数据,并创建数据聚合任务
  20. * |
  21. * ▼
  22. * SyncOnePage Task * 2 查询单页数据,并创建数据聚合任务
  23. * ...
  24. * ▼
  25. * SyncOnePage Task * N 查询单页数据,并创建数据聚合任务
  26. * |
  27. * ▼
  28. * Aggregation (Group Aggregator) 聚合数据并创建,批量数据处理任务
  29. * |
  30. * ▼
  31. * Bulk Task 将聚合的数据批量写入数据库
  32. ***********************************************************************/
  33. const (
  34. TaskTypeSimCardAggregation = "SimCard:Aggregation"
  35. GroupNameSimCardAggregation = "SimCard:GroupAggregation"
  36. TaskTypeSimCardBulk = "SimCard:Bulk"
  37. )
  38. type TaskCore interface {
  39. CreateTask() *asynq.Client
  40. }
  41. type SimCardSyncOnePagePayload struct {
  42. Size uint64 `json:"size"`
  43. Page uint64 `json:"page"`
  44. Date string `json:"date,omitempty"` // UTC collection date
  45. }
  46. type simCardPageBatch struct {
  47. Date string
  48. Pages []domain.SimCardRunPage
  49. Info []domain.SimCardInfo
  50. }
  51. type SimCardProcessor struct {
  52. cacheMu sync.Mutex
  53. infoCacheMap map[string][]simCardPageBatch
  54. webhook string
  55. secret string
  56. }
  57. func (processor *SimCardProcessor) putBatches(key string, batches []simCardPageBatch) {
  58. processor.cacheMu.Lock()
  59. defer processor.cacheMu.Unlock()
  60. processor.infoCacheMap[key] = batches
  61. }
  62. func (processor *SimCardProcessor) getBatches(key string) ([]simCardPageBatch, bool) {
  63. processor.cacheMu.Lock()
  64. defer processor.cacheMu.Unlock()
  65. batches, ok := processor.infoCacheMap[key]
  66. return batches, ok
  67. }
  68. func (processor *SimCardProcessor) deleteBatches(key string) {
  69. processor.cacheMu.Lock()
  70. defer processor.cacheMu.Unlock()
  71. delete(processor.infoCacheMap, key)
  72. }
  73. var TaskClient *asynq.Client
  74. func InitSimCardTaskQueue(tc *task_core.TaskCore, appConfig *config.AppConfig) {
  75. processor := &SimCardProcessor{
  76. infoCacheMap: make(map[string][]simCardPageBatch),
  77. webhook: appConfig.SimCardDingTalkWebhook,
  78. secret: appConfig.SimCardDingTalkSecret,
  79. }
  80. // 注册SmartHub收集任务处理函数
  81. mux := asynq.NewServeMux()
  82. mux.HandleFunc(TaskTypeSimCardSmartHubSyncOnePage, processor.handleSimCardSmartHubSyncOnePageTask)
  83. // 注册Acceleronix收集任务处理函数
  84. mux.HandleFunc(TaskTypeSimCardAcceleronixSyncOnePage, processor.handleSimCardAcceleronixSyncOnePageTask)
  85. // 注册SimCard批处理任务
  86. mux.HandleFunc(TaskTypeSimCardBulk, processor.handleSimCardBulkTask)
  87. mux.HandleFunc(taskTypeSimCardDailyReport, processor.handleSimCardDailyReport)
  88. // 注册SimCard组聚合器
  89. aggManager := task_pg.NewAggregatorManager()
  90. err := aggManager.Register(GroupNameSimCardAggregation, func(tasks []*asynq.Task) *asynq.Task {
  91. var batches []simCardPageBatch
  92. for _, t := range tasks {
  93. payloadStr := t.Payload()
  94. pageBatches, ok := processor.getBatches(string(payloadStr))
  95. if !ok {
  96. zap.L().Error("SimCard aggregation payload missing", zap.String("key", string(payloadStr)))
  97. continue
  98. }
  99. defer processor.deleteBatches(string(payloadStr))
  100. batches = append(batches, pageBatches...)
  101. }
  102. uuidStr := uuid.NewString()
  103. processor.putBatches(uuidStr, batches)
  104. zap.L().Debug("SimCardBulk get batches.", zap.Int("len", len(batches)))
  105. bulkTask := asynq.NewTask(
  106. TaskTypeSimCardBulk,
  107. []byte(uuidStr),
  108. asynq.MaxRetry(5),
  109. asynq.Queue("default"),
  110. asynq.Timeout(time.Minute),
  111. asynq.Unique(5*time.Minute),
  112. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  113. )
  114. return bulkTask
  115. })
  116. if err != nil {
  117. zap.L().Error("GroupNameSimCardAggregation register failed.", zap.String("err", err.Error()))
  118. }
  119. TaskClient = tc.CreateTask(task_core.TaskConfig{
  120. Name: "SimCardCollectTask",
  121. SvrCfg: asynq.Config{
  122. // Specify how many concurrent workers to use
  123. Concurrency: 1, // 串行任务,只配置1个worker
  124. // Optionally specify multiple queues with different priority.
  125. Queues: map[string]int{
  126. "critical": 6,
  127. "default": 3,
  128. "low": 1,
  129. },
  130. // See the godoc for other configuration options
  131. GroupAggregator: asynq.GroupAggregatorFunc(aggManager.Aggregate),
  132. GroupGracePeriod: 30 * time.Second, // 宽限期,30秒没人提交开始聚合
  133. GroupMaxDelay: 5 * time.Minute, // 最长等待时间,5分钟必须聚合
  134. GroupMaxSize: 100, // 最大数量,数据总量到达100必须聚合
  135. },
  136. Mux: mux,
  137. Agg: aggManager,
  138. })
  139. err = CreateSchedulerSimCardCollect(tc)
  140. if err != nil {
  141. zap.L().Error("CreateSchedulerSimCardCollect failed.", zap.String("err", err.Error()))
  142. }
  143. }
  144. func CreateSchedulerSimCardCollect(tc *task_core.TaskCore) error {
  145. payload := SimCardSyncOnePagePayload{
  146. Size: SmartHubSimCardInfoPageSize,
  147. Page: 0, // SmartHub从第0页开始
  148. }
  149. payloadStr, err := json.Marshal(payload)
  150. if err != nil {
  151. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  152. return err
  153. }
  154. // 创建SmartHub收集任务
  155. taskSmartHub := asynq.NewTask(
  156. TaskTypeSimCardSmartHubSyncOnePage,
  157. payloadStr,
  158. asynq.MaxRetry(5),
  159. asynq.Queue("default"),
  160. asynq.Timeout(time.Minute),
  161. asynq.Unique(5*time.Minute),
  162. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  163. )
  164. err = tc.Scheduler.RegisterUtcScheduler("SmartHubSyncOnePageScheduler", "0 23 * * *", taskSmartHub)
  165. if err != nil {
  166. zap.L().Error("TaskTypeSimCardSmartHubSyncOnePage RegisterUtcScheduler failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  167. return err
  168. }
  169. payload.Page = 1 // Acceleronix从第1页开始
  170. payloadStr, err = json.Marshal(payload)
  171. if err != nil {
  172. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  173. return err
  174. }
  175. // 创建Acceleronix收集任务
  176. taskAcceleronix := asynq.NewTask(
  177. TaskTypeSimCardAcceleronixSyncOnePage,
  178. payloadStr,
  179. asynq.MaxRetry(5),
  180. asynq.Queue("default"),
  181. asynq.Timeout(30*time.Minute),
  182. asynq.Unique(5*time.Minute),
  183. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  184. )
  185. err = tc.Scheduler.RegisterUtcScheduler("AcceleronixSyncOnePageScheduler", "0 23 * * *", taskAcceleronix)
  186. if err != nil {
  187. zap.L().Error("TaskTypeSimCardAcceleronixSyncOnePage RegisterUtcScheduler failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  188. return err
  189. }
  190. return nil
  191. }
  192. // 创建SimCard信息收集任务
  193. func CreateTaskSimCardCollect() error {
  194. if TaskClient == nil {
  195. return fmt.Errorf("*asynq.Client Uninitialized.")
  196. }
  197. payload := SimCardSyncOnePagePayload{
  198. Size: SmartHubSimCardInfoPageSize,
  199. Page: 0, // SmartHub从第0页开始
  200. }
  201. payloadStr, err := json.Marshal(payload)
  202. if err != nil {
  203. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  204. return err
  205. }
  206. // 创建SmartHub收集任务
  207. taskSmartHub := asynq.NewTask(
  208. TaskTypeSimCardSmartHubSyncOnePage,
  209. payloadStr,
  210. asynq.MaxRetry(5),
  211. asynq.Queue("default"),
  212. asynq.Timeout(time.Minute),
  213. asynq.Unique(5*time.Minute),
  214. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  215. )
  216. _, err = TaskClient.Enqueue(taskSmartHub)
  217. if err != nil {
  218. zap.L().Error("TaskTypeSimCardSmartHubSyncOnePage Enqueue failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  219. return err
  220. }
  221. payload.Page = 1 // Acceleronix从第1页开始
  222. payloadStr, err = json.Marshal(payload)
  223. if err != nil {
  224. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  225. return err
  226. }
  227. // 创建Acceleronix收集任务
  228. taskAcceleronix := asynq.NewTask(
  229. TaskTypeSimCardAcceleronixSyncOnePage,
  230. payloadStr,
  231. asynq.MaxRetry(5),
  232. asynq.Queue("default"),
  233. asynq.Timeout(30*time.Minute),
  234. asynq.Unique(5*time.Minute),
  235. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  236. )
  237. _, err = TaskClient.Enqueue(taskAcceleronix)
  238. if err != nil {
  239. zap.L().Error("TaskTypeSimCardAcceleronixSyncOnePage Enqueue failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  240. return err
  241. }
  242. return nil
  243. }
  244. // 保存SimCard信息到数据库
  245. func (processor *SimCardProcessor) saveSimCardInfo(infoList []domain.SimCardInfo, date time.Time) error {
  246. zap.L().Info("saveSimCardInfo", zap.Int("Num", len(infoList)))
  247. // 保存基本信息
  248. _, err := repositories.UpdateSimCardBatchInfo(infoList)
  249. if err != nil {
  250. zap.L().Error("UpdateSimCardInfo failed.", zap.String("err", err.Error()), zap.Int("infoNum", len(infoList)))
  251. return err
  252. }
  253. // 提取日数据
  254. dayDataList := []domain.SimCardDayData{}
  255. for _, info := range infoList {
  256. dayData := domain.SimCardDayData{
  257. SimId: info.GICCID, // GICCID
  258. UpdateTime: info.UpdateTime, // UTC时间
  259. Month: info.Month, // 统计月份 格式:yyyy-MM
  260. AllDataUsage: info.AllDataUsage, // 月数据用量(KB)
  261. Day: uint8(date.Day()), // 当天日期值 1-31
  262. DailyUsage: domain.SimCardDailyUsage{
  263. UpdateTime: info.UpdateTime, // UTC时间
  264. Day: date.Format(time.DateOnly), // 统计日期 格式:yyyy-MM-DD
  265. DataUsage: info.AllDataUsage, // 数据用量(KB)
  266. }, // 日用量数据信息
  267. }
  268. dayDataList = append(dayDataList, dayData)
  269. }
  270. // 保存月数据信息
  271. _, err = repositories.UpdateSimCardBatchDayData(dayDataList)
  272. if err != nil {
  273. zap.L().Error("UpdateSimCardBatchMonthlyData failed.", zap.String("err", err.Error()), zap.Int("infoNum", len(infoList)))
  274. return err
  275. }
  276. return nil
  277. }
  278. // 处理SimCard批处理任务
  279. func (processor *SimCardProcessor) handleSimCardBulkTask(ctx context.Context, task *asynq.Task) error {
  280. payloadStr := task.Payload()
  281. batches, ok := processor.getBatches(string(payloadStr))
  282. if !ok {
  283. return fmt.Errorf("SIM card bulk payload missing: %s", string(payloadStr))
  284. }
  285. byDate := make(map[string]*simCardPageBatch)
  286. for _, batch := range batches {
  287. combined := byDate[batch.Date]
  288. if combined == nil {
  289. combined = &simCardPageBatch{Date: batch.Date}
  290. byDate[batch.Date] = combined
  291. }
  292. combined.Pages = append(combined.Pages, batch.Pages...)
  293. combined.Info = append(combined.Info, batch.Info...)
  294. }
  295. for dateStr, batch := range byDate {
  296. date, err := time.Parse(time.DateOnly, dateStr)
  297. if err != nil {
  298. return err
  299. }
  300. if err := processor.saveSimCardInfo(batch.Info, date); err != nil {
  301. return err
  302. }
  303. if err := repositories.MarkSimCardPagesSaved(dateStr, batch.Pages); err != nil {
  304. return err
  305. }
  306. if err := processor.enqueueSimCardReportIfComplete(dateStr); err != nil {
  307. return err
  308. }
  309. }
  310. processor.deleteBatches(string(payloadStr))
  311. return nil
  312. }