update
This commit is contained in:
335
controllers/chapterController.js
Normal file
335
controllers/chapterController.js
Normal file
@@ -0,0 +1,335 @@
|
||||
const { Chapter, Subject, Lesson } = require('../models');
|
||||
const { cacheUtils } = require('../config/redis');
|
||||
|
||||
/**
|
||||
* Chapter Controller - Quản lý chương học
|
||||
*/
|
||||
class ChapterController {
|
||||
/**
|
||||
* Get all chapters with pagination
|
||||
*/
|
||||
async getAllChapters(req, res, next) {
|
||||
try {
|
||||
const { page = 1, limit = 20, subject_id, is_published } = req.query;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const cacheKey = `chapters:list:${page}:${limit}:${subject_id || 'all'}:${is_published || 'all'}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const where = {};
|
||||
if (subject_id) where.subject_id = subject_id;
|
||||
if (is_published !== undefined) where.is_published = is_published === 'true';
|
||||
|
||||
const { count, rows } = await Chapter.findAndCountAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: Subject,
|
||||
as: 'subject',
|
||||
attributes: ['id', 'subject_name', 'subject_code'],
|
||||
},
|
||||
],
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset),
|
||||
order: [['display_order', 'ASC'], ['chapter_number', 'ASC']],
|
||||
});
|
||||
|
||||
const result = {
|
||||
chapters: 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 chapter by ID
|
||||
*/
|
||||
async getChapterById(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { include_lessons } = req.query;
|
||||
|
||||
const cacheKey = `chapter:${id}:${include_lessons || 'no'}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const includeOptions = [
|
||||
{
|
||||
model: Subject,
|
||||
as: 'subject',
|
||||
attributes: ['id', 'subject_name', 'subject_code', 'description'],
|
||||
},
|
||||
];
|
||||
|
||||
if (include_lessons === 'true') {
|
||||
includeOptions.push({
|
||||
model: Lesson,
|
||||
as: 'lessons',
|
||||
where: { is_published: true },
|
||||
required: false,
|
||||
order: [['display_order', 'ASC']],
|
||||
});
|
||||
}
|
||||
|
||||
const chapter = await Chapter.findByPk(id, {
|
||||
include: includeOptions,
|
||||
});
|
||||
|
||||
if (!chapter) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Chapter not found',
|
||||
});
|
||||
}
|
||||
|
||||
await cacheUtils.set(cacheKey, chapter, 3600);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: chapter,
|
||||
cached: false,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new chapter
|
||||
*/
|
||||
async createChapter(req, res, next) {
|
||||
try {
|
||||
const {
|
||||
subject_id,
|
||||
chapter_number,
|
||||
chapter_title,
|
||||
chapter_description,
|
||||
duration_minutes,
|
||||
is_published,
|
||||
display_order,
|
||||
} = req.body;
|
||||
|
||||
// Validate required fields
|
||||
if (!subject_id || !chapter_number || !chapter_title) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'subject_id, chapter_number, and chapter_title are required',
|
||||
});
|
||||
}
|
||||
|
||||
// Check if subject exists
|
||||
const subject = await Subject.findByPk(subject_id);
|
||||
if (!subject) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Subject not found',
|
||||
});
|
||||
}
|
||||
|
||||
const chapter = await Chapter.create({
|
||||
subject_id,
|
||||
chapter_number,
|
||||
chapter_title,
|
||||
chapter_description,
|
||||
duration_minutes,
|
||||
is_published: is_published !== undefined ? is_published : false,
|
||||
display_order: display_order || 0,
|
||||
});
|
||||
|
||||
// Clear cache
|
||||
await cacheUtils.deletePattern('chapters:*');
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: chapter,
|
||||
message: 'Chapter created successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update chapter
|
||||
*/
|
||||
async updateChapter(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
chapter_number,
|
||||
chapter_title,
|
||||
chapter_description,
|
||||
duration_minutes,
|
||||
is_published,
|
||||
display_order,
|
||||
} = req.body;
|
||||
|
||||
const chapter = await Chapter.findByPk(id);
|
||||
|
||||
if (!chapter) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Chapter not found',
|
||||
});
|
||||
}
|
||||
|
||||
await chapter.update({
|
||||
...(chapter_number !== undefined && { chapter_number }),
|
||||
...(chapter_title !== undefined && { chapter_title }),
|
||||
...(chapter_description !== undefined && { chapter_description }),
|
||||
...(duration_minutes !== undefined && { duration_minutes }),
|
||||
...(is_published !== undefined && { is_published }),
|
||||
...(display_order !== undefined && { display_order }),
|
||||
});
|
||||
|
||||
// Clear cache
|
||||
await cacheUtils.deletePattern('chapters:*');
|
||||
await cacheUtils.deletePattern(`chapter:${id}*`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: chapter,
|
||||
message: 'Chapter updated successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete chapter
|
||||
*/
|
||||
async deleteChapter(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const chapter = await Chapter.findByPk(id);
|
||||
|
||||
if (!chapter) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Chapter not found',
|
||||
});
|
||||
}
|
||||
|
||||
// Check if chapter has lessons
|
||||
const lessonsCount = await Lesson.count({ where: { chapter_id: id } });
|
||||
if (lessonsCount > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Cannot delete chapter. It has ${lessonsCount} lesson(s). Delete lessons first.`,
|
||||
});
|
||||
}
|
||||
|
||||
await chapter.destroy();
|
||||
|
||||
// Clear cache
|
||||
await cacheUtils.deletePattern('chapters:*');
|
||||
await cacheUtils.deletePattern(`chapter:${id}*`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Chapter deleted successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lessons in a chapter
|
||||
*/
|
||||
async getLessonsByChapter(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { page = 1, limit = 20, is_published } = req.query;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const cacheKey = `chapter:${id}:lessons:${page}:${limit}:${is_published || 'all'}`;
|
||||
|
||||
const cached = await cacheUtils.get(cacheKey);
|
||||
if (cached) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: cached,
|
||||
cached: true,
|
||||
});
|
||||
}
|
||||
|
||||
const chapter = await Chapter.findByPk(id);
|
||||
if (!chapter) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Chapter not found',
|
||||
});
|
||||
}
|
||||
|
||||
const where = { chapter_id: id };
|
||||
if (is_published !== undefined) where.is_published = is_published === 'true';
|
||||
|
||||
const { count, rows } = await Lesson.findAndCountAll({
|
||||
where,
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset),
|
||||
order: [['display_order', 'ASC'], ['lesson_number', 'ASC']],
|
||||
});
|
||||
|
||||
const result = {
|
||||
chapter: {
|
||||
id: chapter.id,
|
||||
chapter_title: chapter.chapter_title,
|
||||
chapter_number: chapter.chapter_number,
|
||||
},
|
||||
lessons: 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ChapterController();
|
||||
Reference in New Issue
Block a user