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();