const { SubscriptionPlan, UserSubscription, UsersAuth } = require('../models'); const { cacheUtils } = require('../config/redis'); const { v4: uuidv4 } = require('uuid'); /** * Subscription Controller - Quản lý gói thẻ tháng */ class SubscriptionController { /** * GET /api/subscriptions/plans - Danh sách các gói subscription */ async getPlans(req, res, next) { try { const { target_role } = req.query; const cacheKey = `subscription:plans:${target_role || 'all'}`; const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const where = { is_active: true }; if (target_role) where.target_role = target_role; const plans = await SubscriptionPlan.findAll({ where, order: [['price', 'ASC']], }); await cacheUtils.set(cacheKey, plans, 1800); // Cache 30 min res.json({ success: true, data: plans, }); } catch (error) { next(error); } } /** * POST /api/subscriptions/purchase - Mua gói subscription */ async purchaseSubscription(req, res, next) { try { const { user_id, plan_id, payment_method, transaction_id } = req.body; if (!user_id || !plan_id || !payment_method || !transaction_id) { return res.status(400).json({ success: false, message: 'Missing required fields: user_id, plan_id, payment_method, transaction_id', }); } // Kiểm tra plan có tồn tại không const plan = await SubscriptionPlan.findByPk(plan_id); if (!plan || !plan.is_active) { return res.status(404).json({ success: false, message: 'Subscription plan not found or inactive', }); } // Kiểm tra user có subscription đang active không const existingSubscription = await UserSubscription.findOne({ where: { user_id, status: 'active', }, }); if (existingSubscription) { return res.status(400).json({ success: false, message: 'User already has an active subscription', }); } const start_date = new Date(); const end_date = new Date(start_date); end_date.setDate(end_date.getDate() + plan.duration_days); const subscriptionData = { id: uuidv4(), user_id, plan_id, start_date, end_date, status: 'active', transaction_id, payment_method, payment_amount: plan.price, auto_renew: req.body.auto_renew || false, }; const newSubscription = await UserSubscription.create(subscriptionData); // Clear cache await cacheUtils.del(`subscription:user:${user_id}`); res.status(201).json({ success: true, message: 'Subscription purchased successfully', data: newSubscription, }); } catch (error) { next(error); } } /** * GET /api/subscriptions/user/:user_id - Kiểm tra subscription status của user */ async getUserSubscription(req, res, next) { try { const { user_id } = req.params; const cacheKey = `subscription:user:${user_id}`; const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const subscription = await UserSubscription.findOne({ where: { user_id }, include: [ { model: SubscriptionPlan, as: 'plan', attributes: ['name', 'price', 'features', 'duration_days'], }, ], order: [['created_at', 'DESC']], }); if (!subscription) { return res.status(404).json({ success: false, message: 'No subscription found for this user', }); } // Check nếu subscription đã hết hạn const now = new Date(); if (subscription.status === 'active' && new Date(subscription.end_date) < now) { await subscription.update({ status: 'expired' }); } await cacheUtils.set(cacheKey, subscription, 900); // Cache 15 min res.json({ success: true, data: subscription, }); } catch (error) { next(error); } } /** * POST /api/subscriptions/:id/cancel - Hủy subscription */ async cancelSubscription(req, res, next) { try { const { id } = req.params; const { reason } = req.body; const subscription = await UserSubscription.findByPk(id); if (!subscription) { return res.status(404).json({ success: false, message: 'Subscription not found', }); } if (subscription.status === 'cancelled') { return res.status(400).json({ success: false, message: 'Subscription already cancelled', }); } // Update subscription await subscription.update({ status: 'cancelled', auto_renew: false, }); // Clear cache await cacheUtils.del(`subscription:user:${subscription.user_id}`); res.status(200).json({ success: true, message: 'Subscription cancelled successfully', data: subscription, }); } catch (error) { next(error); } } /** * GET /api/subscriptions/stats - Thống kê subscription */ async getSubscriptionStats(req, res, next) { try { const cacheKey = 'subscription:stats'; const cached = await cacheUtils.get(cacheKey); if (cached) { return res.json({ success: true, data: cached, cached: true, }); } const stats = await UserSubscription.findAll({ attributes: [ 'status', [require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'], ], group: ['status'], }); await cacheUtils.set(cacheKey, stats, 300); // Cache 5 min res.json({ success: true, data: stats, }); } catch (error) { next(error); } } } module.exports = new SubscriptionController();