package image import ( "sync" "github.com/golang/freetype/truetype" "douyu_go/internal/logger" "douyu_go/internal/types" ) const ( defaultSuccessImageWorkers = 1 defaultSuccessImageQueue = 256 ) type successImageJob struct { Account types.Account Product types.Product RoleName string } // SuccessImageSaver 用后台队列生成成功图片,避免图片编码和磁盘写入占用抢购链路。 type SuccessImageSaver struct { font *truetype.Font successDir string ch chan successImageJob wg sync.WaitGroup mu sync.Mutex closed bool } // NewSuccessImageSaver 创建成功图片后台保存器。 func NewSuccessImageSaver(font *truetype.Font, successDir string, workers, queueSize int) *SuccessImageSaver { if font == nil { return nil } if workers <= 0 { workers = defaultSuccessImageWorkers } if queueSize <= 0 { queueSize = defaultSuccessImageQueue } saver := &SuccessImageSaver{ font: font, successDir: successDir, ch: make(chan successImageJob, queueSize), } for i := 0; i < workers; i++ { saver.wg.Add(1) go saver.run() } return saver } // Enqueue 投递成功图片任务;队列满时丢弃图片任务,优先保护抢购请求。 func (s *SuccessImageSaver) Enqueue(account types.Account, product types.Product, roleName string) { if s == nil { return } job := successImageJob{ Account: account, Product: product, RoleName: roleName, } s.mu.Lock() defer s.mu.Unlock() if s.closed { return } select { case s.ch <- job: default: logger.LogEvent("图片", "warn", "成功图片队列已满,已跳过: 账号=%s 商品=%s", account.Name, product.CommodityID) } } func (s *SuccessImageSaver) run() { defer s.wg.Done() for job := range s.ch { SaveSuccessImage(job.Account, job.Product, job.RoleName, s.font, s.successDir) } } // Close 关闭后台队列并等待已投递图片保存完成。 func (s *SuccessImageSaver) Close() { if s == nil { return } s.mu.Lock() if s.closed { s.mu.Unlock() return } s.closed = true close(s.ch) s.mu.Unlock() s.wg.Wait() }