- database.ts 导出db常量替代getDb()函数 - env.ts JWT_SECRET/ES_URL/NEO4J_URL改optional加DEV_MODE - neo4j.ts driver惰性创建+try/catch+connectionTimeout:3000 - health/lifecycle改用Drizzle原生查询 - textbooks.schema修复integer到int+导出NewTextbook类型 - 新建chapters/knowledge-points/questions三模块CRUD - knowledge-points含Neo4j前置依赖图非阻塞查询 - content-init.sql创建4张表 端到端验证: textbooks/chapters/knowledge-points/questions全CRUD通过
84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { Injectable } from "@nestjs/common";
|
|
import { questionsRepository } from "./questions.repository.js";
|
|
import type { Question } from "./questions.schema.js";
|
|
import {
|
|
NotFoundError,
|
|
ValidationError,
|
|
} from "../shared/errors/application-error.js";
|
|
|
|
export interface CreateQuestionInput {
|
|
knowledgePointId: string;
|
|
type: string;
|
|
content: string;
|
|
answer?: string;
|
|
explanation?: string;
|
|
difficulty?: number;
|
|
}
|
|
|
|
export interface UpdateQuestionInput {
|
|
type?: string;
|
|
content?: string;
|
|
answer?: string;
|
|
explanation?: string;
|
|
difficulty?: number;
|
|
}
|
|
|
|
const VALID_TYPES = new Set([
|
|
"single_choice",
|
|
"multiple_choice",
|
|
"short_answer",
|
|
"essay",
|
|
]);
|
|
|
|
@Injectable()
|
|
export class QuestionsService {
|
|
async createQuestion(input: CreateQuestionInput): Promise<{ id: string }> {
|
|
if (!input.knowledgePointId || !input.type || !input.content) {
|
|
throw new ValidationError("knowledgePointId, type, content are required");
|
|
}
|
|
if (!VALID_TYPES.has(input.type)) {
|
|
throw new ValidationError(
|
|
`Invalid question type: ${input.type}. Must be one of: ${[...VALID_TYPES].join(", ")}`,
|
|
);
|
|
}
|
|
|
|
const id = randomUUID();
|
|
await questionsRepository.create({
|
|
id,
|
|
knowledgePointId: input.knowledgePointId,
|
|
type: input.type,
|
|
content: input.content,
|
|
answer: input.answer,
|
|
explanation: input.explanation,
|
|
difficulty: input.difficulty,
|
|
});
|
|
return { id };
|
|
}
|
|
|
|
async getQuestion(id: string): Promise<Question> {
|
|
const question = await questionsRepository.findById(id);
|
|
if (!question) {
|
|
throw new NotFoundError("Question", id);
|
|
}
|
|
return question;
|
|
}
|
|
|
|
async listByKnowledgePoint(knowledgePointId: string): Promise<Question[]> {
|
|
return questionsRepository.findByKnowledgePointId(knowledgePointId);
|
|
}
|
|
|
|
async updateQuestion(id: string, data: UpdateQuestionInput): Promise<void> {
|
|
await this.getQuestion(id);
|
|
if (data.type && !VALID_TYPES.has(data.type)) {
|
|
throw new ValidationError(`Invalid question type: ${data.type}`);
|
|
}
|
|
await questionsRepository.update(id, data);
|
|
}
|
|
|
|
async deleteQuestion(id: string): Promise<void> {
|
|
await this.getQuestion(id);
|
|
await questionsRepository.delete(id);
|
|
}
|
|
}
|