update
This commit is contained in:
364
controllers/classController.js
Normal file
364
controllers/classController.js
Normal file
@@ -0,0 +1,364 @@
|
||||
const { Class } = require('../models');
|
||||
const { StudentDetail, UserProfile, UsersAuth, School } = require('../models');
|
||||
const { cacheUtils } = require('../config/redis');
|
||||
const { addDatabaseWriteJob } = require('../config/bullmq');
|
||||
|
||||
/**
|
||||
* 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 via BullMQ)
|
||||
*/
|
||||
async createClass(req, res, next) {
|
||||
try {
|
||||
const classData = req.body;
|
||||
|
||||
// Add to job queue for async processing
|
||||
const job = await addDatabaseWriteJob('create', 'Class', classData);
|
||||
|
||||
// Invalidate related caches
|
||||
await cacheUtils.deletePattern('classes:list:*');
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Class creation job queued',
|
||||
jobId: job.id,
|
||||
data: classData,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update class (async via BullMQ)
|
||||
*/
|
||||
async updateClass(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updates = req.body;
|
||||
|
||||
// Add to job queue for async processing
|
||||
const job = await addDatabaseWriteJob('update', 'Class', {
|
||||
id,
|
||||
updates,
|
||||
});
|
||||
|
||||
// Invalidate related caches
|
||||
await cacheUtils.delete(`class:${id}`);
|
||||
await cacheUtils.deletePattern('classes:list:*');
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Class update job queued',
|
||||
jobId: job.id,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete class (async via BullMQ)
|
||||
*/
|
||||
async deleteClass(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Add to job queue for async processing
|
||||
const job = await addDatabaseWriteJob('delete', 'Class', { id });
|
||||
|
||||
// Invalidate related caches
|
||||
await cacheUtils.delete(`class:${id}`);
|
||||
await cacheUtils.deletePattern('classes:list:*');
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Class deletion job queued',
|
||||
jobId: job.id,
|
||||
});
|
||||
} 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();
|
||||
Reference in New Issue
Block a user