import { Router, Response } from "express"; import prisma from "../lib/prisma"; import { getDeepSeekClient, getPrompt } from "../lib/deepseek"; import type { AuthRequest } from "../middleware/auth"; const router = Router(); const DEFAULT_TEST_PROMPT = `Ты — составитель тестов. Сгенерируй тест из РОВНО 10 вопросов. Требования к вопросам: - Вопросы должны проверять ПОНИМАНИЕ темы, а не запоминание фактов - Сложность: 3 лёгких, 4 средних, 3 сложных - Каждый вопрос имеет 4 варианта ответа (a, b, c, d) и ровно один правильный - Неправильные варианты должны быть правдоподобными (не абсурдными) - Вопросы на русском языке ВАЖНО: Верни ТОЛЬКО JSON-массив, без markdown-обёрток (\`\`\`json), без пояснений, ЧИСТЫЙ JSON: [{"question":"текст вопроса","options":{"a":"вариант А","b":"вариант Б","c":"вариант В","d":"вариант Г"},"correct":"a"}]`; /** Контекст пары: id назначенного ученика (общие тесты для наставника и ученика). */ function pairStudentScopeId(req: AuthRequest): number { return req.studentId!; } const authorSelect = { username: true, displayName: true } as const; router.get("/", async (req: AuthRequest, res: Response) => { const scopeId = pairStudentScopeId(req); const tests = await prisma.test.findMany({ where: { studentId: scopeId }, orderBy: { createdAt: "desc" }, include: { author: { select: authorSelect }, results: { where: { studentId: scopeId }, orderBy: { createdAt: "asc" }, }, }, }); res.json(tests); }); router.get("/:id", async (req: AuthRequest, res: Response) => { const id = parseInt(req.params.id as string, 10); const scopeId = pairStudentScopeId(req); const test = await prisma.test.findFirst({ where: { id, studentId: scopeId }, include: { author: { select: authorSelect }, results: { where: { studentId: scopeId }, orderBy: { createdAt: "asc" }, }, }, }); if (!test) { res.status(404).json({ error: "Test not found" }); return; } res.json(test); }); router.post("/generate", async (req: AuthRequest, res: Response) => { const { topic, fromQuestions } = req.body; const scopeId = pairStudentScopeId(req); const questionBankStudentId = req.studentId!; const client = await getDeepSeekClient(); const systemPrompt = await getPrompt("prompt_test", DEFAULT_TEST_PROMPT); let userMessage: string; if (fromQuestions) { const questions = await prisma.question.findMany({ where: { studentId: questionBankStudentId }, orderBy: { createdAt: "desc" }, take: 20, }); const questionTexts = questions.map((q: { text: string }) => q.text).join("\n"); userMessage = `Составь тест на основе этих вопросов, которые задавал пользователь ранее:\n${questionTexts}`; } else { if (!topic) { res.status(400).json({ error: "Topic is required" }); return; } userMessage = `Тема: ${topic}`; } try { const response = await client.chat.completions.create({ model: "deepseek-chat", messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userMessage }, ], }); const raw = response.choices[0]?.message?.content || "[]"; const jsonMatch = raw.match(/\[[\s\S]*\]/); const questionsJson = jsonMatch ? jsonMatch[0] : "[]"; const test = await prisma.test.create({ data: { topic: topic || "По прошлым вопросам", questions: questionsJson, studentId: scopeId, authorId: req.user!.id, }, include: { author: { select: authorSelect }, results: true, }, }); res.json(test); } catch (err: any) { res.status(500).json({ error: err.message }); } }); router.post("/:id/submit", async (req: AuthRequest, res: Response) => { const id = parseInt(req.params.id as string, 10); const scopeId = pairStudentScopeId(req); const { answers } = req.body as { answers: Record }; const test = await prisma.test.findFirst({ where: { id, studentId: scopeId } }); if (!test) { res.status(404).json({ error: "Test not found" }); return; } const questions = JSON.parse(test.questions); let score = 0; const total = questions.length; for (const q of questions as { question: string; correct: string }[]) { if (answers[q.question] === q.correct) { score++; } } if (req.user!.role === "TUTOR") { res.json({ score, total, persisted: false as const }); return; } const result = await prisma.testResult.create({ data: { testId: id, studentId: req.user!.id, answers: JSON.stringify(answers), score, total, }, }); res.json({ ...result, persisted: true as const }); }); export default router;