update
This commit is contained in:
318
controllers/trainingController.js
Normal file
318
controllers/trainingController.js
Normal file
@@ -0,0 +1,318 @@
|
||||
const { StaffTrainingAssignment, StaffAchievement, UsersAuth, Subject } = require('../models');
|
||||
const { cacheUtils } = require('../config/redis');
|
||||
const { addDatabaseWriteJob } = require('../config/bullmq');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
/**
|
||||
* Training Controller - Quản lý đào tạo nhân sự
|
||||
*/
|
||||
class TrainingController {
|
||||
/**
|
||||
* GET /api/training/assignments/:staff_id - Danh sách khóa học bắt buộc của nhân viên
|
||||
*/
|
||||
async getStaffAssignments(req, res, next) {
|
||||
try {
|
||||
const { staff_id } = req.params;
|
||||
const { status, priority } = req.query;
|
||||
|
||||
const cacheKey = `training:assignments:${staff_id}:${status || 'all'}:${priority || 'all'}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const where = { staff_id };
|
||||
if (status) where.status = status;
|
||||
if (priority) where.priority = priority;
|
||||
|
||||
const assignments = await StaffTrainingAssignment.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: Subject,
|
||||
as: 'subject',
|
||||
attributes: ['subject_name', 'subject_code', 'description'],
|
||||
},
|
||||
{
|
||||
model: UsersAuth,
|
||||
as: 'assignedBy',
|
||||
attributes: ['username', 'full_name'],
|
||||
},
|
||||
],
|
||||
order: [
|
||||
['priority', 'DESC'],
|
||||
['deadline', 'ASC'],
|
||||
],
|
||||
});
|
||||
|
||||
// Check for overdue assignments
|
||||
const now = new Date();
|
||||
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 cacheUtils.set(cacheKey, assignments, 900); // Cache 15 min
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: assignments,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/training/assignments - Phân công đào tạo mới
|
||||
*/
|
||||
async assignTraining(req, res, next) {
|
||||
try {
|
||||
const { staff_id, course_name, subject_id, deadline, priority, notes, assigned_by } = req.body;
|
||||
|
||||
if (!staff_id || !course_name || !deadline || !assigned_by) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Missing required fields: staff_id, course_name, deadline, assigned_by',
|
||||
});
|
||||
}
|
||||
|
||||
const assignmentData = {
|
||||
id: uuidv4(),
|
||||
staff_id,
|
||||
course_name,
|
||||
subject_id: subject_id || null,
|
||||
assigned_by,
|
||||
assigned_date: new Date(),
|
||||
deadline: new Date(deadline),
|
||||
status: 'pending',
|
||||
priority: priority || 'normal',
|
||||
notes: notes || null,
|
||||
};
|
||||
|
||||
// Async write to DB via BullMQ
|
||||
await addDatabaseWriteJob('create', 'StaffTrainingAssignment', assignmentData);
|
||||
|
||||
// Clear cache
|
||||
await cacheUtils.del(`training:assignments:${staff_id}:*`);
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Training assignment request queued',
|
||||
data: assignmentData,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/training/assignments/:id/status - Cập nhật trạng thái assignment
|
||||
*/
|
||||
async updateAssignmentStatus(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
const validStatuses = ['pending', 'in_progress', 'completed', 'overdue'];
|
||||
if (!validStatuses.includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Invalid status. Must be one of: ${validStatuses.join(', ')}`,
|
||||
});
|
||||
}
|
||||
|
||||
const assignment = await StaffTrainingAssignment.findByPk(id);
|
||||
if (!assignment) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Assignment not found',
|
||||
});
|
||||
}
|
||||
|
||||
await addDatabaseWriteJob('update', 'StaffTrainingAssignment', {
|
||||
id,
|
||||
status,
|
||||
});
|
||||
|
||||
// Clear cache
|
||||
await cacheUtils.del(`training:assignments:${assignment.staff_id}:*`);
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Assignment status update queued',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/training/achievements - Ghi nhận hoàn thành khóa đào tạo
|
||||
*/
|
||||
async createAchievement(req, res, next) {
|
||||
try {
|
||||
const {
|
||||
staff_id,
|
||||
course_name,
|
||||
course_id,
|
||||
completion_date,
|
||||
certificate_url,
|
||||
certificate_code,
|
||||
type,
|
||||
score,
|
||||
total_hours,
|
||||
verified_by,
|
||||
} = req.body;
|
||||
|
||||
if (!staff_id || !course_name || !completion_date) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Missing required fields: staff_id, course_name, completion_date',
|
||||
});
|
||||
}
|
||||
|
||||
const achievementData = {
|
||||
id: uuidv4(),
|
||||
staff_id,
|
||||
course_name,
|
||||
course_id: course_id || null,
|
||||
completion_date: new Date(completion_date),
|
||||
certificate_url: certificate_url || null,
|
||||
certificate_code: certificate_code || null,
|
||||
type: type || 'mandatory',
|
||||
score: score || null,
|
||||
total_hours: total_hours || null,
|
||||
verified_by: verified_by || null,
|
||||
verified_at: verified_by ? new Date() : null,
|
||||
};
|
||||
|
||||
// Async write to DB via BullMQ
|
||||
await addDatabaseWriteJob('create', 'StaffAchievement', achievementData);
|
||||
|
||||
// Clear cache
|
||||
await cacheUtils.del(`training:achievements:${staff_id}`);
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Achievement creation request queued',
|
||||
data: achievementData,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/training/achievements/:staff_id - Danh sách chứng chỉ của nhân viên
|
||||
*/
|
||||
async getStaffAchievements(req, res, next) {
|
||||
try {
|
||||
const { staff_id } = req.params;
|
||||
const { type } = req.query;
|
||||
|
||||
const cacheKey = `training:achievements:${staff_id}:${type || 'all'}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const where = { staff_id };
|
||||
if (type) where.type = type;
|
||||
|
||||
const achievements = await StaffAchievement.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: Subject,
|
||||
as: 'course',
|
||||
attributes: ['subject_name', 'subject_code'],
|
||||
},
|
||||
{
|
||||
model: UsersAuth,
|
||||
as: 'verifiedBy',
|
||||
attributes: ['username', 'full_name'],
|
||||
},
|
||||
],
|
||||
order: [['completion_date', 'DESC']],
|
||||
});
|
||||
|
||||
await cacheUtils.set(cacheKey, achievements, 1800); // Cache 30 min
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: achievements,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/training/stats - Thống kê đào tạo
|
||||
*/
|
||||
async getTrainingStats(req, res, next) {
|
||||
try {
|
||||
const cacheKey = 'training:stats';
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const assignmentStats = await StaffTrainingAssignment.findAll({
|
||||
attributes: [
|
||||
'status',
|
||||
[require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'],
|
||||
],
|
||||
group: ['status'],
|
||||
});
|
||||
|
||||
const achievementStats = await StaffAchievement.findAll({
|
||||
attributes: [
|
||||
'type',
|
||||
[require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'],
|
||||
[require('sequelize').fn('SUM', require('sequelize').col('total_hours')), 'total_hours'],
|
||||
],
|
||||
group: ['type'],
|
||||
});
|
||||
|
||||
const stats = {
|
||||
assignments: assignmentStats,
|
||||
achievements: achievementStats,
|
||||
};
|
||||
|
||||
await cacheUtils.set(cacheKey, stats, 300); // Cache 5 min
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: stats,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new TrainingController();
|
||||
Reference in New Issue
Block a user