- Add user management system with all CRUD operations - Add promotion statistics dashboard with export - Simplify Trading & Market settings UI - Fix promotion schema (dates now optional) - Add missing API endpoints and PATCH support - Add comprehensive documentation - Fix critical bugs (deletePromotion, duplicate endpoints) All features tested and production-ready.
113 lines
2.5 KiB
JavaScript
113 lines
2.5 KiB
JavaScript
import mongoose from "mongoose";
|
|
|
|
const PromoUsageSchema = new mongoose.Schema(
|
|
{
|
|
userId: {
|
|
type: mongoose.Schema.Types.ObjectId,
|
|
ref: "User",
|
|
required: true,
|
|
},
|
|
promoId: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
promoCode: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
promoName: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
promoType: {
|
|
type: String,
|
|
enum: ["deposit_bonus", "discount", "free_item", "custom"],
|
|
required: true,
|
|
},
|
|
// Bonus received
|
|
bonusAmount: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
discountAmount: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// Context of usage
|
|
transactionId: {
|
|
type: mongoose.Schema.Types.ObjectId,
|
|
ref: "Transaction",
|
|
default: null,
|
|
},
|
|
depositAmount: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// Metadata
|
|
usedAt: {
|
|
type: Date,
|
|
default: Date.now,
|
|
},
|
|
ipAddress: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
},
|
|
{ timestamps: true }
|
|
);
|
|
|
|
// Index for quick lookups
|
|
PromoUsageSchema.index({ userId: 1, promoId: 1 });
|
|
PromoUsageSchema.index({ promoId: 1 });
|
|
PromoUsageSchema.index({ userId: 1 });
|
|
|
|
// Static method to check if user has used a promo
|
|
PromoUsageSchema.statics.hasUserUsedPromo = async function (userId, promoId) {
|
|
const usage = await this.findOne({ userId, promoId });
|
|
return !!usage;
|
|
};
|
|
|
|
// Static method to get user's promo usage count
|
|
PromoUsageSchema.statics.getUserPromoCount = async function (userId, promoId) {
|
|
return await this.countDocuments({ userId, promoId });
|
|
};
|
|
|
|
// Static method to get total promo usage count
|
|
PromoUsageSchema.statics.getPromoTotalUses = async function (promoId) {
|
|
return await this.countDocuments({ promoId });
|
|
};
|
|
|
|
// Static method to get promo usage stats
|
|
PromoUsageSchema.statics.getPromoStats = async function (promoId) {
|
|
const stats = await this.aggregate([
|
|
{ $match: { promoId } },
|
|
{
|
|
$group: {
|
|
_id: "$promoId",
|
|
totalUses: { $sum: 1 },
|
|
totalBonusGiven: { $sum: "$bonusAmount" },
|
|
totalDiscountGiven: { $sum: "$discountAmount" },
|
|
uniqueUsers: { $addToSet: "$userId" },
|
|
averageBonusPerUse: { $avg: "$bonusAmount" },
|
|
},
|
|
},
|
|
]);
|
|
|
|
if (stats.length === 0) {
|
|
return {
|
|
totalUses: 0,
|
|
totalBonusGiven: 0,
|
|
totalDiscountGiven: 0,
|
|
uniqueUsers: 0,
|
|
averageBonusPerUse: 0,
|
|
};
|
|
}
|
|
|
|
return {
|
|
...stats[0],
|
|
uniqueUsers: stats[0].uniqueUsers.length,
|
|
};
|
|
};
|
|
|
|
export default mongoose.model("PromoUsage", PromoUsageSchema);
|