api-server/src/modules/import-candidate/import-candidate.service.ts

85 lines
3.1 KiB
TypeScript
Raw Normal View History

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<boolean> {
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<any>) {
// 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);
}
}