const { StaffTrainingAssignment, StaffAchievement, UsersAuth, Subject } = require('../models'); const { cacheUtils } = require('../config/redis'); 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 assignment.update({ 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, }; const newAssignment = await StaffTrainingAssignment.create(assignmentData); // Clear cache await cacheUtils.del(`training:assignments:${staff_id}:*`); res.status(201).json({ success: true, message: 'Training assignment created successfully', data: newAssignment, }); } 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 assignment.update({ status }); // Clear cache await cacheUtils.del(`training:assignments:${assignment.staff_id}:*`); res.status(200).json({ success: true, message: 'Assignment status updated successfully', data: assignment, }); } 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, }; const newAchievement = await StaffAchievement.create(achievementData); // Clear cache await cacheUtils.del(`training:achievements:${staff_id}`); res.status(201).json({ success: true, message: 'Achievement created successfully', data: newAchievement, }); } 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();