feat: implement consume credits action and get credit balance action, update credits balance references
This commit is contained in:
parent
263440742a
commit
de1ccca27b
@ -624,7 +624,7 @@
|
|||||||
"remainingAmount": "剩余金额",
|
"remainingAmount": "剩余金额",
|
||||||
"paymentId": "支付编号",
|
"paymentId": "支付编号",
|
||||||
"expirationDate": "过期日期",
|
"expirationDate": "过期日期",
|
||||||
"expirationDateProcessedAt": "过期日期处理时间",
|
"expirationDateProcessedAt": "过期处理时间",
|
||||||
"createdAt": "创建时间",
|
"createdAt": "创建时间",
|
||||||
"updatedAt": "更新时间"
|
"updatedAt": "更新时间"
|
||||||
},
|
},
|
||||||
|
43
src/actions/consume-credits.ts
Normal file
43
src/actions/consume-credits.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import { consumeCredits } from '@/credits/credits';
|
||||||
|
import { getSession } from '@/lib/server';
|
||||||
|
import { createSafeActionClient } from 'next-safe-action';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const actionClient = createSafeActionClient();
|
||||||
|
|
||||||
|
// consume credits schema
|
||||||
|
const consumeSchema = z.object({
|
||||||
|
amount: z.number().min(1),
|
||||||
|
description: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume credits
|
||||||
|
*/
|
||||||
|
export const consumeCreditsAction = actionClient
|
||||||
|
.schema(consumeSchema)
|
||||||
|
.action(async ({ parsedInput }) => {
|
||||||
|
const session = await getSession();
|
||||||
|
if (!session) {
|
||||||
|
console.warn('unauthorized request to consume credits');
|
||||||
|
return { success: false, error: 'Unauthorized' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await consumeCredits({
|
||||||
|
userId: session.user.id,
|
||||||
|
amount: parsedInput.amount,
|
||||||
|
description:
|
||||||
|
parsedInput.description || `Consume credits: ${parsedInput.amount}`,
|
||||||
|
});
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('consume credits error:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Something went wrong',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -1,61 +0,0 @@
|
|||||||
'use server';
|
|
||||||
|
|
||||||
import {
|
|
||||||
addMonthlyFreeCredits,
|
|
||||||
addRegisterGiftCredits,
|
|
||||||
consumeCredits,
|
|
||||||
getUserCredits,
|
|
||||||
} from '@/credits/credits';
|
|
||||||
import { getSession } from '@/lib/server';
|
|
||||||
import { createSafeActionClient } from 'next-safe-action';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
const actionClient = createSafeActionClient();
|
|
||||||
|
|
||||||
// get current user's credits
|
|
||||||
export const getCreditsAction = actionClient.action(async () => {
|
|
||||||
const session = await getSession();
|
|
||||||
if (!session) return { success: false, error: 'Unauthorized' };
|
|
||||||
const credits = await getUserCredits(session.user.id);
|
|
||||||
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),
|
|
||||||
description: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const consumeCreditsAction = actionClient
|
|
||||||
.schema(consumeSchema)
|
|
||||||
.action(async ({ parsedInput }) => {
|
|
||||||
const session = await getSession();
|
|
||||||
if (!session) return { success: false, error: 'Unauthorized' };
|
|
||||||
try {
|
|
||||||
await consumeCredits({
|
|
||||||
userId: session.user.id,
|
|
||||||
amount: parsedInput.amount,
|
|
||||||
description:
|
|
||||||
parsedInput.description || `Consume credits: ${parsedInput.amount}`,
|
|
||||||
});
|
|
||||||
return { success: true };
|
|
||||||
} catch (e) {
|
|
||||||
return { success: false, error: (e as Error).message };
|
|
||||||
}
|
|
||||||
});
|
|
21
src/actions/get-credit-balance.ts
Normal file
21
src/actions/get-credit-balance.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import { getUserCredits } from '@/credits/credits';
|
||||||
|
import { getSession } from '@/lib/server';
|
||||||
|
import { createSafeActionClient } from 'next-safe-action';
|
||||||
|
|
||||||
|
const actionClient = createSafeActionClient();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current user's credits
|
||||||
|
*/
|
||||||
|
export const getCreditBalanceAction = actionClient.action(async () => {
|
||||||
|
const session = await getSession();
|
||||||
|
if (!session) {
|
||||||
|
console.warn('unauthorized request to get credit balance');
|
||||||
|
return { success: false, error: 'Unauthorized' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const credits = await getUserCredits(session.user.id);
|
||||||
|
return { success: true, credits };
|
||||||
|
});
|
@ -45,11 +45,13 @@ export const getCreditTransactionsAction = actionClient
|
|||||||
try {
|
try {
|
||||||
const { pageIndex, pageSize, search, sorting } = parsedInput;
|
const { pageIndex, pageSize, search, sorting } = parsedInput;
|
||||||
|
|
||||||
|
// search by type, amount, paymentId, description
|
||||||
const where = search
|
const where = search
|
||||||
? or(
|
? or(
|
||||||
ilike(creditTransaction.description, `%${search}%`),
|
|
||||||
ilike(creditTransaction.type, `%${search}%`),
|
ilike(creditTransaction.type, `%${search}%`),
|
||||||
ilike(creditTransaction.paymentId, `%${search}%`)
|
ilike(creditTransaction.amount, `%${search}%`),
|
||||||
|
ilike(creditTransaction.paymentId, `%${search}%`),
|
||||||
|
ilike(creditTransaction.description, `%${search}%`)
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { getCreditsAction } from '@/actions/credits.action';
|
import { getCreditBalanceAction } from '@/actions/get-credit-balance';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useLocaleRouter } from '@/i18n/navigation';
|
import { useLocaleRouter } from '@/i18n/navigation';
|
||||||
import { Routes } from '@/routes';
|
import { Routes } from '@/routes';
|
||||||
@ -17,7 +17,7 @@ export function CreditsBalanceButton() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchCredits = async () => {
|
const fetchCredits = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await getCreditsAction();
|
const result = await getCreditBalanceAction();
|
||||||
if (result?.data?.success && result.data.credits !== undefined) {
|
if (result?.data?.success && result.data.credits !== undefined) {
|
||||||
setCredits(result.data.credits);
|
setCredits(result.data.credits);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { getCreditsAction } from '@/actions/credits.action';
|
import { getCreditBalanceAction } from '@/actions/get-credit-balance';
|
||||||
import { useLocaleRouter } from '@/i18n/navigation';
|
import { useLocaleRouter } from '@/i18n/navigation';
|
||||||
import { Routes } from '@/routes';
|
import { Routes } from '@/routes';
|
||||||
import { useTransactionStore } from '@/stores/transaction-store';
|
import { useTransactionStore } from '@/stores/transaction-store';
|
||||||
@ -18,7 +18,7 @@ export function CreditsBalanceMenu() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchCredits = async () => {
|
const fetchCredits = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await getCreditsAction();
|
const result = await getCreditBalanceAction();
|
||||||
if (result?.data?.success && result.data.credits !== undefined) {
|
if (result?.data?.success && result.data.credits !== undefined) {
|
||||||
setCredits(result.data.credits);
|
setCredits(result.data.credits);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { getCreditsAction } from '@/actions/credits.action';
|
import { getCreditBalanceAction } from '@/actions/get-credit-balance';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -44,7 +44,7 @@ export function CreditPackages() {
|
|||||||
const fetchCredits = async () => {
|
const fetchCredits = async () => {
|
||||||
try {
|
try {
|
||||||
setLoadingCredits(true);
|
setLoadingCredits(true);
|
||||||
const result = await getCreditsAction();
|
const result = await getCreditBalanceAction();
|
||||||
if (result?.data?.success) {
|
if (result?.data?.success) {
|
||||||
console.log('CreditPackages, fetched credits:', result.data.credits);
|
console.log('CreditPackages, fetched credits:', result.data.credits);
|
||||||
setCredits(result.data.credits || 0);
|
setCredits(result.data.credits || 0);
|
||||||
|
@ -828,7 +828,7 @@ export class StripeProvider implements PaymentProvider {
|
|||||||
userId,
|
userId,
|
||||||
amount: Number.parseInt(credits),
|
amount: Number.parseInt(credits),
|
||||||
type: CREDIT_TRANSACTION_TYPE.PURCHASE,
|
type: CREDIT_TRANSACTION_TYPE.PURCHASE,
|
||||||
description: `+${credits} credits for package ${packageId} (${amount})`,
|
description: `+${credits} credits for package ${packageId} ($${amount.toLocaleString()})`,
|
||||||
paymentId: session.id,
|
paymentId: session.id,
|
||||||
expireDays: creditPackage.expireDays,
|
expireDays: creditPackage.expireDays,
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user