update
All checks were successful
Deploy to Production / deploy (push) Successful in 21s

This commit is contained in:
silverpro89
2026-01-28 11:21:21 +07:00
parent 57c45d27a3
commit 3791b7cae1
23 changed files with 1033 additions and 317 deletions

View File

@@ -1,6 +1,6 @@
const { AcademicYear } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
/**
* Academic Year Controller - Quản lý năm học
@@ -146,24 +146,23 @@ class AcademicYearController {
}
/**
* Create new academic year (async via BullMQ)
* Create new academic year
*/
async createAcademicYear(req, res, next) {
try {
const academicYearData = req.body;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('create', 'AcademicYear', academicYearData);
// Create academic year directly
const academicYear = await AcademicYear.create(academicYearData);
// Invalidate related caches
await cacheUtils.deletePattern('academic_years:list:*');
await cacheUtils.delete('academic_year:current');
res.status(202).json({
res.status(201).json({
success: true,
message: 'Academic year creation job queued',
jobId: job.id,
data: academicYearData,
message: 'Academic year created successfully',
data: academicYear,
});
} catch (error) {
next(error);
@@ -171,28 +170,34 @@ class AcademicYearController {
}
/**
* Update academic year (async via BullMQ)
* Update academic year
*/
async updateAcademicYear(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('update', 'AcademicYear', {
id,
updates,
});
const academicYear = await AcademicYear.findByPk(id);
if (!academicYear) {
return res.status(404).json({
success: false,
message: 'Academic year not found',
});
}
// Update academic year directly
await academicYear.update(updates);
// Invalidate related caches
await cacheUtils.delete(`academic_year:${id}`);
await cacheUtils.deletePattern('academic_years:list:*');
await cacheUtils.delete('academic_year:current');
res.status(202).json({
res.json({
success: true,
message: 'Academic year update job queued',
jobId: job.id,
message: 'Academic year updated successfully',
data: academicYear,
});
} catch (error) {
next(error);
@@ -200,24 +205,32 @@ class AcademicYearController {
}
/**
* Delete academic year (async via BullMQ)
* Delete academic year
*/
async deleteAcademicYear(req, res, next) {
try {
const { id } = req.params;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('delete', 'AcademicYear', { id });
const academicYear = await AcademicYear.findByPk(id);
if (!academicYear) {
return res.status(404).json({
success: false,
message: 'Academic year not found',
});
}
// Delete academic year directly
await academicYear.destroy();
// Invalidate related caches
await cacheUtils.delete(`academic_year:${id}`);
await cacheUtils.deletePattern('academic_years:list:*');
await cacheUtils.delete('academic_year:current');
res.status(202).json({
res.json({
success: true,
message: 'Academic year deletion job queued',
jobId: job.id,
message: 'Academic year deleted successfully',
});
} catch (error) {
next(error);
@@ -231,20 +244,31 @@ class AcademicYearController {
try {
const { id } = req.params;
// Add to job queue to update current year
const job = await addDatabaseWriteJob('update', 'AcademicYear', {
id,
updates: { is_current: true },
// Will also set all other years to is_current: false
});
const academicYear = await AcademicYear.findByPk(id);
if (!academicYear) {
return res.status(404).json({
success: false,
message: 'Academic year not found',
});
}
// Set all other years to not current
await AcademicYear.update(
{ is_current: false },
{ where: {} }
);
// Set this year as current
await academicYear.update({ is_current: true });
// Invalidate all academic year caches
await cacheUtils.deletePattern('academic_year*');
res.status(202).json({
res.json({
success: true,
message: 'Set current academic year job queued',
jobId: job.id,
message: 'Academic year set as current successfully',
data: academicYear,
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,6 @@
const { AttendanceLog, AttendanceDaily, UsersAuth, School } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob, addAttendanceProcessJob } = require('../config/bullmq');
const { addAttendanceProcessJob } = require('../config/bullmq');
/**
* Attendance Controller - Quản lý điểm danh
@@ -82,15 +82,15 @@ class AttendanceController {
try {
const logData = req.body;
const job = await addDatabaseWriteJob('create', 'AttendanceLog', logData);
const newLog = await AttendanceLog.create(logData);
await cacheUtils.deletePattern('attendance:logs:*');
await cacheUtils.deletePattern('attendance:daily:*');
res.status(202).json({
res.status(201).json({
success: true,
message: 'Attendance log created',
jobId: job.id,
message: 'Attendance log created successfully',
data: newLog,
});
} catch (error) {
next(error);

View File

@@ -1,7 +1,6 @@
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
@@ -107,22 +106,55 @@ class ClassController {
}
/**
* Create new class (async via BullMQ)
* Create new class
*/
async createClass(req, res, next) {
try {
const classData = req.body;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('create', 'Class', classData);
// Create class directly
const newClass = await Class.create(classData);
// Invalidate related caches
await cacheUtils.deletePattern('classes:list:*');
res.status(202).json({
res.status(201).json({
success: true,
message: 'Class creation job queued',
jobId: job.id,
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) {
@@ -131,51 +163,31 @@ class ClassController {
}
/**
* 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)
* Delete class
*/
async deleteClass(req, res, next) {
try {
const { id } = req.params;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('delete', 'Class', { id });
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.status(202).json({
res.json({
success: true,
message: 'Class deletion job queued',
jobId: job.id,
message: 'Class deleted successfully',
});
} catch (error) {
next(error);

View File

@@ -0,0 +1,343 @@
const { GameType } = require('../models');
const { cacheUtils } = require('../config/redis');
/**
* GameType Controller - Quản lý loại trò chơi giáo dục
*/
class GameTypeController {
/**
* Get all game types with pagination
*/
async getAllGameTypes(req, res, next) {
try {
const { page = 1, limit = 20, is_active, is_premium, difficulty_level } = req.query;
const offset = (page - 1) * limit;
const cacheKey = `game_types:list:${page}:${limit}:${is_active || 'all'}:${is_premium || 'all'}:${difficulty_level || 'all'}`;
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const where = {};
if (is_active !== undefined) where.is_active = is_active === 'true';
if (is_premium !== undefined) where.is_premium = is_premium === 'true';
if (difficulty_level) where.difficulty_level = difficulty_level;
const { count, rows } = await GameType.findAndCountAll({
where,
limit: parseInt(limit),
offset: parseInt(offset),
order: [['display_order', 'ASC'], ['rating', 'DESC']],
});
const result = {
gameTypes: rows,
pagination: {
total: count,
page: parseInt(page),
limit: parseInt(limit),
totalPages: Math.ceil(count / limit),
},
};
await cacheUtils.set(cacheKey, result, 1800);
res.json({
success: true,
data: result,
cached: false,
});
} catch (error) {
next(error);
}
}
/**
* Get game type by ID
*/
async getGameTypeById(req, res, next) {
try {
const { id } = req.params;
const cacheKey = `game_type:${id}`;
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const gameType = await GameType.findByPk(id);
if (!gameType) {
return res.status(404).json({
success: false,
error: 'Game type not found',
});
}
await cacheUtils.set(cacheKey, gameType, 3600);
res.json({
success: true,
data: gameType,
cached: false,
});
} catch (error) {
next(error);
}
}
/**
* Get game type by type code
*/
async getGameTypeByType(req, res, next) {
try {
const { type } = req.params;
const cacheKey = `game_type:type:${type}`;
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const gameType = await GameType.findOne({
where: { type },
});
if (!gameType) {
return res.status(404).json({
success: false,
error: 'Game type not found',
});
}
await cacheUtils.set(cacheKey, gameType, 3600);
res.json({
success: true,
data: gameType,
cached: false,
});
} catch (error) {
next(error);
}
}
/**
* Create new game type
*/
async createGameType(req, res, next) {
try {
const gameTypeData = req.body;
// Check if type already exists
const existingType = await GameType.findOne({
where: { type: gameTypeData.type },
});
if (existingType) {
return res.status(400).json({
success: false,
error: 'Game type with this type code already exists',
});
}
const gameType = await GameType.create(gameTypeData);
// Invalidate cache
await cacheUtils.deletePattern('game_types:*');
res.status(201).json({
success: true,
data: gameType,
message: 'Game type created successfully',
});
} catch (error) {
next(error);
}
}
/**
* Update game type
*/
async updateGameType(req, res, next) {
try {
const { id } = req.params;
const updateData = req.body;
const gameType = await GameType.findByPk(id);
if (!gameType) {
return res.status(404).json({
success: false,
error: 'Game type not found',
});
}
// If updating type code, check uniqueness
if (updateData.type && updateData.type !== gameType.type) {
const existingType = await GameType.findOne({
where: { type: updateData.type },
});
if (existingType) {
return res.status(400).json({
success: false,
error: 'Game type with this type code already exists',
});
}
}
await gameType.update(updateData);
// Invalidate cache
await cacheUtils.del(`game_type:${id}`);
await cacheUtils.del(`game_type:type:${gameType.type}`);
await cacheUtils.deletePattern('game_types:*');
res.json({
success: true,
data: gameType,
message: 'Game type updated successfully',
});
} catch (error) {
next(error);
}
}
/**
* Delete game type
*/
async deleteGameType(req, res, next) {
try {
const { id } = req.params;
const gameType = await GameType.findByPk(id);
if (!gameType) {
return res.status(404).json({
success: false,
error: 'Game type not found',
});
}
const typeCode = gameType.type;
await gameType.destroy();
// Invalidate cache
await cacheUtils.del(`game_type:${id}`);
await cacheUtils.del(`game_type:type:${typeCode}`);
await cacheUtils.deletePattern('game_types:*');
res.json({
success: true,
message: 'Game type deleted successfully',
});
} catch (error) {
next(error);
}
}
/**
* Get active game types only
*/
async getActiveGameTypes(req, res, next) {
try {
const cacheKey = 'game_types:active';
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const gameTypes = await GameType.findAll({
where: { is_active: true },
order: [['display_order', 'ASC'], ['rating', 'DESC']],
});
await cacheUtils.set(cacheKey, gameTypes, 3600);
res.json({
success: true,
data: gameTypes,
cached: false,
});
} catch (error) {
next(error);
}
}
/**
* Get game types statistics
*/
async getGameTypeStats(req, res, next) {
try {
const cacheKey = 'game_types:stats';
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const [total, active, premium, byDifficulty] = await Promise.all([
GameType.count(),
GameType.count({ where: { is_active: true } }),
GameType.count({ where: { is_premium: true } }),
GameType.findAll({
attributes: [
'difficulty_level',
[GameType.sequelize.fn('COUNT', GameType.sequelize.col('id')), 'count'],
],
group: ['difficulty_level'],
}),
]);
const stats = {
total,
active,
premium,
inactive: total - active,
byDifficulty: byDifficulty.reduce((acc, item) => {
if (item.difficulty_level) {
acc[item.difficulty_level] = parseInt(item.dataValues.count);
}
return acc;
}, {}),
};
await cacheUtils.set(cacheKey, stats, 1800);
res.json({
success: true,
data: stats,
cached: false,
});
} catch (error) {
next(error);
}
}
}
module.exports = new GameTypeController();

View File

@@ -1,6 +1,6 @@
const { Grade, GradeItem, GradeSummary, StudentDetail, Subject, AcademicYear } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob, addGradeCalculationJob } = require('../config/bullmq');
const { addGradeCalculationJob } = require('../config/bullmq');
/**
* Grade Controller - Quản lý điểm số
@@ -121,22 +121,21 @@ class GradeController {
}
/**
* Create new grade (async via BullMQ)
* Create new grade
*/
async createGrade(req, res, next) {
try {
const gradeData = req.body;
const job = await addDatabaseWriteJob('create', 'Grade', gradeData);
const grade = await Grade.create(gradeData);
await cacheUtils.deletePattern('grades:list:*');
await cacheUtils.deletePattern(`grade:student:${gradeData.student_id}:*`);
res.status(202).json({
res.status(201).json({
success: true,
message: 'Grade creation job queued',
jobId: job.id,
data: gradeData,
message: 'Grade created successfully',
data: grade,
});
} catch (error) {
next(error);
@@ -144,25 +143,31 @@ class GradeController {
}
/**
* Update grade (async via BullMQ)
* Update grade
*/
async updateGrade(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
const job = await addDatabaseWriteJob('update', 'Grade', {
id,
updates,
});
const grade = await Grade.findByPk(id);
if (!grade) {
return res.status(404).json({
success: false,
message: 'Grade not found',
});
}
await grade.update(updates);
await cacheUtils.delete(`grade:${id}`);
await cacheUtils.deletePattern('grades:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'Grade update job queued',
jobId: job.id,
message: 'Grade updated successfully',
data: grade,
});
} catch (error) {
next(error);
@@ -170,21 +175,29 @@ class GradeController {
}
/**
* Delete grade (async via BullMQ)
* Delete grade
*/
async deleteGrade(req, res, next) {
try {
const { id } = req.params;
const job = await addDatabaseWriteJob('delete', 'Grade', { id });
const grade = await Grade.findByPk(id);
if (!grade) {
return res.status(404).json({
success: false,
message: 'Grade not found',
});
}
await grade.destroy();
await cacheUtils.delete(`grade:${id}`);
await cacheUtils.deletePattern('grades:list:*');
res.status(202).json({
res.json({
success: true,
message: 'Grade deletion job queued',
jobId: job.id,
message: 'Grade deleted successfully',
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,6 @@
const { ParentAssignedTask, GradeItem, StudentDetail, UsersAuth, ParentStudentMap } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob, addNotificationJob } = require('../config/bullmq');
const { addNotificationJob } = require('../config/bullmq');
const { v4: uuidv4 } = require('uuid');
/**
@@ -64,8 +64,8 @@ class ParentTaskController {
priority: priority || 'normal',
};
// Async write to DB via BullMQ
await addDatabaseWriteJob('create', 'ParentAssignedTask', taskData);
// Create task directly in database
const task = await ParentAssignedTask.create(taskData);
// Gửi notification cho học sinh
await addNotificationJob({
@@ -80,10 +80,10 @@ class ParentTaskController {
await cacheUtils.del(`parent:tasks:${parent_id}`);
await cacheUtils.del(`student:tasks:${student_id}`);
res.status(202).json({
res.status(201).json({
success: true,
message: 'Task assignment request queued',
data: taskData,
message: 'Task assigned successfully',
data: task,
});
} catch (error) {
next(error);
@@ -138,11 +138,7 @@ class ParentTaskController {
for (const task of tasks) {
if (task.status !== 'completed' && new Date(task.due_date) < now) {
if (task.status !== 'overdue') {
await addDatabaseWriteJob('update', 'ParentAssignedTask', {
id: task.id,
status: 'overdue',
});
task.status = 'overdue';
await task.update({ status: 'overdue' });
}
}
}
@@ -235,8 +231,7 @@ class ParentTaskController {
});
}
await addDatabaseWriteJob('update', 'ParentAssignedTask', {
id,
await task.update({
status: 'completed',
completion_date: new Date(),
student_notes: student_notes || null,
@@ -255,9 +250,10 @@ class ParentTaskController {
await cacheUtils.del(`parent:tasks:${task.parent_id}:*`);
await cacheUtils.del(`student:tasks:${task.student_id}:*`);
res.status(202).json({
res.status(200).json({
success: true,
message: 'Task completion request queued',
message: 'Task completed successfully',
data: task,
});
} catch (error) {
next(error);
@@ -288,18 +284,16 @@ class ParentTaskController {
});
}
await addDatabaseWriteJob('update', 'ParentAssignedTask', {
id,
status,
});
await task.update({ status });
// Clear cache
await cacheUtils.del(`parent:tasks:${task.parent_id}:*`);
await cacheUtils.del(`student:tasks:${task.student_id}:*`);
res.status(202).json({
res.status(200).json({
success: true,
message: 'Task status update queued',
message: 'Task status updated successfully',
data: task,
});
} catch (error) {
next(error);
@@ -330,15 +324,15 @@ class ParentTaskController {
});
}
await addDatabaseWriteJob('delete', 'ParentAssignedTask', { id });
await task.destroy();
// Clear cache
await cacheUtils.del(`parent:tasks:${task.parent_id}:*`);
await cacheUtils.del(`student:tasks:${task.student_id}:*`);
res.status(202).json({
res.status(200).json({
success: true,
message: 'Task deletion request queued',
message: 'Task deleted successfully',
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
const { Room, School } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
/**
* Room Controller - Quản lý phòng học
@@ -97,21 +96,20 @@ class RoomController {
}
/**
* Create new room (async via BullMQ)
* Create new room
*/
async createRoom(req, res, next) {
try {
const roomData = req.body;
const job = await addDatabaseWriteJob('create', 'Room', roomData);
const room = await Room.create(roomData);
await cacheUtils.deletePattern('rooms:list:*');
res.status(202).json({
res.status(201).json({
success: true,
message: 'Room creation job queued',
jobId: job.id,
data: roomData,
message: 'Room created successfully',
data: room,
});
} catch (error) {
next(error);
@@ -119,25 +117,31 @@ class RoomController {
}
/**
* Update room (async via BullMQ)
* Update room
*/
async updateRoom(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
const job = await addDatabaseWriteJob('update', 'Room', {
id,
updates,
});
const room = await Room.findByPk(id);
if (!room) {
return res.status(404).json({
success: false,
message: 'Room not found',
});
}
await room.update(updates);
await cacheUtils.delete(`room:${id}`);
await cacheUtils.deletePattern('rooms:list:*');
res.status(202).json({
res.json({
success: true,
message: 'Room update job queued',
jobId: job.id,
message: 'Room updated successfully',
data: room,
});
} catch (error) {
next(error);
@@ -145,21 +149,29 @@ class RoomController {
}
/**
* Delete room (async via BullMQ)
* Delete room
*/
async deleteRoom(req, res, next) {
try {
const { id } = req.params;
const job = await addDatabaseWriteJob('delete', 'Room', { id });
const room = await Room.findByPk(id);
if (!room) {
return res.status(404).json({
success: false,
message: 'Room not found',
});
}
await room.destroy();
await cacheUtils.delete(`room:${id}`);
await cacheUtils.deletePattern('rooms:list:*');
res.status(202).json({
res.json({
success: true,
message: 'Room deletion job queued',
jobId: job.id,
message: 'Room deleted successfully',
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
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
@@ -108,12 +107,11 @@ class SchoolController {
}
/**
* Create new school - Push to BullMQ
* Create new school
*/
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) {
@@ -123,22 +121,14 @@ class SchoolController {
});
}
// Add job to BullMQ queue
const job = await addDatabaseWriteJob(
'create',
'School',
schoolData,
{ userId, priority: 3 }
);
const newSchool = await School.create(schoolData);
res.status(202).json({
await cacheUtils.deletePattern('schools:list:*');
res.status(201).json({
success: true,
message: 'School creation queued',
jobId: job.id,
data: {
school_code: schoolData.school_code,
school_name: schoolData.school_name,
},
message: 'School created successfully',
data: newSchool,
});
} catch (error) {
next(error);
@@ -146,44 +136,31 @@ class SchoolController {
}
/**
* Update school - Push to BullMQ
* Update school
*/
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);
const school = await School.findByPk(id);
if (!school) {
school = await School.findByPk(id);
if (!school) {
return res.status(404).json({
success: false,
message: 'School not found',
});
}
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 }
);
await school.update(updateData);
// Invalidate cache
await cacheUtils.delete(cacheKey);
await cacheUtils.delete(`school:${id}`);
await cacheUtils.deletePattern('schools:list:*');
res.json({
res.status(200).json({
success: true,
message: 'School update queued',
jobId: job.id,
message: 'School updated successfully',
data: school,
});
} catch (error) {
next(error);
@@ -191,12 +168,11 @@ class SchoolController {
}
/**
* Delete school - Push to BullMQ
* Delete school (soft delete)
*/
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);
@@ -208,21 +184,16 @@ class SchoolController {
}
// Soft delete - just mark as inactive
const job = await addDatabaseWriteJob(
'update',
'School',
{ id, is_active: false },
{ userId, priority: 3 }
);
await school.update({ is_active: false });
// Invalidate cache
await cacheUtils.delete(`school:${id}`);
await cacheUtils.deletePattern('schools:list:*');
res.json({
res.status(200).json({
success: true,
message: 'School deletion queued',
jobId: job.id,
message: 'School deactivated successfully',
data: school,
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
const { StudentDetail, UserProfile, UsersAuth, Class } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
/**
* Student Controller - Quản lý học sinh
@@ -120,21 +119,20 @@ class StudentController {
}
/**
* Create new student (async via BullMQ)
* Create new student
*/
async createStudent(req, res, next) {
try {
const studentData = req.body;
const job = await addDatabaseWriteJob('create', 'StudentDetail', studentData);
const newStudent = await StudentDetail.create(studentData);
await cacheUtils.deletePattern('students:list:*');
res.status(202).json({
res.status(201).json({
success: true,
message: 'Student creation job queued',
jobId: job.id,
data: studentData,
message: 'Student created successfully',
data: newStudent,
});
} catch (error) {
next(error);
@@ -142,25 +140,30 @@ class StudentController {
}
/**
* Update student (async via BullMQ)
* Update student
*/
async updateStudent(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
const job = await addDatabaseWriteJob('update', 'StudentDetail', {
id,
updates,
});
const student = await StudentDetail.findByPk(id);
if (!student) {
return res.status(404).json({
success: false,
message: 'Student not found',
});
}
await student.update(updates);
await cacheUtils.delete(`student:${id}`);
await cacheUtils.deletePattern('students:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'Student update job queued',
jobId: job.id,
message: 'Student updated successfully',
data: student,
});
} catch (error) {
next(error);
@@ -174,18 +177,23 @@ class StudentController {
try {
const { id } = req.params;
const job = await addDatabaseWriteJob('update', 'StudentDetail', {
id,
updates: { status: 'dropped' },
});
const student = await StudentDetail.findByPk(id);
if (!student) {
return res.status(404).json({
success: false,
message: 'Student not found',
});
}
await student.update({ status: 'dropped' });
await cacheUtils.delete(`student:${id}`);
await cacheUtils.deletePattern('students:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'Student deletion job queued',
jobId: job.id,
message: 'Student status updated to dropped',
data: student,
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
const { Subject, Chapter } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
/**
* Subject Controller - Quản lý môn học
@@ -147,22 +146,20 @@ class SubjectController {
}
/**
* Create new subject (async via BullMQ)
* Create new subject
*/
async createSubject(req, res, next) {
try {
const subjectData = req.body;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('create', 'Subject', subjectData);
const subject = await Subject.create(subjectData);
// Note: Cache will be invalidated by worker after successful creation
await cacheUtils.deletePattern('subjects:list:*');
res.status(202).json({
res.status(201).json({
success: true,
message: 'Subject creation job queued',
jobId: job.id,
data: subjectData,
message: 'Subject created successfully',
data: subject,
});
} catch (error) {
next(error);
@@ -170,25 +167,31 @@ class SubjectController {
}
/**
* Update subject (async via BullMQ)
* Update subject
*/
async updateSubject(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('update', 'Subject', {
id,
updates,
});
const subject = await Subject.findByPk(id);
// Note: Cache will be invalidated by worker after successful update
if (!subject) {
return res.status(404).json({
success: false,
message: 'Subject not found',
});
}
res.status(202).json({
await subject.update(updates);
await cacheUtils.delete(`subject:${id}`);
await cacheUtils.deletePattern('subjects:list:*');
res.json({
success: true,
message: 'Subject update job queued',
jobId: job.id,
message: 'Subject updated successfully',
data: subject,
});
} catch (error) {
next(error);
@@ -196,21 +199,29 @@ class SubjectController {
}
/**
* Delete subject (async via BullMQ)
* Delete subject
*/
async deleteSubject(req, res, next) {
try {
const { id } = req.params;
// Add to job queue for async processing
const job = await addDatabaseWriteJob('delete', 'Subject', { id });
const subject = await Subject.findByPk(id);
// Note: Cache will be invalidated by worker after successful deletion
if (!subject) {
return res.status(404).json({
success: false,
message: 'Subject not found',
});
}
res.status(202).json({
await subject.destroy();
await cacheUtils.delete(`subject:${id}`);
await cacheUtils.deletePattern('subjects:list:*');
res.json({
success: true,
message: 'Subject deletion job queued',
jobId: job.id,
message: 'Subject deleted successfully',
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
const { SubscriptionPlan, UserSubscription, UsersAuth } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
const { v4: uuidv4 } = require('uuid');
/**
@@ -98,16 +97,15 @@ class SubscriptionController {
auto_renew: req.body.auto_renew || false,
};
// Async write to DB via BullMQ
await addDatabaseWriteJob('create', 'UserSubscription', subscriptionData);
const newSubscription = await UserSubscription.create(subscriptionData);
// Clear cache
await cacheUtils.del(`subscription:user:${user_id}`);
res.status(202).json({
res.status(201).json({
success: true,
message: 'Subscription purchase request queued',
data: subscriptionData,
message: 'Subscription purchased successfully',
data: newSubscription,
});
} catch (error) {
next(error);
@@ -153,11 +151,7 @@ class SubscriptionController {
// Check nếu subscription đã hết hạn
const now = new Date();
if (subscription.status === 'active' && new Date(subscription.end_date) < now) {
subscription.status = 'expired';
await addDatabaseWriteJob('update', 'UserSubscription', {
id: subscription.id,
status: 'expired',
});
await subscription.update({ status: 'expired' });
}
await cacheUtils.set(cacheKey, subscription, 900); // Cache 15 min
@@ -195,9 +189,8 @@ class SubscriptionController {
});
}
// Async update via BullMQ
await addDatabaseWriteJob('update', 'UserSubscription', {
id,
// Update subscription
await subscription.update({
status: 'cancelled',
auto_renew: false,
});
@@ -205,9 +198,10 @@ class SubscriptionController {
// Clear cache
await cacheUtils.del(`subscription:user:${subscription.user_id}`);
res.status(202).json({
res.status(200).json({
success: true,
message: 'Subscription cancellation request queued',
message: 'Subscription cancelled successfully',
data: subscription,
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
const { TeacherDetail, UserProfile, UsersAuth } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
const teacherProfileService = require('../services/teacherProfileService');
/**
@@ -205,25 +204,30 @@ class TeacherController {
}
/**
* Update teacher (async via BullMQ)
* Update teacher
*/
async updateTeacher(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
const job = await addDatabaseWriteJob('update', 'TeacherDetail', {
id,
updates,
});
const teacher = await TeacherDetail.findByPk(id);
if (!teacher) {
return res.status(404).json({
success: false,
message: 'Teacher not found',
});
}
await teacher.update(updates);
await cacheUtils.delete(`teacher:${id}`);
await cacheUtils.deletePattern('teachers:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'Teacher update job queued',
jobId: job.id,
message: 'Teacher updated successfully',
data: teacher,
});
} catch (error) {
next(error);
@@ -237,17 +241,28 @@ class TeacherController {
try {
const { id } = req.params;
const job = await addDatabaseWriteJob('update', 'TeacherDetail', {
id,
updates: { status: 'resigned' },
});
const teacher = await TeacherDetail.findByPk(id);
if (!teacher) {
return res.status(404).json({
success: false,
message: 'Teacher not found',
});
}
await teacher.update({ status: 'resigned' });
await cacheUtils.delete(`teacher:${id}`);
await cacheUtils.deletePattern('teachers:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'Teacher deletion job queued',
message: 'Teacher status updated to resigned',
data: teacher,
});
} catch (error) {
next(error);
}
}
jobId: job.id,
});
} catch (error) {

View File

@@ -1,6 +1,5 @@
const { StaffTrainingAssignment, StaffAchievement, UsersAuth, Subject } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
const { v4: uuidv4 } = require('uuid');
/**
@@ -55,11 +54,7 @@ class TrainingController {
for (const assignment of assignments) {
if (assignment.status !== 'completed' && new Date(assignment.deadline) < now) {
if (assignment.status !== 'overdue') {
await addDatabaseWriteJob('update', 'StaffTrainingAssignment', {
id: assignment.id,
status: 'overdue',
});
assignment.status = 'overdue';
await assignment.update({ status: 'overdue' });
}
}
}
@@ -102,16 +97,15 @@ class TrainingController {
notes: notes || null,
};
// Async write to DB via BullMQ
await addDatabaseWriteJob('create', 'StaffTrainingAssignment', assignmentData);
const newAssignment = await StaffTrainingAssignment.create(assignmentData);
// Clear cache
await cacheUtils.del(`training:assignments:${staff_id}:*`);
res.status(202).json({
res.status(201).json({
success: true,
message: 'Training assignment request queued',
data: assignmentData,
message: 'Training assignment created successfully',
data: newAssignment,
});
} catch (error) {
next(error);
@@ -142,17 +136,15 @@ class TrainingController {
});
}
await addDatabaseWriteJob('update', 'StaffTrainingAssignment', {
id,
status,
});
await assignment.update({ status });
// Clear cache
await cacheUtils.del(`training:assignments:${assignment.staff_id}:*`);
res.status(202).json({
res.status(200).json({
success: true,
message: 'Assignment status update queued',
message: 'Assignment status updated successfully',
data: assignment,
});
} catch (error) {
next(error);
@@ -199,16 +191,15 @@ class TrainingController {
verified_at: verified_by ? new Date() : null,
};
// Async write to DB via BullMQ
await addDatabaseWriteJob('create', 'StaffAchievement', achievementData);
const newAchievement = await StaffAchievement.create(achievementData);
// Clear cache
await cacheUtils.del(`training:achievements:${staff_id}`);
res.status(202).json({
res.status(201).json({
success: true,
message: 'Achievement creation request queued',
data: achievementData,
message: 'Achievement created successfully',
data: newAchievement,
});
} catch (error) {
next(error);

View File

@@ -1,6 +1,5 @@
const { UsersAuth, UserProfile } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
@@ -404,21 +403,20 @@ class UserController {
}
/**
* Create new user (async via BullMQ)
* Create new user
*/
async createUser(req, res, next) {
try {
const userData = req.body;
const job = await addDatabaseWriteJob('create', 'UsersAuth', userData);
const newUser = await UsersAuth.create(userData);
await cacheUtils.deletePattern('users:list:*');
res.status(202).json({
res.status(201).json({
success: true,
message: 'User creation job queued',
jobId: job.id,
data: userData,
message: 'User created successfully',
data: newUser,
});
} catch (error) {
next(error);
@@ -426,25 +424,30 @@ class UserController {
}
/**
* Update user (async via BullMQ)
* Update user
*/
async updateUser(req, res, next) {
try {
const { id } = req.params;
const updates = req.body;
const job = await addDatabaseWriteJob('update', 'UsersAuth', {
id,
updates,
});
const user = await UsersAuth.findByPk(id);
if (!user) {
return res.status(404).json({
success: false,
message: 'User not found',
});
}
await user.update(updates);
await cacheUtils.delete(`user:${id}`);
await cacheUtils.deletePattern('users:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'User update job queued',
jobId: job.id,
message: 'User updated successfully',
data: user,
});
} catch (error) {
next(error);
@@ -458,18 +461,23 @@ class UserController {
try {
const { id } = req.params;
const job = await addDatabaseWriteJob('update', 'UsersAuth', {
id,
updates: { is_active: false },
});
const user = await UsersAuth.findByPk(id);
if (!user) {
return res.status(404).json({
success: false,
message: 'User not found',
});
}
await user.update({ is_active: false });
await cacheUtils.delete(`user:${id}`);
await cacheUtils.deletePattern('users:list:*');
res.status(202).json({
res.status(200).json({
success: true,
message: 'User deletion job queued',
jobId: job.id,
message: 'User deactivated successfully',
data: user,
});
} catch (error) {
next(error);