Files
sena_db_api_layer/controllers/classController.js
2026-01-19 09:33:35 +07:00

365 lines
9.0 KiB
JavaScript

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();