api-server/src/infrastructure/queue/queue.service.ts
WangDL 5fd737967f
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 23s
feat: M1-01~03 — AI Gateway deepening, Vector module, Task Queue deepening
M1-01 AI Gateway:
- DB-driven ModelRoute/ProviderConfig/FallbackEvent tables
- ModelRouter rewrite with loadFromDb() hot-reload
- Fallback event recording + AIUsageRecorded event publishing
- Admin AAPI: routes CRUD, provider enable/disable, fallback events log

M1-02 Vector & Retrieval:
- VectorService with Qdrant client (upsert/delete/search/rerank)
- Admin AAPI: collection status, vector count, reindex trigger

M1-03 Task Queue:
- 16 task types with default retry/timeout configs
- Task stats dashboard, worker status panel, batch retry endpoint

M0 audit fixes:
- ApiMetric retention policy (30-day cleanup)
- Content Safety integration in Files module
- Queue registration centralized (domain-events)
- SECRET_MASTER_KEY production validation

E2E tests:
- M0: 28 smoke tests covering all 14 M0 issues
- M1: 16 tests covering M1-01/02/03
- Mock infrastructure: prisma, ioredis, jose, bullmq, qdrant

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:18:07 +08:00

53 lines
2.3 KiB
TypeScript

import { Injectable, Logger, Optional, Inject, forwardRef } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bullmq';
import { EventBusService } from '../../common/event-bus/event-bus.service';
import { BaseDomainEvent } from '../../common/events/base-domain.event';
import { PrismaService } from '../database/prisma.service';
import { Queue } from 'bullmq';
export const QUEUE_AI_ANALYSIS = 'ai-analysis';
export const QUEUE_DOCUMENT_IMPORT = 'document-import';
export const QUEUE_NOTIFICATION = 'notification';
export const QUEUE_DOMAIN_EVENTS = 'domain-events';
export const QUEUE_AUDIT_LOG = 'audit-logs';
export const QUEUE_FILE_CLEANUP = 'file-cleanup';
@Injectable()
export class QueueService {
private readonly logger = new Logger(QueueService.name);
constructor(
private readonly prisma: PrismaService,
@InjectQueue(QUEUE_AI_ANALYSIS) private readonly aiQueue: Queue,
@InjectQueue(QUEUE_DOCUMENT_IMPORT) private readonly importQueue: Queue,
@InjectQueue(QUEUE_NOTIFICATION) private readonly notifyQueue: Queue,
@Optional() private readonly eventBus?: any,
) {}
async add(queueName: string, data: any, opts?: { jobId?: string; attempts?: number; backoff?: number }) {
const queue = this.getQueue(queueName);
const job = await queue.add(queueName, data, { attempts: 3, backoff: { type: 'exponential', delay: 1000 }, ...opts });
// Log to DB
await this.prisma.taskLog.create({ data: { queueName, jobId: job.id || '', status: 'enqueued', payload: JSON.parse(JSON.stringify(data)) } }).catch(() => {});
this.eventBus?.publish(new (class extends BaseDomainEvent { eventType = 'task.enqueued'; queueName: string; jobId: string; constructor(q: string, j: string) { super(); this.queueName = q; this.jobId = j; } })(queueName, job.id || ''));
this.logger.log(`Job ${job.id} added to ${queueName}`);
return job;
}
async getJob(queueName: string, jobId: string) {
const queue = this.getQueue(queueName);
return queue.getJob(jobId);
}
private getQueue(name: string): Queue {
switch (name) {
case QUEUE_AI_ANALYSIS: return this.aiQueue;
case QUEUE_DOCUMENT_IMPORT: return this.importQueue;
case QUEUE_NOTIFICATION: return this.notifyQueue;
default: throw new Error(`Unknown queue: ${name}`);
}
}
}