api-server/src/workers/document-import.worker.ts
wangdl 939adcebd3
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 43s
feat(API-AI-081): add KnowledgeItem.sourceId FK to KnowledgeSource
- Prisma schema: add sourceId field + @relation to KnowledgeSource
- KnowledgeSource: add items[] reverse relation + @index on sourceId
- KnowledgeItemsRepository: accept sourceId in create()
- ImportCandidateService.accept(): pass sourceId to create()
- DocumentImport worker: pass sourceId alongside sourceRef

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 21:54:58 +08:00

92 lines
4.1 KiB
TypeScript

import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Logger } from '@nestjs/common';
import { Job } from 'bullmq';
import { QUEUE_DOCUMENT_IMPORT } from '../infrastructure/queue/queue.service';
import { DocumentImportRepository } from '../modules/document-import/document-import.repository';
import { KnowledgeItemsRepository } from '../modules/knowledge-items/knowledge-items.repository';
import { KnowledgeImportWorkflow } from '../modules/ai/workflows/knowledge-import.workflow';
import { RedisService } from '../infrastructure/redis/redis.service';
@Processor(QUEUE_DOCUMENT_IMPORT)
export class DocumentImportWorker extends WorkerHost {
private readonly logger = new Logger(DocumentImportWorker.name);
constructor(
private readonly repository: DocumentImportRepository,
private readonly knowledgeItemsRepo: KnowledgeItemsRepository,
private readonly workflow: KnowledgeImportWorkflow,
private readonly redis: RedisService,
) {
super();
}
async process(job: Job<{
importId: string;
userId: string;
knowledgeBaseId?: string;
sourceId?: string;
rawText?: string;
fileName?: string;
}>) {
const { importId, userId, knowledgeBaseId, rawText, fileName } = job.data;
// Resolve sourceId: from job data first, then fallback to DocumentImport record
let sourceId = job.data.sourceId;
if (!sourceId) {
const importRecord = await this.repository.findById(importId);
sourceId = importRecord?.sourceId ?? undefined;
}
this.logger.log(`Processing document import job ${job.id}, importId=${importId}, sourceId=${sourceId ?? 'none'}`);
try {
if (!rawText) {
await this.repository.updateStatus(importId, 'completed');
await this.redis.set(`job:document-import:${importId}:status`, 'completed', 86400);
await this.redis.set(`job:document-import:${importId}:progress`, '100', 86400);
await this.redis.set(`job:document-import:${importId}:message`, '无需解析的空文件', 86400);
return;
}
await this.repository.updateStatus(importId, 'processing');
await this.redis.set(`job:document-import:${importId}:status`, 'parsing', 86400);
await this.redis.set(`job:document-import:${importId}:progress`, '25', 86400);
await this.redis.set(`job:document-import:${importId}:message`, 'AI 正在分析文本,提取知识点...', 86400);
const result = await this.workflow.execute({
userId,
rawText,
sourceName: fileName,
});
await this.redis.set(`job:document-import:${importId}:status`, 'saving', 86400);
await this.redis.set(`job:document-import:${importId}:progress`, '80', 86400);
await this.redis.set(`job:document-import:${importId}:message`, `正在保存 ${result.knowledgePoints.length} 个知识点...`, 86400);
if (knowledgeBaseId && result.knowledgePoints.length > 0) {
for (let i = 0; i < result.knowledgePoints.length; i++) {
const kp = result.knowledgePoints[i];
await this.knowledgeItemsRepo.create(userId, knowledgeBaseId, {
title: kp.title,
content: kp.content,
itemType: 'lesson',
orderIndex: kp.suggestedOrder ?? i + 1,
sourceId: sourceId,
sourceRef: sourceId,
});
}
}
await this.repository.updateStatus(importId, 'completed');
await this.redis.set(`job:document-import:${importId}:status`, 'completed', 86400);
await this.redis.set(`job:document-import:${importId}:progress`, '100', 86400);
await this.redis.set(`job:document-import:${importId}:message`, `成功提取 ${result.knowledgePoints.length} 个知识点`, 86400);
this.logger.log(`Document import job ${job.id} completed: ${result.knowledgePoints.length} knowledge points`);
} catch (err: any) {
this.logger.error(`Document import job ${job.id} failed: ${err.message}`);
await this.repository.updateStatus(importId, 'failed');
await this.redis.set(`job:document-import:${importId}:status`, 'failed', 86400);
await this.redis.set(`job:document-import:${importId}:message`, `导入失败: ${err.message}`, 86400);
throw err;
}
}
}