fix(dev): bind frontend to 127.0.0.1:8080 and avoid EACCES\nfix(backend): bind server to 127.0.0.1:8081, add permissive CORS whitelist\nfix(auth): login form UX remove default username, clarify placeholder, add test account autofill\nchore(api): set frontend API_BASE_URL to 127.0.0.1:8081\nrefactor(assignments): lifecycle/state logic and archive endpoint\nfeat(analytics): add exam stats endpoint and client method\nchore(lint): add eslint configs
This commit is contained in:
121
backend/src/services/curriculum.service.ts
Normal file
121
backend/src/services/curriculum.service.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import prisma from '../utils/prisma';
|
||||
|
||||
export class CurriculumService {
|
||||
async getSubjects() {
|
||||
return prisma.subject.findMany({ where: { isDeleted: false }, select: { id: true, name: true, code: true, icon: true }, orderBy: { name: 'asc' } });
|
||||
}
|
||||
|
||||
async getTextbookTree(id: string) {
|
||||
let textbook = await prisma.textbook.findUnique({
|
||||
where: { id, isDeleted: false },
|
||||
include: {
|
||||
units: {
|
||||
where: { isDeleted: false },
|
||||
include: {
|
||||
lessons: {
|
||||
where: { isDeleted: false },
|
||||
include: { knowledgePoints: { where: { isDeleted: false }, orderBy: { difficulty: 'asc' } } },
|
||||
orderBy: { sortOrder: 'asc' }
|
||||
}
|
||||
},
|
||||
orderBy: { sortOrder: 'asc' }
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!textbook) {
|
||||
textbook = await prisma.textbook.findFirst({
|
||||
where: { subjectId: id, isDeleted: false },
|
||||
include: {
|
||||
units: {
|
||||
where: { isDeleted: false },
|
||||
include: {
|
||||
lessons: {
|
||||
where: { isDeleted: false },
|
||||
include: { knowledgePoints: { where: { isDeleted: false }, orderBy: { difficulty: 'asc' } } },
|
||||
orderBy: { sortOrder: 'asc' }
|
||||
}
|
||||
},
|
||||
orderBy: { sortOrder: 'asc' }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!textbook) throw new Error('Textbook not found');
|
||||
const units = textbook.units.map(unit => ({
|
||||
id: unit.id,
|
||||
textbookId: unit.textbookId,
|
||||
name: unit.name,
|
||||
sortOrder: unit.sortOrder,
|
||||
lessons: unit.lessons.map(lesson => ({
|
||||
id: lesson.id,
|
||||
unitId: lesson.unitId,
|
||||
name: lesson.name,
|
||||
sortOrder: lesson.sortOrder,
|
||||
knowledgePoints: lesson.knowledgePoints.map(kp => ({ id: kp.id, lessonId: kp.lessonId, name: kp.name, difficulty: kp.difficulty, description: kp.description }))
|
||||
}))
|
||||
}));
|
||||
return { textbook: { id: textbook.id, name: textbook.name, publisher: textbook.publisher, versionYear: textbook.versionYear, coverUrl: textbook.coverUrl }, units };
|
||||
}
|
||||
|
||||
async getTextbooksBySubject(subjectId: string) {
|
||||
return prisma.textbook.findMany({ where: { subjectId, isDeleted: false }, select: { id: true, name: true, publisher: true, versionYear: true, coverUrl: true }, orderBy: { name: 'asc' } });
|
||||
}
|
||||
|
||||
async createTextbook(userId: string, data: { subjectId: string; name: string; publisher: string; versionYear: string; coverUrl?: string }) {
|
||||
const { subjectId, name, publisher, versionYear, coverUrl } = data;
|
||||
return prisma.textbook.create({ data: { subjectId, name, publisher, versionYear, coverUrl: coverUrl || '', createdBy: userId, updatedBy: userId } });
|
||||
}
|
||||
|
||||
async updateTextbook(id: string, data: { name?: string; publisher?: string; versionYear?: string; coverUrl?: string }) {
|
||||
return prisma.textbook.update({ where: { id }, data });
|
||||
}
|
||||
|
||||
async deleteTextbook(id: string) {
|
||||
await prisma.textbook.update({ where: { id }, data: { isDeleted: true } });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async createUnit(userId: string, data: { textbookId: string; name: string; sortOrder?: number }) {
|
||||
const { textbookId, name, sortOrder } = data;
|
||||
return prisma.textbookUnit.create({ data: { textbookId, name, sortOrder: sortOrder || 0, createdBy: userId, updatedBy: userId } });
|
||||
}
|
||||
|
||||
async updateUnit(id: string, data: { name?: string; sortOrder?: number }) {
|
||||
return prisma.textbookUnit.update({ where: { id }, data });
|
||||
}
|
||||
|
||||
async deleteUnit(id: string) {
|
||||
await prisma.textbookUnit.update({ where: { id }, data: { isDeleted: true } });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async createLesson(userId: string, data: { unitId: string; name: string; sortOrder?: number }) {
|
||||
const { unitId, name, sortOrder } = data;
|
||||
return prisma.textbookLesson.create({ data: { unitId, name, sortOrder: sortOrder || 0, createdBy: userId, updatedBy: userId } });
|
||||
}
|
||||
|
||||
async updateLesson(id: string, data: { name?: string; sortOrder?: number }) {
|
||||
return prisma.textbookLesson.update({ where: { id }, data });
|
||||
}
|
||||
|
||||
async deleteLesson(id: string) {
|
||||
await prisma.textbookLesson.update({ where: { id }, data: { isDeleted: true } });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async createKnowledgePoint(userId: string, data: { lessonId: string; name: string; difficulty?: number; description?: string }) {
|
||||
const { lessonId, name, difficulty, description } = data;
|
||||
return prisma.knowledgePoint.create({ data: { lessonId, name, difficulty: difficulty || 1, description: description || '', createdBy: userId, updatedBy: userId } });
|
||||
}
|
||||
|
||||
async updateKnowledgePoint(id: string, data: { name?: string; difficulty?: number; description?: string }) {
|
||||
return prisma.knowledgePoint.update({ where: { id }, data });
|
||||
}
|
||||
|
||||
async deleteKnowledgePoint(id: string) {
|
||||
await prisma.knowledgePoint.update({ where: { id }, data: { isDeleted: true } });
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
|
||||
export const curriculumService = new CurriculumService();
|
||||
Reference in New Issue
Block a user