All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 59s
B12: AI analysis now async — POST /ai-analysis queues job, returns immediately.
Worker supports both active-recall and feynman-evaluation types.
B13: DocumentImportWorker fully implemented — all processing moved from
service to worker. Service only queues and returns.
B14: NotificationWorker already complete (no changes needed).
B15: All 3 workers now fully functional.
New endpoint: GET /ai-analysis/jobs/:id for job status polling.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { DocumentImportRepository } from './document-import.repository';
|
|
import { RedisService } from '../../infrastructure/redis/redis.service';
|
|
import { QueueService } from '../../infrastructure/queue/queue.service';
|
|
|
|
@Injectable()
|
|
export class DocumentImportService {
|
|
constructor(
|
|
private readonly repository: DocumentImportRepository,
|
|
private readonly redis: RedisService,
|
|
private readonly queue: QueueService,
|
|
) {}
|
|
|
|
async createImport(dto: {
|
|
userId?: string;
|
|
knowledgeBaseId?: string;
|
|
fileName?: string;
|
|
sourceType?: string;
|
|
rawText?: string;
|
|
}) {
|
|
const lockKey = `lock:document-import:${dto.fileName || Date.now()}`;
|
|
const lockToken = await this.redis.lock(lockKey, 1800);
|
|
if (!lockToken) {
|
|
throw new Error('相同文件正在导入中,请稍候');
|
|
}
|
|
|
|
const job = await this.repository.create(dto);
|
|
|
|
await this.redis.set(`job:document-import:${job.id}:status`, 'pending', 86400);
|
|
await this.redis.set(`job:document-import:${job.id}:progress`, '0', 86400);
|
|
await this.redis.set(`job:document-import:${job.id}:message`, '任务已加入队列', 86400);
|
|
|
|
await this.queue.add('document-import', {
|
|
importId: job.id,
|
|
userId: dto.userId || 'anonymous',
|
|
knowledgeBaseId: dto.knowledgeBaseId,
|
|
rawText: dto.rawText,
|
|
fileName: dto.fileName,
|
|
});
|
|
|
|
// Release the lock — the worker will re-lock if needed
|
|
await this.redis.unlock(lockKey, lockToken);
|
|
|
|
return { jobId: job.id, status: 'queued' };
|
|
}
|
|
|
|
async getStatus(id: string) {
|
|
const redisStatus = await this.redis.get(`job:document-import:${id}:status`);
|
|
const redisProgress = await this.redis.get(`job:document-import:${id}:progress`);
|
|
const redisMessage = await this.redis.get(`job:document-import:${id}:message`);
|
|
const dbJob = await this.repository.findById(id);
|
|
|
|
return {
|
|
id,
|
|
fileName: dbJob?.sourceName,
|
|
status: redisStatus || dbJob?.status || 'unknown',
|
|
progress: redisProgress ? parseInt(redisProgress, 10) : 0,
|
|
message: redisMessage || null,
|
|
};
|
|
}
|
|
}
|