This commit is contained in:
Ken
2026-01-19 09:33:35 +07:00
parent 374dc12b2d
commit 70838a4bc1
103 changed files with 16929 additions and 2 deletions

View File

@@ -0,0 +1,374 @@
const { Game } = require('../models');
const { cacheUtils } = require('../config/redis');
/**
* Game Controller - Quản lý trò chơi giáo dục
*/
class GameController {
/**
* Get all games with pagination
*/
async getAllGames(req, res, next) {
try {
const { page = 1, limit = 20, type, is_active, is_premium, difficulty_level } = req.query;
const offset = (page - 1) * limit;
const cacheKey = `games:list:${page}:${limit}:${type || 'all'}:${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 (type) where.type = type;
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 Game.findAndCountAll({
where,
limit: parseInt(limit),
offset: parseInt(offset),
order: [['display_order', 'ASC'], ['rating', 'DESC']],
});
const result = {
games: 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 by ID
*/
async getGameById(req, res, next) {
try {
const { id } = req.params;
const cacheKey = `game:${id}`;
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const game = await Game.findByPk(id);
if (!game) {
return res.status(404).json({
success: false,
message: 'Game not found',
});
}
await cacheUtils.set(cacheKey, game, 3600);
res.json({
success: true,
data: game,
cached: false,
});
} catch (error) {
next(error);
}
}
/**
* Create new game
*/
async createGame(req, res, next) {
try {
const {
title,
description,
url,
thumbnail,
type,
config,
is_active,
is_premium,
min_grade,
max_grade,
difficulty_level,
display_order,
} = req.body;
// Validate required fields
if (!title || !url || !type) {
return res.status(400).json({
success: false,
message: 'title, url, and type are required',
});
}
const game = await Game.create({
title,
description,
url,
thumbnail,
type,
config: config || {},
is_active: is_active !== undefined ? is_active : true,
is_premium: is_premium !== undefined ? is_premium : false,
min_grade,
max_grade,
difficulty_level,
display_order: display_order || 0,
play_count: 0,
});
// Clear cache
await cacheUtils.deletePattern('games:*');
res.status(201).json({
success: true,
data: game,
message: 'Game created successfully',
});
} catch (error) {
next(error);
}
}
/**
* Update game
*/
async updateGame(req, res, next) {
try {
const { id } = req.params;
const {
title,
description,
url,
thumbnail,
type,
config,
is_active,
is_premium,
min_grade,
max_grade,
difficulty_level,
display_order,
rating,
} = req.body;
const game = await Game.findByPk(id);
if (!game) {
return res.status(404).json({
success: false,
message: 'Game not found',
});
}
await game.update({
...(title !== undefined && { title }),
...(description !== undefined && { description }),
...(url !== undefined && { url }),
...(thumbnail !== undefined && { thumbnail }),
...(type !== undefined && { type }),
...(config !== undefined && { config }),
...(is_active !== undefined && { is_active }),
...(is_premium !== undefined && { is_premium }),
...(min_grade !== undefined && { min_grade }),
...(max_grade !== undefined && { max_grade }),
...(difficulty_level !== undefined && { difficulty_level }),
...(display_order !== undefined && { display_order }),
...(rating !== undefined && { rating }),
});
// Clear cache
await cacheUtils.deletePattern('games:*');
await cacheUtils.deletePattern(`game:${id}`);
res.json({
success: true,
data: game,
message: 'Game updated successfully',
});
} catch (error) {
next(error);
}
}
/**
* Delete game
*/
async deleteGame(req, res, next) {
try {
const { id } = req.params;
const game = await Game.findByPk(id);
if (!game) {
return res.status(404).json({
success: false,
message: 'Game not found',
});
}
await game.destroy();
// Clear cache
await cacheUtils.deletePattern('games:*');
await cacheUtils.deletePattern(`game:${id}`);
res.json({
success: true,
message: 'Game deleted successfully',
});
} catch (error) {
next(error);
}
}
/**
* Increment play count
*/
async incrementPlayCount(req, res, next) {
try {
const { id } = req.params;
const game = await Game.findByPk(id);
if (!game) {
return res.status(404).json({
success: false,
message: 'Game not found',
});
}
await game.increment('play_count');
await game.reload();
// Clear cache
await cacheUtils.deletePattern(`game:${id}`);
res.json({
success: true,
data: {
id: game.id,
play_count: game.play_count,
},
message: 'Play count incremented',
});
} catch (error) {
next(error);
}
}
/**
* Get games by type (for lesson matching)
*/
async getGamesByType(req, res, next) {
try {
const { type } = req.params;
const { only_active = 'true' } = req.query;
const cacheKey = `games:type:${type}:${only_active}`;
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const where = { type };
if (only_active === 'true') where.is_active = true;
const games = await Game.findAll({
where,
order: [['display_order', 'ASC'], ['rating', 'DESC']],
});
await cacheUtils.set(cacheKey, games, 3600);
res.json({
success: true,
data: games,
cached: false,
});
} catch (error) {
next(error);
}
}
/**
* Get game statistics
*/
async getGameStats(req, res, next) {
try {
const cacheKey = 'games:stats';
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
const [totalGames, activeGames, premiumGames, totalPlays] = await Promise.all([
Game.count(),
Game.count({ where: { is_active: true } }),
Game.count({ where: { is_premium: true } }),
Game.sum('play_count'),
]);
const topGames = await Game.findAll({
where: { is_active: true },
order: [['play_count', 'DESC']],
limit: 10,
attributes: ['id', 'title', 'type', 'play_count', 'rating'],
});
const stats = {
totalGames,
activeGames,
premiumGames,
totalPlays: totalPlays || 0,
topGames,
};
await cacheUtils.set(cacheKey, stats, 600);
res.json({
success: true,
data: stats,
cached: false,
});
} catch (error) {
next(error);
}
}
}
module.exports = new GameController();