package service import ( "context" "sync" "gen2d/internal/logger" ) // TaskJob 队列中的任务。 type TaskJob struct { Ctx context.Context ProjectID string TaskID string Execute func(ctx context.Context) error } // TaskQueue 串行 FIFO 任务队列。 type TaskQueue struct { mu sync.Mutex jobs []*TaskJob ready chan struct{} stop chan struct{} stopped bool } // NewTaskQueue 创建任务队列并启动调度协程。 func NewTaskQueue() *TaskQueue { q := &TaskQueue{ jobs: make([]*TaskJob, 0), ready: make(chan struct{}, 1), stop: make(chan struct{}), } go q.run() return q } // Enqueue 将任务加入队尾,返回队列中的位置(1-based)。 func (q *TaskQueue) Enqueue(job *TaskJob) int { q.mu.Lock() defer q.mu.Unlock() q.jobs = append(q.jobs, job) pos := len(q.jobs) select { case q.ready <- struct{}{}: default: } return pos } // QueueLen 返回当前队列长度。 func (q *TaskQueue) QueueLen() int { q.mu.Lock() defer q.mu.Unlock() return len(q.jobs) } // Stop 优雅关闭队列。 func (q *TaskQueue) Stop() { q.mu.Lock() defer q.mu.Unlock() if !q.stopped { q.stopped = true close(q.stop) } } func (q *TaskQueue) run() { for { select { case <-q.stop: return case <-q.ready: q.processNext() } } } func (q *TaskQueue) processNext() { q.mu.Lock() if len(q.jobs) == 0 { q.mu.Unlock() return } job := q.jobs[0] q.jobs = q.jobs[1:] // 如果队列还有任务,重新发信号 if len(q.jobs) > 0 { select { case q.ready <- struct{}{}: default: } } q.mu.Unlock() l := logger.With("task_id", job.TaskID, "project_id", job.ProjectID) l.Info("task queue executing job", "queue_remaining", len(q.jobs)) if err := job.Execute(job.Ctx); err != nil { l.Error("task job failed", "error", err) } else { l.Info("task job completed") } }