This commit is contained in:
Ken
2026-01-19 09:33:35 +07:00
parent 374dc12b2d
commit 70838a4bc1
103 changed files with 16929 additions and 2 deletions

View File

@@ -0,0 +1,302 @@
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();