update
This commit is contained in:
253
controllers/subscriptionController.js
Normal file
253
controllers/subscriptionController.js
Normal file
@@ -0,0 +1,253 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user