diff --git a/src/pages/api/admin/groups.ts b/src/pages/api/admin/groups.ts index 911524f..b91a04c 100644 --- a/src/pages/api/admin/groups.ts +++ b/src/pages/api/admin/groups.ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next' import { withAuth, ApiResponse } from '@/shared/utils/api-wrapper' -import { loadGroups, saveGroups, clearGroupsCache, GroupsData } from '@/shared/data/groups-loader' +import { loadGroups, saveGroups, GroupsData } from '@/shared/data/groups-loader' import { validateGroupId, validateCourse } from '@/shared/utils/validation' import { SCHED_MODE } from '@/shared/constants/urls' @@ -19,7 +19,6 @@ async function handler( if (req.method === 'GET') { // Получение списка групп (всегда свежие данные для админ-панели) - clearGroupsCache() const groups = await loadGroups(true) res.status(200).json({ groups }) return @@ -72,8 +71,7 @@ async function handler( } saveGroups(groups) - // Сбрасываем кеш и загружаем свежие данные из БД - clearGroupsCache() + // Загружаем свежие данные из БД (кеш уже сброшен в saveGroups) const updatedGroups = await loadGroups(true) res.status(200).json({ success: true, groups: updatedGroups }) return diff --git a/src/pages/api/admin/groups/[id].ts b/src/pages/api/admin/groups/[id].ts index cc377b9..4b8633e 100644 --- a/src/pages/api/admin/groups/[id].ts +++ b/src/pages/api/admin/groups/[id].ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next' import { withAuth, ApiResponse } from '@/shared/utils/api-wrapper' -import { loadGroups, saveGroups, clearGroupsCache, GroupsData } from '@/shared/data/groups-loader' +import { loadGroups, saveGroups, GroupsData } from '@/shared/data/groups-loader' import { validateCourse } from '@/shared/utils/validation' import { SCHED_MODE } from '@/shared/constants/urls' @@ -60,8 +60,7 @@ async function handler( } saveGroups(groups) - // Сбрасываем кеш и загружаем свежие данные из БД - clearGroupsCache() + // Загружаем свежие данные из БД (кеш уже сброшен в saveGroups) const updatedGroups = await loadGroups(true) res.status(200).json({ success: true, groups: updatedGroups }) return @@ -77,8 +76,7 @@ async function handler( delete groups[id] saveGroups(groups) - // Сбрасываем кеш и загружаем свежие данные из БД - clearGroupsCache() + // Загружаем свежие данные из БД (кеш уже сброшен в saveGroups) const updatedGroups = await loadGroups(true) res.status(200).json({ success: true, groups: updatedGroups }) return diff --git a/src/pages/api/admin/teachers.ts b/src/pages/api/admin/teachers.ts index 26544c7..13ad2b3 100644 --- a/src/pages/api/admin/teachers.ts +++ b/src/pages/api/admin/teachers.ts @@ -17,7 +17,6 @@ async function handler( ) { if (req.method === 'GET') { // Получение списка преподавателей (всегда свежие данные для админ-панели) - clearTeachersCache() const teachers = loadTeachers(true) res.status(200).json({ teachers }) return @@ -69,17 +68,16 @@ async function handler( // Сохраняем в БД saveTeachers(teachersData) - + // Сохраняем timestamp последнего обновления const { setTeachersLastUpdateTime } = await import('@/shared/data/database') setTeachersLastUpdateTime(Date.now()) - - // Сбрасываем кеш и загружаем свежие данные из БД - clearTeachersCache() + + // Загружаем свежие данные из БД (кеш уже сброшен в saveTeachers) const updatedTeachers = loadTeachers(true) - - res.status(200).json({ - success: true, + + res.status(200).json({ + success: true, teachers: updatedTeachers, parsed: teachersList.length }) diff --git a/src/shared/data/database.ts b/src/shared/data/database.ts index 3cc6110..eb64663 100644 --- a/src/shared/data/database.ts +++ b/src/shared/data/database.ts @@ -91,7 +91,7 @@ function migrateDatabaseLocation(): void { // Инициализация базы данных let db: Database.Database | null = null -function getDatabase(): Database.Database { +export function getDatabase(): Database.Database { if (db) { return db } diff --git a/src/shared/data/groups-loader.ts b/src/shared/data/groups-loader.ts index 79a0d14..32dc680 100644 --- a/src/shared/data/groups-loader.ts +++ b/src/shared/data/groups-loader.ts @@ -1,6 +1,7 @@ -import { getAllGroups as getAllGroupsFromDB, createGroup, updateGroup, deleteGroup, getGroup } from './database' +import { getAllGroups as getAllGroupsFromDB, createGroup, updateGroup, deleteGroup, getGroup, getDatabase } from './database' import { SCHED_MODE } from '@/shared/constants/urls' import { syncGroupsFromKspsutiIfNeeded } from '@/app/agregator/groups' +import type { Database } from 'better-sqlite3' export type GroupInfo = { parseId: number @@ -24,17 +25,19 @@ export async function loadGroups(forceRefresh: boolean = false): Promise { + // Добавляем или обновляем группы + for (const [id, group] of Object.entries(groupsData)) { + if (existingIds.has(id)) { + updateStmt.run(group.parseId, group.name, group.course, id) + } else { + insertStmt.run(id, group.parseId, group.name, group.course) + } } - } - // Сбрасываем кеш и timestamp + // Удаляем группы, которых больше нет + for (const id of existingIds) { + if (!newIds.has(id)) { + deleteStmt.run(id) + } + } + }) + + saveTransaction(groups) + + // Сбрасываем кеш и timestamp после успешной транзакции cachedGroups = null cacheTimestamp = 0 } catch (error) { diff --git a/src/shared/data/teachers-loader.ts b/src/shared/data/teachers-loader.ts index 6e4e205..51847eb 100644 --- a/src/shared/data/teachers-loader.ts +++ b/src/shared/data/teachers-loader.ts @@ -1,4 +1,5 @@ -import { getAllTeachers as getAllTeachersFromDB, createTeacher, updateTeacher, deleteTeacher, getTeacher, type TeacherInfo, type TeachersData } from './database' +import { getAllTeachers as getAllTeachersFromDB, createTeacher, updateTeacher, deleteTeacher, getTeacher, getDatabase, type TeacherInfo, type TeachersData } from './database' +import type { Database } from 'better-sqlite3' let cachedTeachers: TeachersData | null = null let cacheTimestamp: number = 0 @@ -11,7 +12,7 @@ const CACHE_TTL_MS = 1000 * 60 // 1 минута export function loadTeachers(forceRefresh: boolean = false): TeachersData { const now = Date.now() const isCacheValid = cachedTeachers !== null && !forceRefresh && (now - cacheTimestamp) < CACHE_TTL_MS - + if (isCacheValid && cachedTeachers !== null) { return cachedTeachers } @@ -33,28 +34,39 @@ export function loadTeachers(forceRefresh: boolean = false): TeachersData { export function saveTeachers(teachers: TeachersData): void { try { const existingTeachers = getAllTeachersFromDB() - + // Определяем, каких преподавателей нужно добавить, обновить или удалить const existingIds = new Set(Object.keys(existingTeachers)) const newIds = new Set(Object.keys(teachers)) - // Добавляем или обновляем преподавателей - for (const [id, teacher] of Object.entries(teachers)) { - if (existingIds.has(id)) { - updateTeacher(id, teacher) - } else { - createTeacher(id, teacher) - } - } + // Получаем ссылки на подготовленные выражения для транзакции + const database = getDatabase() as Database + const insertStmt = database.prepare('INSERT INTO teachers (id, parseId, name) VALUES (?, ?, ?)') + const updateStmt = database.prepare('UPDATE teachers SET parseId = ?, name = ? WHERE id = ?') + const deleteStmt = database.prepare('DELETE FROM teachers WHERE id = ?') - // Удаляем преподавателей, которых больше нет - for (const id of existingIds) { - if (!newIds.has(id)) { - deleteTeacher(id) + // Выполняем все операции в транзакции для атомарности + const saveTransaction = database.transaction((teachersData: TeachersData) => { + // Добавляем или обновляем преподавателей + for (const [id, teacher] of Object.entries(teachersData)) { + if (existingIds.has(id)) { + updateStmt.run(teacher.parseId, teacher.name, id) + } else { + insertStmt.run(id, teacher.parseId, teacher.name) + } } - } - // Сбрасываем кеш и timestamp + // Удаляем преподавателей, которых больше нет + for (const id of existingIds) { + if (!newIds.has(id)) { + deleteStmt.run(id) + } + } + }) + + saveTransaction(teachers) + + // Сбрасываем кеш и timestamp после успешной транзакции cachedTeachers = null cacheTimestamp = 0 } catch (error) {