update
This commit is contained in:
248
controllers/attendanceController.js
Normal file
248
controllers/attendanceController.js
Normal file
@@ -0,0 +1,248 @@
|
||||
const { AttendanceLog, AttendanceDaily, UsersAuth, School } = require('../models');
|
||||
const { cacheUtils } = require('../config/redis');
|
||||
const { addDatabaseWriteJob, addAttendanceProcessJob } = require('../config/bullmq');
|
||||
|
||||
/**
|
||||
* Attendance Controller - Quản lý điểm danh
|
||||
*/
|
||||
class AttendanceController {
|
||||
/**
|
||||
* Get attendance logs with pagination
|
||||
*/
|
||||
async getAttendanceLogs(req, res, next) {
|
||||
try {
|
||||
const { page = 1, limit = 50, user_id, school_id, date_from, date_to } = req.query;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const cacheKey = `attendance:logs:${page}:${limit}:${user_id || 'all'}:${school_id || 'all'}:${date_from || ''}:${date_to || ''}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const where = {};
|
||||
if (user_id) where.user_id = user_id;
|
||||
if (school_id) where.school_id = school_id;
|
||||
if (date_from && date_to) {
|
||||
where.check_time = {
|
||||
[require('sequelize').Op.between]: [date_from, date_to],
|
||||
};
|
||||
}
|
||||
|
||||
const { count, rows } = await AttendanceLog.findAndCountAll({
|
||||
where,
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset),
|
||||
include: [
|
||||
{
|
||||
model: UsersAuth,
|
||||
as: 'user',
|
||||
attributes: ['username', 'email'],
|
||||
},
|
||||
{
|
||||
model: School,
|
||||
as: 'school',
|
||||
attributes: ['school_code', 'school_name'],
|
||||
},
|
||||
],
|
||||
order: [['check_time', 'DESC']],
|
||||
});
|
||||
|
||||
const result = {
|
||||
logs: rows,
|
||||
pagination: {
|
||||
total: count,
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
totalPages: Math.ceil(count / limit),
|
||||
},
|
||||
};
|
||||
|
||||
await cacheUtils.set(cacheKey, result, 300); // Cache 5 minutes
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
cached: false,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create attendance log (QR scan)
|
||||
*/
|
||||
async createAttendanceLog(req, res, next) {
|
||||
try {
|
||||
const logData = req.body;
|
||||
|
||||
const job = await addDatabaseWriteJob('create', 'AttendanceLog', logData);
|
||||
|
||||
await cacheUtils.deletePattern('attendance:logs:*');
|
||||
await cacheUtils.deletePattern('attendance:daily:*');
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Attendance log created',
|
||||
jobId: job.id,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get daily attendance summary
|
||||
*/
|
||||
async getDailyAttendance(req, res, next) {
|
||||
try {
|
||||
const { page = 1, limit = 50, school_id, class_id } = req.query;
|
||||
let { date } = req.query;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// Use today's date if not provided
|
||||
if (!date) {
|
||||
date = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD
|
||||
}
|
||||
|
||||
const cacheKey = `attendance:daily:${school_id || 'all'}:${date}:${class_id || 'all'}:${page}:${limit}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const where = { attendance_date: date };
|
||||
if (school_id) where.school_id = school_id;
|
||||
if (class_id) where.class_id = class_id;
|
||||
|
||||
const { count, rows } = await AttendanceDaily.findAndCountAll({
|
||||
where,
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset),
|
||||
include: [{
|
||||
model: UsersAuth,
|
||||
as: 'user',
|
||||
attributes: ['username'],
|
||||
}],
|
||||
order: [['status', 'ASC']],
|
||||
});
|
||||
|
||||
const result = {
|
||||
attendance: rows,
|
||||
date: date,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process daily attendance (aggregate from logs)
|
||||
*/
|
||||
async processAttendance(req, res, next) {
|
||||
try {
|
||||
const { school_id, date } = req.body;
|
||||
|
||||
const job = await addAttendanceProcessJob(school_id, date);
|
||||
|
||||
await cacheUtils.deletePattern('attendance:daily:*');
|
||||
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: 'Attendance processing job queued',
|
||||
jobId: job.id,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attendance statistics
|
||||
*/
|
||||
async getAttendanceStats(req, res, next) {
|
||||
try {
|
||||
const { school_id, date_from, date_to } = req.query;
|
||||
const cacheKey = `attendance:stats:${school_id}:${date_from}:${date_to}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const stats = await AttendanceDaily.findAll({
|
||||
where: {
|
||||
school_id,
|
||||
attendance_date: {
|
||||
[require('sequelize').Op.between]: [date_from, date_to],
|
||||
},
|
||||
},
|
||||
attributes: [
|
||||
'status',
|
||||
[require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'],
|
||||
],
|
||||
group: ['status'],
|
||||
raw: true,
|
||||
});
|
||||
|
||||
await cacheUtils.set(cacheKey, stats, 3600);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: stats,
|
||||
cached: false,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attendance datatypes
|
||||
*/
|
||||
async getAttendanceDatatypes(req, res, next) {
|
||||
try {
|
||||
const datatypes = {
|
||||
AttendanceLog: AttendanceLog.rawAttributes,
|
||||
AttendanceDaily: AttendanceDaily.rawAttributes,
|
||||
};
|
||||
res.json({
|
||||
success: true,
|
||||
data: datatypes,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AttendanceController();
|
||||
Reference in New Issue
Block a user