174 lines
3.6 KiB
TypeScript
174 lines
3.6 KiB
TypeScript
/**
|
|
* Points management utilities
|
|
*/
|
|
|
|
import {
|
|
Transaction,
|
|
TransactionType
|
|
} from './types';
|
|
import {
|
|
createTransaction,
|
|
getUserTransactions,
|
|
getUserBalance
|
|
} from './db/store';
|
|
|
|
/**
|
|
* Add points to a user's account
|
|
*/
|
|
export function addPoints(
|
|
userId: string,
|
|
amount: number,
|
|
source: string
|
|
): Transaction {
|
|
if (amount <= 0) {
|
|
throw new Error('Amount must be positive');
|
|
}
|
|
|
|
return createTransaction(userId, amount, TransactionType.EARNED, source);
|
|
}
|
|
|
|
/**
|
|
* Deduct points from a user's account
|
|
*/
|
|
export function deductPoints(
|
|
userId: string,
|
|
amount: number,
|
|
source: string
|
|
): Transaction {
|
|
if (amount <= 0) {
|
|
throw new Error('Amount must be positive');
|
|
}
|
|
|
|
const currentBalance = getBalance(userId);
|
|
if (currentBalance < amount) {
|
|
throw new Error('Insufficient balance');
|
|
}
|
|
|
|
return createTransaction(userId, amount, TransactionType.SPENT, source);
|
|
}
|
|
|
|
/**
|
|
* Get user's current points balance
|
|
*/
|
|
export function getBalance(userId: string): number {
|
|
return getUserBalance(userId);
|
|
}
|
|
|
|
/**
|
|
* Get all transactions for a user
|
|
*/
|
|
export function getTransactions(userId: string): Transaction[] {
|
|
return getUserTransactions(userId).sort(
|
|
(a, b) => b.createdAt.getTime() - a.createdAt.getTime()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get recent transactions (last N)
|
|
*/
|
|
export function getRecentTransactions(
|
|
userId: string,
|
|
limit: number = 10
|
|
): Transaction[] {
|
|
const allTransactions = getTransactions(userId);
|
|
return allTransactions.slice(0, limit);
|
|
}
|
|
|
|
/**
|
|
* Get transaction statistics for a user
|
|
*/
|
|
export function getTransactionStats(userId: string): {
|
|
totalEarned: number;
|
|
totalSpent: number;
|
|
balance: number;
|
|
transactionCount: number;
|
|
} {
|
|
const transactions = getUserTransactions(userId);
|
|
|
|
const totalEarned = transactions
|
|
.filter(t => t.type === TransactionType.EARNED)
|
|
.reduce((sum, t) => sum + t.amount, 0);
|
|
|
|
const totalSpent = transactions
|
|
.filter(t => t.type === TransactionType.SPENT)
|
|
.reduce((sum, t) => sum + t.amount, 0);
|
|
|
|
return {
|
|
totalEarned,
|
|
totalSpent,
|
|
balance: totalEarned - totalSpent,
|
|
transactionCount: transactions.length
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get points earned from specific source
|
|
*/
|
|
export function getPointsBySource(
|
|
userId: string,
|
|
source: string
|
|
): number {
|
|
const transactions = getUserTransactions(userId);
|
|
|
|
return transactions
|
|
.filter(t => t.type === TransactionType.EARNED && t.source === source)
|
|
.reduce((sum, t) => sum + t.amount, 0);
|
|
}
|
|
|
|
/**
|
|
* Get breakdown of points by source
|
|
*/
|
|
export function getPointsBreakdown(userId: string): Record<string, number> {
|
|
const transactions = getUserTransactions(userId);
|
|
|
|
const breakdown: Record<string, number> = {};
|
|
|
|
transactions
|
|
.filter(t => t.type === TransactionType.EARNED)
|
|
.forEach(t => {
|
|
breakdown[t.source] = (breakdown[t.source] || 0) + t.amount;
|
|
});
|
|
|
|
return breakdown;
|
|
}
|
|
|
|
/**
|
|
* Check if user can afford a purchase
|
|
*/
|
|
export function canAfford(userId: string, amount: number): boolean {
|
|
return getBalance(userId) >= amount;
|
|
}
|
|
|
|
/**
|
|
* Award task completion points
|
|
*/
|
|
export function awardTaskPoints(
|
|
userId: string,
|
|
taskId: string,
|
|
amount: number
|
|
): Transaction {
|
|
return addPoints(userId, amount, `task:${taskId}`);
|
|
}
|
|
|
|
/**
|
|
* Award referral bonus points
|
|
*/
|
|
export function awardReferralBonus(
|
|
userId: string,
|
|
referralId: string,
|
|
amount: number
|
|
): Transaction {
|
|
return addPoints(userId, amount, `referral:${referralId}`);
|
|
}
|
|
|
|
/**
|
|
* Award daily streak bonus
|
|
*/
|
|
export function awardStreakBonus(
|
|
userId: string,
|
|
streakDays: number
|
|
): Transaction {
|
|
const bonusAmount = Math.min(streakDays * 5, 50); // Max 50 points
|
|
return addPoints(userId, bonusAmount, `streak:${streakDays}days`);
|
|
}
|