first commit
This commit is contained in:
206
lib/ranks.ts
Normal file
206
lib/ranks.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { emailService } from '@/lib/email'
|
||||
|
||||
interface RankCalculation {
|
||||
totalReferrals: number
|
||||
salesVolume: number
|
||||
teamVolume: number
|
||||
}
|
||||
|
||||
export class RankSystem {
|
||||
async calculateUserMetrics(userId: string): Promise<RankCalculation> {
|
||||
// Get direct referrals count
|
||||
const totalReferrals = await prisma.user.count({
|
||||
where: { referrerId: userId }
|
||||
})
|
||||
|
||||
// Get user's personal sales volume (last 30 days)
|
||||
const salesVolumeResult = await prisma.order.aggregate({
|
||||
where: {
|
||||
userId,
|
||||
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] },
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days
|
||||
}
|
||||
},
|
||||
_sum: { total: true }
|
||||
})
|
||||
|
||||
// Get team sales volume (referrals' sales in last 30 days)
|
||||
const referralIds = await prisma.user.findMany({
|
||||
where: { referrerId: userId },
|
||||
select: { id: true }
|
||||
}).then(users => users.map(u => u.id))
|
||||
|
||||
const teamVolumeResult = await prisma.order.aggregate({
|
||||
where: {
|
||||
userId: { in: referralIds },
|
||||
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] },
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
},
|
||||
_sum: { total: true }
|
||||
})
|
||||
|
||||
return {
|
||||
totalReferrals,
|
||||
salesVolume: salesVolumeResult._sum.total || 0,
|
||||
teamVolume: teamVolumeResult._sum.total || 0
|
||||
}
|
||||
}
|
||||
|
||||
async getEligibleRank(metrics: RankCalculation) {
|
||||
const ranks = await prisma.rank.findMany({
|
||||
where: { isActive: true },
|
||||
orderBy: { order: 'desc' }
|
||||
})
|
||||
|
||||
// Find highest rank user qualifies for
|
||||
for (const rank of ranks) {
|
||||
if (
|
||||
metrics.totalReferrals >= rank.minReferrals &&
|
||||
metrics.salesVolume >= rank.minSalesVolume &&
|
||||
metrics.teamVolume >= rank.minTeamVolume
|
||||
) {
|
||||
return rank
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
async updateUserRank(userId: string) {
|
||||
try {
|
||||
const metrics = await this.calculateUserMetrics(userId)
|
||||
const eligibleRank = await this.getEligibleRank(metrics)
|
||||
|
||||
if (!eligibleRank) return null
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { currentRank: true }
|
||||
})
|
||||
|
||||
if (!user) return null
|
||||
|
||||
// Check if this is a rank upgrade
|
||||
const isUpgrade = !user.currentRank || eligibleRank.order > user.currentRank.order
|
||||
|
||||
if (isUpgrade) {
|
||||
// Update user's current rank
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: { currentRankId: eligibleRank.id }
|
||||
})
|
||||
|
||||
// Record achievement
|
||||
await prisma.rankAchievement.upsert({
|
||||
where: {
|
||||
userId_rankId: {
|
||||
userId,
|
||||
rankId: eligibleRank.id
|
||||
}
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId,
|
||||
rankId: eligibleRank.id
|
||||
}
|
||||
})
|
||||
|
||||
// Send achievement email
|
||||
try {
|
||||
await emailService.sendRankAchievement(user.email, user.name || 'User', eligibleRank)
|
||||
} catch (emailError) {
|
||||
console.error('Failed to send rank achievement email:', emailError)
|
||||
}
|
||||
|
||||
console.log(`User ${user.email} achieved rank: ${eligibleRank.name}`)
|
||||
return eligibleRank
|
||||
}
|
||||
|
||||
return null
|
||||
} catch (error) {
|
||||
console.error('Error updating user rank:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async initializeDefaultRanks() {
|
||||
const existingRanks = await prisma.rank.count()
|
||||
|
||||
if (existingRanks === 0) {
|
||||
const defaultRanks = [
|
||||
{
|
||||
name: 'Bronze',
|
||||
description: 'Welcome to the journey!',
|
||||
minReferrals: 0,
|
||||
minSalesVolume: 0,
|
||||
minTeamVolume: 0,
|
||||
commissionMultiplier: 1.0,
|
||||
benefits: ['Basic commission rates', 'Access to all products'],
|
||||
color: '#CD7F32',
|
||||
icon: '🥉',
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
name: 'Silver',
|
||||
description: 'Building momentum!',
|
||||
minReferrals: 3,
|
||||
minSalesVolume: 5000,
|
||||
minTeamVolume: 10000,
|
||||
commissionMultiplier: 1.2,
|
||||
benefits: ['20% commission bonus', 'Priority support', 'Monthly bonuses'],
|
||||
color: '#C0C0C0',
|
||||
icon: '🥈',
|
||||
order: 2
|
||||
},
|
||||
{
|
||||
name: 'Gold',
|
||||
description: 'Excellent performance!',
|
||||
minReferrals: 5,
|
||||
minSalesVolume: 15000,
|
||||
minTeamVolume: 30000,
|
||||
commissionMultiplier: 1.5,
|
||||
benefits: ['50% commission bonus', 'VIP support', 'Exclusive events'],
|
||||
color: '#FFD700',
|
||||
icon: '🥇',
|
||||
order: 3
|
||||
},
|
||||
{
|
||||
name: 'Platinum',
|
||||
description: 'Elite achiever!',
|
||||
minReferrals: 10,
|
||||
minSalesVolume: 30000,
|
||||
minTeamVolume: 75000,
|
||||
commissionMultiplier: 2.0,
|
||||
benefits: ['100% commission bonus', 'Personal coach', 'Leadership trips'],
|
||||
color: '#E5E4E2',
|
||||
icon: '💎',
|
||||
order: 4
|
||||
},
|
||||
{
|
||||
name: 'Diamond',
|
||||
description: 'Ultimate success!',
|
||||
minReferrals: 15,
|
||||
minSalesVolume: 50000,
|
||||
minTeamVolume: 150000,
|
||||
commissionMultiplier: 3.0,
|
||||
benefits: ['200% commission bonus', 'Revenue sharing', 'Executive benefits'],
|
||||
color: '#B9F2FF',
|
||||
icon: '💎',
|
||||
order: 5
|
||||
}
|
||||
]
|
||||
|
||||
await prisma.rank.createMany({
|
||||
data: defaultRanks
|
||||
})
|
||||
|
||||
console.log('Default ranks initialized')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const rankSystem = new RankSystem()
|
||||
Reference in New Issue
Block a user