import { Injectable, NotFoundException, Optional } from '@nestjs/common'; import { ImportCandidateRepository } from './import-candidate.repository'; import { KnowledgeItemsRepository } from '../knowledge-items/knowledge-items.repository'; import { ContentSafetyService } from '../content-safety/content-safety.service'; @Injectable() export class ImportCandidateService { constructor( private readonly repository: ImportCandidateRepository, private readonly itemsRepo: KnowledgeItemsRepository, @Optional() private readonly safety?: ContentSafetyService, ) {} private async checkSafety(title: string, content: string, userId: string): Promise { if (!this.safety) return true; const check = await this.safety.check(title + ' ' + (content || '').slice(0, 200), { userId, contentType: 'candidate' }); return check.safe; } async findBySource(sourceId: string) { return this.repository.findBySource(sourceId); } async findOne(id: string) { const c = await this.repository.findById(id); if (!c) throw new NotFoundException('候选知识点不存在'); return c; } async accept(id: string) { const candidate = await this.repository.findById(id); if (!candidate) throw new NotFoundException('候选知识点不存在'); // Content safety check before accepting const safe = await this.checkSafety(candidate.title, candidate.content || '', candidate.userId); if (!safe) return { status: 'BLOCKED', reason: '内容安全审核未通过' }; await this.repository.updateStatus(id, 'ACCEPTED'); // 生成 KnowledgeItem await this.itemsRepo.create(candidate.userId, candidate.knowledgeBaseId, { title: candidate.title, content: (candidate.content as string) ?? '', itemType: 'ai_generated', orderIndex: candidate.orderIndex, }); return { status: 'ACCEPTED' }; } async reject(id: string) { const candidate = await this.repository.findById(id); if (!candidate) throw new NotFoundException('候选知识点不存在'); return this.repository.updateStatus(id, 'REJECTED'); } async batchAccept(sourceId: string) { const candidates = await this.repository.findBySource(sourceId); const pending = candidates.filter(c => c.status === 'PENDING'); for (const c of pending) { await this.accept(c.id); } return { accepted: pending.length }; } async update(id: string, dto: any) { const candidate = await this.repository.findById(id); if (!candidate) throw new NotFoundException('候选知识点不存在'); return this.repository.update(id, dto); } async createCandidates(userId: string, knowledgeBaseId: string, sourceId: string, importId: string, candidates: Array) { // Filter out unsafe candidates const safeCandidates = []; for (const c of candidates) { const safe = await this.checkSafety(c.title || '', c.content || '', userId); if (safe) safeCandidates.push(c); } if (safeCandidates.length === 0) return { created: 0, filtered: candidates.length }; return this.repository.createMany(userId, knowledgeBaseId, sourceId, importId, safeCandidates); } }