const { Grade, GradeItem, GradeSummary, StudentDetail, Subject, AcademicYear } = require('../models'); const { cacheUtils } = require('../config/redis'); const { addDatabaseWriteJob, addGradeCalculationJob } = require('../config/bullmq'); /** * Grade Controller - Quản lý điểm số */ class GradeController { /** * Get grades with pagination */ async getAllGrades(req, res, next) { try { const { page = 1, limit = 50, student_id, subject_id, academic_year_id } = req.query; const offset = (page - 1) * limit; const cacheKey = `grades:list:${page}:${limit}:${student_id || 'all'}:${subject_id || 'all'}:${academic_year_id || 'all'}`; const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const where = {}; if (student_id) where.student_id = student_id; if (subject_id) where.subject_id = subject_id; if (academic_year_id) where.academic_year_id = academic_year_id; const { count, rows } = await Grade.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), include: [ { model: StudentDetail, as: 'student', attributes: ['student_code'], }, { model: GradeItem, as: 'gradeItem', attributes: ['item_name', 'max_score'], }, ], order: [['created_at', 'DESC']], }); const result = { grades: rows, pagination: { total: count, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(count / limit), }, }; await cacheUtils.set(cacheKey, result, 1800); res.json({ success: true, data: result, cached: false, }); } catch (error) { next(error); } } /** * Get grade by ID */ async getGradeById(req, res, next) { try { const { id } = req.params; const cacheKey = `grade:${id}`; const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const grade = await Grade.findByPk(id, { include: [ { model: StudentDetail, as: 'student', }, { model: GradeItem, as: 'gradeItem', }, ], }); if (!grade) { return res.status(404).json({ success: false, message: 'Grade not found', }); } await cacheUtils.set(cacheKey, grade, 3600); res.json({ success: true, data: grade, cached: false, }); } catch (error) { next(error); } } /** * Create new grade (async via BullMQ) */ async createGrade(req, res, next) { try { const gradeData = req.body; const job = await addDatabaseWriteJob('create', 'Grade', gradeData); await cacheUtils.deletePattern('grades:list:*'); await cacheUtils.deletePattern(`grade:student:${gradeData.student_id}:*`); res.status(202).json({ success: true, message: 'Grade creation job queued', jobId: job.id, data: gradeData, }); } catch (error) { next(error); } } /** * Update grade (async via BullMQ) */ async updateGrade(req, res, next) { try { const { id } = req.params; const updates = req.body; const job = await addDatabaseWriteJob('update', 'Grade', { id, updates, }); await cacheUtils.delete(`grade:${id}`); await cacheUtils.deletePattern('grades:list:*'); res.status(202).json({ success: true, message: 'Grade update job queued', jobId: job.id, }); } catch (error) { next(error); } } /** * Delete grade (async via BullMQ) */ async deleteGrade(req, res, next) { try { const { id } = req.params; const job = await addDatabaseWriteJob('delete', 'Grade', { id }); await cacheUtils.delete(`grade:${id}`); await cacheUtils.deletePattern('grades:list:*'); res.status(202).json({ success: true, message: 'Grade deletion job queued', jobId: job.id, }); } catch (error) { next(error); } } /** * Get student grade summary */ async getStudentGradeSummary(req, res, next) { try { const { student_id, academic_year_id } = req.params; const cacheKey = `grade:student:${student_id}:${academic_year_id}`; const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const summary = await GradeSummary.findOne({ where: { student_id, academic_year_id }, }); await cacheUtils.set(cacheKey, summary, 3600); res.json({ success: true, data: summary, cached: false, }); } catch (error) { next(error); } } /** * Calculate student GPA (async via BullMQ) */ async calculateGPA(req, res, next) { try { const { student_id, academic_year_id } = req.body; const job = await addGradeCalculationJob(student_id, academic_year_id); await cacheUtils.deletePattern(`grade:student:${student_id}:*`); res.status(202).json({ success: true, message: 'GPA calculation job queued', jobId: job.id, }); } catch (error) { next(error); } } /** * Get grade datatypes */ async getGradeDatatypes(req, res, next) { try { const datatypes = Grade.rawAttributes; res.json({ success: true, data: datatypes, }); } catch (error) { next(error); } } } module.exports = new GradeController();