const { Class } = require('../models'); const { StudentDetail, UserProfile, UsersAuth, School } = require('../models'); const { cacheUtils } = require('../config/redis'); /** * Class Controller - Quản lý thông tin lớp học */ class ClassController { /** * Get all classes with pagination and caching */ async getAllClasses(req, res, next) { try { const { page = 1, limit = 20, school_id, grade_level, academic_year_id } = req.query; const offset = (page - 1) * limit; // Generate cache key const cacheKey = `classes:list:${page}:${limit}:${school_id || 'all'}:${grade_level || 'all'}:${academic_year_id || 'all'}`; // Try to get from cache first const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } // Build query conditions const where = {}; if (school_id) where.school_id = school_id; if (grade_level) where.grade_level = parseInt(grade_level); if (academic_year_id) where.academic_year_id = academic_year_id; // Query from database (through ProxySQL) const { count, rows } = await Class.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['class_name', 'ASC']], }); const result = { classes: rows, pagination: { total: count, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(count / limit), }, }; // Cache the result await cacheUtils.set(cacheKey, result, 1800); // Cache for 30 minutes res.json({ success: true, data: result, cached: false, }); } catch (error) { next(error); } } /** * Get class by ID with caching */ async getClassById(req, res, next) { try { const { id } = req.params; const cacheKey = `class:${id}`; // Try cache first const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } // Query from database const classData = await Class.findByPk(id); if (!classData) { return res.status(404).json({ success: false, message: 'Class not found', }); } // Cache the result await cacheUtils.set(cacheKey, classData, 3600); // Cache for 1 hour res.json({ success: true, data: classData, cached: false, }); } catch (error) { next(error); } } /** * Create new class */ async createClass(req, res, next) { try { const classData = req.body; // Create class directly const newClass = await Class.create(classData); // Invalidate related caches await cacheUtils.deletePattern('classes:list:*'); res.status(201).json({ success: true, message: 'Class created successfully', data: newClass, }); } catch (error) { next(error); } } /** * Update class */ async updateClass(req, res, next) { try { const { id } = req.params; const updates = req.body; const classData = await Class.findByPk(id); if (!classData) { return res.status(404).json({ success: false, message: 'Class not found', }); } // Update class directly await classData.update(updates); // Invalidate related caches await cacheUtils.delete(`class:${id}`); await cacheUtils.deletePattern('classes:list:*'); res.json({ success: true, message: 'Class updated successfully', data: classData, }); } catch (error) { next(error); } } /** * Delete class */ async deleteClass(req, res, next) { try { const { id } = req.params; const classData = await Class.findByPk(id); if (!classData) { return res.status(404).json({ success: false, message: 'Class not found', }); } // Delete class directly await classData.destroy(); // Invalidate related caches await cacheUtils.delete(`class:${id}`); await cacheUtils.deletePattern('classes:list:*'); res.json({ success: true, message: 'Class deleted successfully', }); } catch (error) { next(error); } } /** * Get class statistics */ async getClassStatistics(req, res, next) { try { const { school_id } = req.query; const cacheKey = `class:stats:${school_id || 'all'}`; // Try cache first const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const where = {}; if (school_id) where.school_id = school_id; const stats = { totalClasses: await Class.count({ where }), byGradeLevel: await Class.findAll({ where, attributes: [ 'grade_level', [require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'], ], group: ['grade_level'], raw: true, }), }; // Cache for 1 hour await cacheUtils.set(cacheKey, stats, 3600); res.json({ success: true, data: stats, cached: false, }); } catch (error) { next(error); } } /** * Get class datatypes */ async getClassDatatypes(req, res, next) { try { const datatypes = Class.rawAttributes; res.json({ success: true, data: datatypes, }); } catch (error) { next(error); } } /** * Get students in a class */ async getStudentsByClass(req, res, next) { try { const { id } = req.params; const { page = 1, limit = 20, status = 'active' } = req.query; const offset = (page - 1) * limit; // Generate cache key const cacheKey = `class:${id}:students:${page}:${limit}:${status}`; // Try cache first const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } // Check if class exists and get school info const classData = await Class.findByPk(id, { include: [ { model: School, as: 'school', attributes: ['school_name', 'school_code', 'address', 'city', 'district'], }, ], }); if (!classData) { return res.status(404).json({ success: false, message: 'Class not found', }); } // Query students const { count, rows: students } = await StudentDetail.findAndCountAll({ where: { current_class_id: id, status: status, }, include: [ { model: UserProfile, as: 'profile', attributes: [ 'full_name', 'first_name', 'last_name', 'date_of_birth', 'phone', 'address', 'city', 'district', ], include: [ { model: UsersAuth, as: 'auth', attributes: ['username', 'email', 'is_active'], }, { model: School, as: 'school', attributes: ['school_name', 'school_code'], }, ], }, ], limit: parseInt(limit), offset: parseInt(offset), order: [[{ model: UserProfile, as: 'profile' }, 'full_name', 'ASC']], }); const result = { class: { id: classData.id, class_name: classData.class_name, class_code: classData.class_code, grade_level: classData.grade_level, max_students: classData.max_students, current_students: classData.current_students, school: classData.school ? { school_name: classData.school.school_name, school_code: classData.school.school_code, address: classData.school.address, city: classData.school.city, district: classData.school.district, } : null, }, students: students, pagination: { total: count, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(count / limit), }, }; // Cache for 10 minutes await cacheUtils.set(cacheKey, result, 600); res.json({ success: true, data: result, cached: false, }); } catch (error) { next(error); } } } module.exports = new ClassController();