feat: enhance credits management with new configurations
- Added credit expiration days, register gift credits, and free monthly credits options in website configuration. - Updated credits handling functions to utilize the new configuration settings for improved flexibility and maintainability. - Removed obsolete constants related to credits from constants.ts to streamline the codebase. - Enhanced type definitions for credits configuration in index.d.ts for better clarity.
This commit is contained in:
parent
b94fd34be5
commit
2e8f70dc76
@ -131,34 +131,39 @@ export const websiteConfig: WebsiteConfig = {
|
||||
},
|
||||
credits: {
|
||||
enableCredits: true,
|
||||
creditExpireDays: 30,
|
||||
registerGiftCredits: {
|
||||
enable: true,
|
||||
credits: 100,
|
||||
},
|
||||
freeMonthlyCredits: {
|
||||
enable: true,
|
||||
credits: 50,
|
||||
},
|
||||
packages: {
|
||||
basic: {
|
||||
id: 'basic',
|
||||
credits: 100,
|
||||
price: 990,
|
||||
popular: false,
|
||||
description: 'Perfect for getting started',
|
||||
},
|
||||
standard: {
|
||||
id: 'standard',
|
||||
credits: 200,
|
||||
price: 1490,
|
||||
popular: true,
|
||||
description: 'Most popular package',
|
||||
},
|
||||
premium: {
|
||||
id: 'premium',
|
||||
credits: 500,
|
||||
price: 3990,
|
||||
popular: false,
|
||||
description: 'Best value for heavy users',
|
||||
},
|
||||
enterprise: {
|
||||
id: 'enterprise',
|
||||
credits: 1000,
|
||||
price: 6990,
|
||||
popular: false,
|
||||
description: 'Tailored for enterprises',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,13 +1,9 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
import { websiteConfig } from '@/config/website';
|
||||
import { getDb } from '@/db';
|
||||
import { creditTransaction, userCredit } from '@/db/schema';
|
||||
import { addDays, isAfter } from 'date-fns';
|
||||
import { and, asc, eq, or } from 'drizzle-orm';
|
||||
import {
|
||||
CREDIT_EXPIRE_DAYS,
|
||||
FREE_MONTHLY_CREDITS,
|
||||
REGISTER_GIFT_CREDITS,
|
||||
} from '../lib/constants';
|
||||
import { CREDIT_TRANSACTION_TYPE } from './types';
|
||||
|
||||
/**
|
||||
@ -45,7 +41,7 @@ export async function updateUserLastRefreshAt(userId: string, date: Date) {
|
||||
* Write a credit transaction record
|
||||
* @param params - Credit transaction parameters
|
||||
*/
|
||||
export async function logCreditTransaction({
|
||||
export async function saveCreditTransaction({
|
||||
userId,
|
||||
type,
|
||||
amount,
|
||||
@ -61,11 +57,16 @@ export async function logCreditTransaction({
|
||||
expirationDate?: Date;
|
||||
}) {
|
||||
if (!userId || !type || !description) {
|
||||
console.error('Invalid params', userId, type, description);
|
||||
console.error(
|
||||
'saveCreditTransaction, invalid params',
|
||||
userId,
|
||||
type,
|
||||
description
|
||||
);
|
||||
throw new Error('Invalid params');
|
||||
}
|
||||
if (!Number.isFinite(amount) || amount === 0) {
|
||||
console.error('Invalid amount', userId, amount);
|
||||
console.error('saveCreditTransaction, invalid amount', userId, amount);
|
||||
throw new Error('Invalid amount');
|
||||
}
|
||||
const db = await getDb();
|
||||
@ -95,7 +96,7 @@ export async function addCredits({
|
||||
type,
|
||||
description,
|
||||
paymentId,
|
||||
expireDays = CREDIT_EXPIRE_DAYS,
|
||||
expireDays = websiteConfig.credits.creditExpireDays.days,
|
||||
}: {
|
||||
userId: string;
|
||||
amount: number;
|
||||
@ -105,15 +106,15 @@ export async function addCredits({
|
||||
expireDays?: number;
|
||||
}) {
|
||||
if (!userId || !type || !description) {
|
||||
console.error('Invalid params', userId, type, description);
|
||||
console.error('addCredits, invalid params', userId, type, description);
|
||||
throw new Error('Invalid params');
|
||||
}
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
console.error('Invalid amount', userId, amount);
|
||||
console.error('addCredits, invalid amount', userId, amount);
|
||||
throw new Error('Invalid amount');
|
||||
}
|
||||
if (!Number.isFinite(expireDays) || expireDays <= 0) {
|
||||
console.error('Invalid expire days', userId, expireDays);
|
||||
console.error('addCredits, invalid expire days', userId, expireDays);
|
||||
throw new Error('Invalid expire days');
|
||||
}
|
||||
// Process expired credits first
|
||||
@ -150,7 +151,7 @@ export async function addCredits({
|
||||
});
|
||||
}
|
||||
// Write credit transaction record
|
||||
await logCreditTransaction({
|
||||
await saveCreditTransaction({
|
||||
userId,
|
||||
type,
|
||||
amount,
|
||||
@ -189,18 +190,20 @@ export async function consumeCredits({
|
||||
description: string;
|
||||
}) {
|
||||
if (!userId || !description) {
|
||||
console.error('Invalid params', userId, description);
|
||||
console.error('consumeCredits, invalid params', userId, description);
|
||||
throw new Error('Invalid params');
|
||||
}
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
console.error('Invalid amount', userId, amount);
|
||||
console.error('consumeCredits, invalid amount', userId, amount);
|
||||
throw new Error('Invalid amount');
|
||||
}
|
||||
// Process expired credits first
|
||||
await processExpiredCredits(userId);
|
||||
// Check balance
|
||||
if (!(await hasEnoughCredits({ userId, requiredCredits: amount }))) {
|
||||
console.error( `Insufficient credits for user ${userId}, required: ${amount}` );
|
||||
console.error(
|
||||
`Insufficient credits for user ${userId}, required: ${amount}`
|
||||
);
|
||||
throw new Error('Insufficient credits');
|
||||
}
|
||||
// FIFO consumption: consume from the earliest unexpired credits first
|
||||
@ -251,7 +254,7 @@ export async function consumeCredits({
|
||||
.set({ currentCredits: newBalance, updatedAt: new Date() })
|
||||
.where(eq(userCredit.userId, userId));
|
||||
// Write usage record
|
||||
await logCreditTransaction({
|
||||
await saveCreditTransaction({
|
||||
userId,
|
||||
type: CREDIT_TRANSACTION_TYPE.USAGE,
|
||||
amount: -amount,
|
||||
@ -319,7 +322,7 @@ export async function processExpiredCredits(userId: string) {
|
||||
.set({ currentCredits: newBalance, updatedAt: now })
|
||||
.where(eq(userCredit.userId, userId));
|
||||
// Write expire record
|
||||
await logCreditTransaction({
|
||||
await saveCreditTransaction({
|
||||
userId,
|
||||
type: CREDIT_TRANSACTION_TYPE.EXPIRE,
|
||||
amount: -expiredTotal,
|
||||
@ -333,6 +336,10 @@ export async function processExpiredCredits(userId: string) {
|
||||
* @param userId - User ID
|
||||
*/
|
||||
export async function addRegisterGiftCredits(userId: string) {
|
||||
if (!websiteConfig.credits.registerGiftCredits.enable) {
|
||||
console.log('addRegisterGiftCredits, disabled');
|
||||
return;
|
||||
}
|
||||
// Check if user has already received register gift credits
|
||||
const db = await getDb();
|
||||
const record = await db
|
||||
@ -347,11 +354,12 @@ export async function addRegisterGiftCredits(userId: string) {
|
||||
.limit(1);
|
||||
// add register gift credits if user has not received them yet
|
||||
if (record.length === 0) {
|
||||
const credits = websiteConfig.credits.registerGiftCredits.credits;
|
||||
await addCredits({
|
||||
userId,
|
||||
amount: REGISTER_GIFT_CREDITS,
|
||||
amount: credits,
|
||||
type: CREDIT_TRANSACTION_TYPE.REGISTER_GIFT,
|
||||
description: `Register gift credits: ${REGISTER_GIFT_CREDITS}`,
|
||||
description: `Register gift credits: ${credits}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -361,6 +369,10 @@ export async function addRegisterGiftCredits(userId: string) {
|
||||
* @param userId - User ID
|
||||
*/
|
||||
export async function addMonthlyFreeCredits(userId: string) {
|
||||
if (!websiteConfig.credits.freeMonthlyCredits.enable) {
|
||||
console.log('addMonthlyFreeCredits, disabled');
|
||||
return;
|
||||
}
|
||||
// Check last refresh time
|
||||
const db = await getDb();
|
||||
const record = await db
|
||||
@ -381,11 +393,12 @@ export async function addMonthlyFreeCredits(userId: string) {
|
||||
}
|
||||
// add credits if it's a new month
|
||||
if (canAdd) {
|
||||
const credits = websiteConfig.credits.freeMonthlyCredits.credits;
|
||||
await addCredits({
|
||||
userId,
|
||||
amount: FREE_MONTHLY_CREDITS,
|
||||
amount: credits,
|
||||
type: CREDIT_TRANSACTION_TYPE.MONTHLY_REFRESH,
|
||||
description: `Free monthly credits: ${FREE_MONTHLY_CREDITS} for ${now.getFullYear()}-${now.getMonth() + 1}`,
|
||||
description: `Free monthly credits: ${credits} for ${now.getFullYear()}-${now.getMonth() + 1}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,2 @@
|
||||
export const PLACEHOLDER_IMAGE =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAoJJREFUWEfFl4lu4zAMRO3cx/9/au6reMaOdkxTTl0grQFCRoqaT+SQotq2bV9N8rRt28xms87m83l553eZ/9vr9Wpkz+ezkT0ej+6dv1X81AFw7M4FBACPVn2c1Z3zLgDeJwHgeLFYdAARYioAEAKJEG2WAjl3gCwNYymQQ9b7/V4spmIAwO6Wy2VnAMikBWlDURBELf8CuN1uHQSrPwMAHK5WqwFELQ01AIXdAa7XawfAb3p6AOwK5+v1ugAoEq4FRSFLgavfQ49jAGQpAE5wjgGCeRrGdBArwHOPcwFcLpcGU1X0IsBuN5tNgYhaiFFwHTiAwq8I+O5xfj6fOz38K+X/fYAdb7fbAgFAjIJ6Aav3AYlQ6nfnDoDz0+lUxNiLALvf7XaDNGQ6GANQBKR85V27B4D3QQRw7hGIYlQKWGM79hSweyCUe1blXhEAogfABwHAXAcqSYkxCtHLUK3XBajSc4Dj8dilAeiSAgD2+30BAEKV4GKcAuDqB4TdYwBgPQByCgApUBoE4EJUGvxUjF3Q69/zLw3g/HA45ABKgdIQu+JPIyDnisCfAxAFNFM0EFNQ64gfS0EUoQP8ighrZSjn3oziZEQpauyKbfjbZchHUL/3AS/Dd30gAkxuRACgfO+EWQW8qwI1o+wseNuKcQiESjALvwNoMI0TcRzD4lFcPYwIM+JTF5x6HOs8yI7jeB5oKhpMRFH9UwaSCDB2Jmg4rc6E2TT0biIaG0rQhNqyhpHBcayTTSXH6vcDL7/sdqRK8LkwTsU499E8vRcAojHcZ4AxABdilgrp4lsXk8oVqgwh7+6H3phqd8J0Kk4vbx/+sZqCD/vNLya/5dT9fAH8g1WdNGgwbQAAAABJRU5ErkJggg==';
|
||||
|
||||
// free monthly credits (10% of the smallest package)
|
||||
export const FREE_MONTHLY_CREDITS = 50;
|
||||
|
||||
// register gift credits (for new user registration)
|
||||
export const REGISTER_GIFT_CREDITS = 100;
|
||||
|
||||
// default credit expiration days
|
||||
export const CREDIT_EXPIRE_DAYS = 30;
|
||||
|
9
src/types/index.d.ts
vendored
9
src/types/index.d.ts
vendored
@ -155,6 +155,15 @@ export interface PriceConfig {
|
||||
*/
|
||||
export interface CreditsConfig {
|
||||
enableCredits: boolean; // Whether to enable credits
|
||||
creditExpireDays: number; // The number of days to expire the credits
|
||||
registerGiftCredits: {
|
||||
enable: boolean; // Whether to enable register gift credits
|
||||
credits: number; // The number of credits to give to the user
|
||||
};
|
||||
freeMonthlyCredits: {
|
||||
enable: boolean; // Whether to enable free monthly credits
|
||||
credits: number; // The number of credits to give to the user
|
||||
};
|
||||
packages: Record<string, CreditPackage>; // Packages indexed by ID
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user