377 lines
9.1 KiB
JavaScript
377 lines
9.1 KiB
JavaScript
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();
|