const { School } = require('../models'); const { cacheUtils } = require('../config/redis'); const { addDatabaseWriteJob } = require('../config/bullmq'); /** * School Controller - Quản lý thông tin trường học */ class SchoolController { /** * Get all schools with pagination and caching */ async getAllSchools(req, res, next) { try { const { page = 1, limit = 20, type, city, is_active } = req.query; const offset = (page - 1) * limit; // Generate cache key const cacheKey = `schools:list:${page}:${limit}:${type || 'all'}:${city || 'all'}:${is_active || '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 (type) where.school_type = type; if (city) where.city = city; if (is_active !== undefined) { where.is_active = is_active === 'true' || is_active === true; } // Query from database (through ProxySQL) const { count, rows } = await School.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['school_name', 'ASC']], attributes: { exclude: ['config'] }, // Exclude sensitive data }); const result = { schools: rows, pagination: { total: count, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(count / limit), }, }; // Cache the result await cacheUtils.set(cacheKey, result, 3600); // Cache for 1 hour res.json({ success: true, data: result, cached: false, }); } catch (error) { next(error); } } /** * Get school by ID with caching */ async getSchoolById(req, res, next) { try { const { id } = req.params; const cacheKey = `school:${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 school = await School.findByPk(id); if (!school) { return res.status(404).json({ success: false, message: 'School not found', }); } // Cache the result await cacheUtils.set(cacheKey, school, 7200); // Cache for 2 hours res.json({ success: true, data: school, cached: false, }); } catch (error) { next(error); } } /** * Create new school - Push to BullMQ */ async createSchool(req, res, next) { try { const schoolData = req.body; const userId = req.user?.id; // From auth middleware // Validate required fields (you can use Joi for this) if (!schoolData.school_code || !schoolData.school_name || !schoolData.school_type) { return res.status(400).json({ success: false, message: 'Missing required fields', }); } // Add job to BullMQ queue const job = await addDatabaseWriteJob( 'create', 'School', schoolData, { userId, priority: 3 } ); res.status(202).json({ success: true, message: 'School creation queued', jobId: job.id, data: { school_code: schoolData.school_code, school_name: schoolData.school_name, }, }); } catch (error) { next(error); } } /** * Update school - Push to BullMQ */ async updateSchool(req, res, next) { try { const { id } = req.params; const updateData = req.body; const userId = req.user?.id; // Check if school exists (from cache or DB) const cacheKey = `school:${id}`; let school = await cacheUtils.get(cacheKey); if (!school) { school = await School.findByPk(id); if (!school) { return res.status(404).json({ success: false, message: 'School not found', }); } } // Add update job to BullMQ const job = await addDatabaseWriteJob( 'update', 'School', { id, ...updateData }, { userId, priority: 3 } ); // Invalidate cache await cacheUtils.delete(cacheKey); await cacheUtils.deletePattern('schools:list:*'); res.json({ success: true, message: 'School update queued', jobId: job.id, }); } catch (error) { next(error); } } /** * Delete school - Push to BullMQ */ async deleteSchool(req, res, next) { try { const { id } = req.params; const userId = req.user?.id; // Check if school exists const school = await School.findByPk(id); if (!school) { return res.status(404).json({ success: false, message: 'School not found', }); } // Soft delete - just mark as inactive const job = await addDatabaseWriteJob( 'update', 'School', { id, is_active: false }, { userId, priority: 3 } ); // Invalidate cache await cacheUtils.delete(`school:${id}`); await cacheUtils.deletePattern('schools:list:*'); res.json({ success: true, message: 'School deletion queued', jobId: job.id, }); } catch (error) { next(error); } } /** * Get school statistics */ async getSchoolStats(req, res, next) { try { const { id } = req.params; const cacheKey = `school:${id}:stats`; // Try cache first const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } // Get school with related data const school = await School.findByPk(id, { include: [ { association: 'classes', attributes: ['id'] }, { association: 'rooms', attributes: ['id'] }, ], }); if (!school) { return res.status(404).json({ success: false, message: 'School not found', }); } const stats = { school_id: id, school_name: school.school_name, total_classes: school.classes?.length || 0, total_rooms: school.rooms?.length || 0, capacity: school.capacity, }; // Cache for 30 minutes await cacheUtils.set(cacheKey, stats, 1800); res.json({ success: true, data: stats, cached: false, }); } catch (error) { next(error); } } /** * Get school datatypes */ async getSchoolDatatypes(req, res, next) { try { const datatypes = School.rawAttributes; res.json({ success: true, data: datatypes, }); } catch (error) { next(error); } } } module.exports = new SchoolController();