Files
sena_db_api_layer/controllers/gameController.js
silverpro89 41cfb533d5
All checks were successful
Deploy to Production / deploy (push) Successful in 20s
update
2026-01-28 20:34:15 +07:00

499 lines
12 KiB
JavaScript

const { Game } = require('../models');
const { cacheUtils } = require('../config/redis');
const { sequelize } = require('../config/database');
const { Op, Sequelize } = require('sequelize');
/**
* 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);
}
}
/**
* Create or Update game with URL check
* Nếu body có id: update game đó
* Nếu body không có id: kiểm tra URL có tồn tại trong DB không, chưa có thì lưu mới
*/
async createGameWithUrlCheck(req, res, next) {
try {
const {
id,
title,
description,
url,
thumbnail,
type,
config,
is_active,
is_premium,
min_grade,
max_grade,
difficulty_level,
display_order,
rating,
} = req.body;
// Validate required fields
if (!title || !url || !type) {
return res.status(400).json({
success: false,
message: 'title, url, and type are required',
});
}
// Check if URL already exists in DB
const existingGame = await Game.findOne({ where: { url } });
if (existingGame) {
return res.status(200).json({
success: true,
data: null,
message: 'URL đã tồn tại, game không được lưu',
});
}
// Create new game (with id if provided, or auto-generate)
const game = await Game.create({
...(id && { id }),
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);
}
}
/**
* Get all unique game types
*/
async getGameTypes(req, res, next) {
try {
const cacheKey = 'games:types';
const cached = await cacheUtils.get(cacheKey);
if (cached) {
return res.json({
success: true,
data: cached,
cached: true,
});
}
// Get all games with only type field
const games = await Game.findAll({
attributes: [[Sequelize.fn('DISTINCT', Sequelize.col('type')), 'type']],
where: {
type: {
[Op.and]: [
{ [Op.ne]: null },
{ [Op.notIn]: ['', ' '] }
]
}
},
raw: true,
});
// Get distinct types
const result = games.filter(g => g.type);
await cacheUtils.set(cacheKey, result, 3600);
res.json({
success: true,
data: result,
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();