Files
sena_db_api_layer/controllers/subscriptionController.js
2026-01-19 09:33:35 +07:00

254 lines
6.6 KiB
JavaScript

const { SubscriptionPlan, UserSubscription, UsersAuth } = require('../models');
const { cacheUtils } = require('../config/redis');
const { addDatabaseWriteJob } = require('../config/bullmq');
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,
};
// Async write to DB via BullMQ
await addDatabaseWriteJob('create', 'UserSubscription', subscriptionData);
// Clear cache
await cacheUtils.del(`subscription:user:${user_id}`);
res.status(202).json({
success: true,
message: 'Subscription purchase request queued',
data: subscriptionData,
});
} 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) {
subscription.status = 'expired';
await addDatabaseWriteJob('update', 'UserSubscription', {
id: subscription.id,
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',
});
}
// Async update via BullMQ
await addDatabaseWriteJob('update', 'UserSubscription', {
id,
status: 'cancelled',
auto_renew: false,
});
// Clear cache
await cacheUtils.del(`subscription:user:${subscription.user_id}`);
res.status(202).json({
success: true,
message: 'Subscription cancellation request queued',
});
} 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();