// 静态 import 而非 createRequire + path.join:打包器不跟踪运行时拼接的路径,编译后会找不到模块。 import portraitTags from '../../../mcp/src/tools/qiwei-portrait-tags-run.js'; const { processPortraitQueue } = portraitTags; export class PortraitQueueWorker { constructor({ intervalMs = 60_000, limit = 5, onState = () => {}, processor = processPortraitQueue } = {}) { this.intervalMs = intervalMs; this.limit = limit; this.onState = onState; this.processor = processor; this.running = false; this.timer = null; this.inFlight = null; } start() { if (this.running) return; this.running = true; this.onState({ portraitQueue: { status: 'running', lastError: '' } }); void this.runOnce(); this.timer = setInterval(() => void this.runOnce(), this.intervalMs); this.timer.unref?.(); } async runOnce() { if (!this.running) return { skipped: true, reason: 'stopped' }; if (this.inFlight) return { skipped: true, reason: 'in-flight' }; this.inFlight = (async () => { try { const result = await this.processor(this.limit); this.onState({ portraitQueue: { status: 'running', lastRunAt: new Date().toISOString(), processed: Number(result?.processed || 0), remaining: Number(result?.remaining || 0), lastError: '', }, }); return result; } catch (error) { this.onState({ portraitQueue: { status: 'error', lastRunAt: new Date().toISOString(), lastError: error.message, }, }); return { processed: 0, error: error.message }; } finally { this.inFlight = null; } })(); return this.inFlight; } async stop() { this.running = false; if (this.timer) clearInterval(this.timer); this.timer = null; if (this.inFlight) await this.inFlight; this.onState({ portraitQueue: { status: 'stopped', stoppedAt: new Date().toISOString() } }); } }