feat(tests): общие тесты с автором, в истории только результаты ученика (для всех ролей)

Made-with: Cursor
This commit is contained in:
Константин Лебединский
2026-04-01 23:09:17 +05:00
parent d8805bd4ed
commit 35f27a6eb7
4 changed files with 79 additions and 19 deletions
+33 -14
View File
@@ -17,27 +17,41 @@ const DEFAULT_TEST_PROMPT = `Ты — составитель тестов. Сг
ВАЖНО: Верни ТОЛЬКО JSON-массив, без markdown-обёрток (\`\`\`json), без пояснений, ЧИСТЫЙ JSON:
[{"question":"текст вопроса","options":{"a":"вариант А","b":"вариант Б","c":"вариант В","d":"вариант Г"},"correct":"a"}]`;
/** Тесты привязаны к текущему пользователю (ученик — свои; наставник — свои черновые, не к ученику). */
function testOwnerId(req: AuthRequest): number {
return req.user!.id;
/** Контекст пары: 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 ownerId = testOwnerId(req);
const scopeId = pairStudentScopeId(req);
const tests = await prisma.test.findMany({
where: { studentId: ownerId },
where: { studentId: scopeId },
orderBy: { createdAt: "desc" },
include: { results: true },
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 ownerId = testOwnerId(req);
const scopeId = pairStudentScopeId(req);
const test = await prisma.test.findFirst({
where: { id, studentId: ownerId },
include: { results: true },
where: { id, studentId: scopeId },
include: {
author: { select: authorSelect },
results: {
where: { studentId: scopeId },
orderBy: { createdAt: "asc" },
},
},
});
if (!test) {
@@ -50,8 +64,7 @@ router.get("/:id", async (req: AuthRequest, res: Response) => {
router.post("/generate", async (req: AuthRequest, res: Response) => {
const { topic, fromQuestions } = req.body;
const ownerId = testOwnerId(req);
/** Вопросы «по моим вопросам» берутся из базы назначенного ученика (для наставника — его ученик). */
const scopeId = pairStudentScopeId(req);
const questionBankStudentId = req.studentId!;
const client = await getDeepSeekClient();
@@ -92,7 +105,12 @@ router.post("/generate", async (req: AuthRequest, res: Response) => {
data: {
topic: topic || "По прошлым вопросам",
questions: questionsJson,
studentId: ownerId,
studentId: scopeId,
authorId: req.user!.id,
},
include: {
author: { select: authorSelect },
results: true,
},
});
@@ -104,10 +122,10 @@ router.post("/generate", async (req: AuthRequest, res: Response) => {
router.post("/:id/submit", async (req: AuthRequest, res: Response) => {
const id = parseInt(req.params.id as string, 10);
const ownerId = testOwnerId(req);
const scopeId = pairStudentScopeId(req);
const { answers } = req.body as { answers: Record<string, string> };
const test = await prisma.test.findFirst({ where: { id, studentId: ownerId } });
const test = await prisma.test.findFirst({ where: { id, studentId: scopeId } });
if (!test) {
res.status(404).json({ error: "Test not found" });
return;
@@ -131,6 +149,7 @@ router.post("/:id/submit", async (req: AuthRequest, res: Response) => {
const result = await prisma.testResult.create({
data: {
testId: id,
studentId: req.user!.id,
answers: JSON.stringify(answers),
score,
total,