fix: M2-02/06 audit — system KB seed + candidate Content Safety
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 19s

- SystemKnowledgeBaseSeed: auto-creates built-in 新手引导知识库
- Content Safety check on candidate accept() and createCandidates()

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
WangDL 2026-05-24 13:58:50 +08:00
parent 0c07b59765
commit 3c242a807a
3 changed files with 52 additions and 3 deletions

View File

@ -1,14 +1,22 @@
import { Injectable, NotFoundException } from '@nestjs/common'; import { Injectable, NotFoundException, Optional } from '@nestjs/common';
import { ImportCandidateRepository } from './import-candidate.repository'; import { ImportCandidateRepository } from './import-candidate.repository';
import { KnowledgeItemsRepository } from '../knowledge-items/knowledge-items.repository'; import { KnowledgeItemsRepository } from '../knowledge-items/knowledge-items.repository';
import { ContentSafetyService } from '../content-safety/content-safety.service';
@Injectable() @Injectable()
export class ImportCandidateService { export class ImportCandidateService {
constructor( constructor(
private readonly repository: ImportCandidateRepository, private readonly repository: ImportCandidateRepository,
private readonly itemsRepo: KnowledgeItemsRepository, 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) { async findBySource(sourceId: string) {
return this.repository.findBySource(sourceId); return this.repository.findBySource(sourceId);
} }
@ -23,6 +31,10 @@ export class ImportCandidateService {
const candidate = await this.repository.findById(id); const candidate = await this.repository.findById(id);
if (!candidate) throw new NotFoundException('候选知识点不存在'); 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'); await this.repository.updateStatus(id, 'ACCEPTED');
// 生成 KnowledgeItem // 生成 KnowledgeItem
@ -60,6 +72,13 @@ export class ImportCandidateService {
} }
async createCandidates(userId: string, knowledgeBaseId: string, sourceId: string, importId: string, candidates: Array<any>) { async createCandidates(userId: string, knowledgeBaseId: string, sourceId: string, importId: string, candidates: Array<any>) {
return this.repository.createMany(userId, knowledgeBaseId, sourceId, importId, candidates); // 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);
} }
} }

View File

@ -2,11 +2,12 @@ import { Module } from '@nestjs/common';
import { KnowledgeBaseController } from './knowledge-base.controller'; import { KnowledgeBaseController } from './knowledge-base.controller';
import { KnowledgeBaseService } from './knowledge-base.service'; import { KnowledgeBaseService } from './knowledge-base.service';
import { KnowledgeBaseRepository } from './knowledge-base.repository'; import { KnowledgeBaseRepository } from './knowledge-base.repository';
import { SystemKnowledgeBaseSeed } from './system-kb.seed';
import { PrismaService } from '../../infrastructure/database/prisma.service'; import { PrismaService } from '../../infrastructure/database/prisma.service';
@Module({ @Module({
controllers: [KnowledgeBaseController], controllers: [KnowledgeBaseController],
providers: [KnowledgeBaseService, KnowledgeBaseRepository, PrismaService], providers: [KnowledgeBaseService, KnowledgeBaseRepository, SystemKnowledgeBaseSeed, PrismaService],
exports: [KnowledgeBaseService], exports: [KnowledgeBaseService],
}) })
export class KnowledgeBaseModule {} export class KnowledgeBaseModule {}

View File

@ -0,0 +1,29 @@
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';
const SYSTEM_KB = {
id: 'system-builtin-kb',
userId: 'system',
title: '新手引导知识库',
description: '系统内置知识库,帮助新用户了解知习的使用方法',
status: 'active',
};
@Injectable()
export class SystemKnowledgeBaseSeed implements OnModuleInit {
private readonly logger = new Logger(SystemKnowledgeBaseSeed.name);
constructor(private readonly prisma: PrismaService) {}
async onModuleInit() {
try {
await this.prisma.knowledgeBase.upsert({
where: { id: SYSTEM_KB.id },
update: {},
create: SYSTEM_KB,
});
} catch {
this.logger.warn('Failed to seed system knowledge base');
}
}
}