chore: update credits related functions (2)
This commit is contained in:
parent
443f01769c
commit
4374f118b4
@ -1,10 +1,6 @@
|
||||
import {
|
||||
CREDIT_TRANSACTION_DESCRIPTION,
|
||||
CREDIT_TRANSACTION_TYPE,
|
||||
} from '@/lib/constants';
|
||||
import {
|
||||
addCredits,
|
||||
addMonthlyFreeCredits,
|
||||
addRegisterGiftCredits,
|
||||
consumeCredits,
|
||||
getUserCredits,
|
||||
} from '@/lib/credits';
|
||||
@ -22,6 +18,22 @@ export const getCreditsAction = actionClient.action(async () => {
|
||||
return { success: true, credits };
|
||||
});
|
||||
|
||||
// add register gift credits (for testing)
|
||||
export const addRegisterCreditsAction = actionClient.action(async () => {
|
||||
const session = await getSession();
|
||||
if (!session) return { success: false, error: 'Unauthorized' };
|
||||
await addRegisterGiftCredits(session.user.id);
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// add monthly free credits (for testing)
|
||||
export const addMonthlyCreditsAction = actionClient.action(async () => {
|
||||
const session = await getSession();
|
||||
if (!session) return { success: false, error: 'Unauthorized' };
|
||||
await addMonthlyFreeCredits(session.user.id);
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// consume credits (simulate button)
|
||||
const consumeSchema = z.object({
|
||||
amount: z.number().min(1),
|
||||
@ -38,31 +50,10 @@ export const consumeCreditsAction = actionClient
|
||||
userId: session.user.id,
|
||||
amount: parsedInput.amount,
|
||||
description:
|
||||
parsedInput.description || CREDIT_TRANSACTION_DESCRIPTION.USAGE,
|
||||
parsedInput.description || `Consume credits: ${parsedInput.amount}`,
|
||||
});
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
return { success: false, error: (e as Error).message };
|
||||
}
|
||||
});
|
||||
|
||||
// add register credits (for testing)
|
||||
export const addRegisterCreditsAction = actionClient.action(async () => {
|
||||
const session = await getSession();
|
||||
if (!session) return { success: false, error: 'Unauthorized' };
|
||||
await addCredits({
|
||||
userId: session.user.id,
|
||||
amount: 100,
|
||||
type: CREDIT_TRANSACTION_TYPE.REGISTER_GIFT,
|
||||
description: CREDIT_TRANSACTION_DESCRIPTION.REGISTER_GIFT,
|
||||
});
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// add monthly free credits (for testing)
|
||||
export const addMonthlyCreditsAction = actionClient.action(async () => {
|
||||
const session = await getSession();
|
||||
if (!session) return { success: false, error: 'Unauthorized' };
|
||||
await addMonthlyFreeCredits(session.user.id);
|
||||
return { success: true };
|
||||
});
|
||||
|
@ -85,7 +85,7 @@ export const creditTransaction = pgTable("credit_transaction", {
|
||||
id: text("id").primaryKey(),
|
||||
userId: text("user_id").notNull().references(() => user.id, { onDelete: 'cascade' }),
|
||||
type: text("type").notNull(), // main type, e.g. REGISTER_GIFT, MONTHLY_REFRESH, PURCHASE, USAGE, EXPIRE
|
||||
description: text("description"), // description, e.g. REGISTER_GIFT, MONTHLY_REFRESH, USAGE, EXPIRE
|
||||
description: text("description"), // description, e.g. "Register gift credits: 100"
|
||||
amount: text("amount").notNull(), // positive for earn, negative for spend
|
||||
remainingAmount: text("remaining_amount"), // for FIFO consumption
|
||||
paymentId: text("payment_id"), // associated payment order, can be null, only has value when purchasing credits
|
||||
|
@ -3,14 +3,17 @@ export const PLACEHOLDER_IMAGE =
|
||||
|
||||
// credit package definition (example)
|
||||
export const CREDIT_PACKAGES = [
|
||||
{ id: 'package-1', credits: 500, price: 5 },
|
||||
{ id: 'package-2', credits: 1200, price: 10 },
|
||||
{ id: 'package-3', credits: 3000, price: 20 },
|
||||
{ id: 'package-1', credits: 1000, price: 10 },
|
||||
{ id: 'package-2', credits: 2500, price: 20 },
|
||||
{ id: 'package-3', credits: 5000, price: 30 },
|
||||
];
|
||||
|
||||
// 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;
|
||||
|
||||
@ -22,12 +25,3 @@ export const CREDIT_TRANSACTION_TYPE = {
|
||||
USAGE: 'USAGE', // credits spent by usage
|
||||
EXPIRE: 'EXPIRE', // credits expired
|
||||
};
|
||||
|
||||
// credit transaction description
|
||||
export const CREDIT_TRANSACTION_DESCRIPTION = {
|
||||
MONTHLY_REFRESH: 'MONTHLY_REFRESH', // credits earned by monthly refresh
|
||||
REGISTER_GIFT: 'REGISTER_GIFT', // credits earned by register gift
|
||||
PURCHASE: 'PURCHASE', // credits earned by purchase
|
||||
USAGE: 'USAGE', // credits spent by usage
|
||||
EXPIRE: 'EXPIRE', // credits expired
|
||||
};
|
||||
|
@ -4,9 +4,9 @@ import { addDays, isAfter } from 'date-fns';
|
||||
import { and, asc, eq, or } from 'drizzle-orm';
|
||||
import {
|
||||
CREDIT_EXPIRE_DAYS,
|
||||
CREDIT_TRANSACTION_DESCRIPTION,
|
||||
CREDIT_TRANSACTION_TYPE,
|
||||
FREE_MONTHLY_CREDITS,
|
||||
REGISTER_GIFT_CREDITS,
|
||||
} from './constants';
|
||||
|
||||
// Get user's current credit balance
|
||||
@ -60,7 +60,7 @@ export async function addCredits({
|
||||
}) {
|
||||
// Process expired credits first
|
||||
await processExpiredCredits(userId);
|
||||
// Update balance
|
||||
// Update user credit balance
|
||||
const current = await db
|
||||
.select()
|
||||
.from(userCredit)
|
||||
@ -72,7 +72,11 @@ export async function addCredits({
|
||||
if (current.length > 0) {
|
||||
await db
|
||||
.update(userCredit)
|
||||
.set({ balance: newBalance, updatedAt: new Date() })
|
||||
.set({
|
||||
balance: newBalance,
|
||||
lastRefresh: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(userCredit.userId, userId));
|
||||
} else {
|
||||
await db.insert(userCredit).values({
|
||||
@ -84,7 +88,7 @@ export async function addCredits({
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
}
|
||||
// Write transaction record
|
||||
// Write credit transaction record
|
||||
await logCreditTransaction({
|
||||
userId,
|
||||
type,
|
||||
@ -230,12 +234,36 @@ export async function processExpiredCredits(userId: string) {
|
||||
userId,
|
||||
type: CREDIT_TRANSACTION_TYPE.EXPIRE,
|
||||
amount: -expiredTotal,
|
||||
description: CREDIT_TRANSACTION_DESCRIPTION.EXPIRE,
|
||||
description: `Expire credits: ${expiredTotal}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add free monthly credits (can be called by scheduler)
|
||||
// Add register gift credits
|
||||
export async function addRegisterGiftCredits(userId: string) {
|
||||
// Check if user has already received register gift credits
|
||||
const record = await db
|
||||
.select()
|
||||
.from(creditTransaction)
|
||||
.where(
|
||||
and(
|
||||
eq(creditTransaction.userId, userId),
|
||||
eq(creditTransaction.type, CREDIT_TRANSACTION_TYPE.REGISTER_GIFT)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
// add register gift credits if user has not received them yet
|
||||
if (record.length === 0) {
|
||||
await addCredits({
|
||||
userId,
|
||||
amount: REGISTER_GIFT_CREDITS,
|
||||
type: CREDIT_TRANSACTION_TYPE.REGISTER_GIFT,
|
||||
description: `Register gift credits: ${REGISTER_GIFT_CREDITS}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add free monthly credits
|
||||
export async function addMonthlyFreeCredits(userId: string) {
|
||||
// Check last refresh time
|
||||
const record = await db
|
||||
@ -245,23 +273,27 @@ export async function addMonthlyFreeCredits(userId: string) {
|
||||
.limit(1);
|
||||
const now = new Date();
|
||||
let canAdd = false;
|
||||
if (!record[0]?.lastRefresh) canAdd = true;
|
||||
else {
|
||||
// never added credits before
|
||||
if (!record[0]?.lastRefresh) {
|
||||
canAdd = true;
|
||||
} else {
|
||||
const last = new Date(record[0].lastRefresh);
|
||||
canAdd =
|
||||
now.getMonth() !== last.getMonth() ||
|
||||
now.getFullYear() !== last.getFullYear();
|
||||
}
|
||||
// add credits if it's a new month
|
||||
if (canAdd) {
|
||||
await addCredits({
|
||||
userId,
|
||||
amount: FREE_MONTHLY_CREDITS,
|
||||
type: CREDIT_TRANSACTION_TYPE.MONTHLY_REFRESH,
|
||||
description: CREDIT_TRANSACTION_DESCRIPTION.MONTHLY_REFRESH,
|
||||
description: `Free monthly credits: ${FREE_MONTHLY_CREDITS} for ${now.getFullYear()}-${now.getMonth() + 1}`,
|
||||
});
|
||||
await db
|
||||
.update(userCredit)
|
||||
.set({ lastRefresh: now, updatedAt: now })
|
||||
.where(eq(userCredit.userId, userId));
|
||||
// update last refresh time ? addCredits has already updated it
|
||||
// await db
|
||||
// .update(userCredit)
|
||||
// .set({ lastRefresh: now, updatedAt: now })
|
||||
// .where(eq(userCredit.userId, userId));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user