mirror of
https://github.com/JetSprow/J-Board-Lite.git
synced 2026-05-01 01:14:10 +05:30
feat: polish wallet recharge cards
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import crypto from "crypto";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { prisma, type DbClient } from "@/lib/prisma";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
import { createNotification } from "@/services/notifications";
|
||||
import { provisionSubscriptionWithDb } from "@/services/provision";
|
||||
import { getPaymentProviderName } from "@/services/payment/catalog";
|
||||
@@ -185,6 +186,38 @@ function getPlanCardGenerationLimit(planType: "PROXY" | "STREAMING", availabilit
|
||||
return limits.length > 0 ? Math.min(...limits) : null;
|
||||
}
|
||||
|
||||
function getUnavailableRechargeCardMessage(card: {
|
||||
status: "UNUSED" | "REDEEMED" | "EXPIRED" | "DISABLED";
|
||||
redeemedAt: Date | null;
|
||||
expiresAt: Date | null;
|
||||
}) {
|
||||
if (card.status === "REDEEMED") {
|
||||
return card.redeemedAt
|
||||
? `这张充值卡已在 ${formatDate(card.redeemedAt)} 兑换,不能重复使用。`
|
||||
: "这张充值卡已兑换,不能重复使用。";
|
||||
}
|
||||
if (card.status === "EXPIRED") {
|
||||
return card.expiresAt
|
||||
? `这张充值卡已于 ${formatDate(card.expiresAt)} 过期。`
|
||||
: "这张充值卡已过期。";
|
||||
}
|
||||
if (card.status === "DISABLED") {
|
||||
return "这张充值卡已停用,请联系管理员处理。";
|
||||
}
|
||||
return "这张充值卡当前不可兑换。";
|
||||
}
|
||||
|
||||
export type RedeemRechargeCardResult =
|
||||
| {
|
||||
type: "BALANCE";
|
||||
amount: number;
|
||||
balanceAfter: number;
|
||||
}
|
||||
| {
|
||||
type: "PLAN";
|
||||
planName: string;
|
||||
};
|
||||
|
||||
export async function payOrderWithWallet(orderId: string, userId: string) {
|
||||
const tradeNo = `BAL-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
||||
|
||||
@@ -379,11 +412,12 @@ export async function createRechargeCards(input: {
|
||||
});
|
||||
}
|
||||
|
||||
export async function redeemRechargeCard(userId: string, rawCode: string) {
|
||||
export async function redeemRechargeCard(userId: string, rawCode: string): Promise<RedeemRechargeCardResult> {
|
||||
const code = rawCode.trim().toUpperCase();
|
||||
if (!code) throw new Error("请输入充值卡卡密");
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const redeemedAt = new Date();
|
||||
const card = await tx.rechargeCard.findUnique({
|
||||
where: { code },
|
||||
include: {
|
||||
@@ -398,27 +432,45 @@ export async function redeemRechargeCard(userId: string, rawCode: string) {
|
||||
});
|
||||
|
||||
if (!card) throw new Error("充值卡不存在");
|
||||
if (card.status !== "UNUSED") throw new Error("这张充值卡已使用或已停用");
|
||||
if (card.expiresAt && card.expiresAt < new Date()) {
|
||||
await tx.rechargeCard.update({
|
||||
where: { id: card.id },
|
||||
if (card.status !== "UNUSED") throw new Error(getUnavailableRechargeCardMessage(card));
|
||||
if (card.expiresAt && card.expiresAt < redeemedAt) {
|
||||
await tx.rechargeCard.updateMany({
|
||||
where: { id: card.id, status: "UNUSED" },
|
||||
data: { status: "EXPIRED" },
|
||||
});
|
||||
throw new Error("这张充值卡已过期");
|
||||
throw new Error(getUnavailableRechargeCardMessage({ ...card, status: "EXPIRED" }));
|
||||
}
|
||||
|
||||
const claimed = await tx.rechargeCard.updateMany({
|
||||
where: { id: card.id, status: "UNUSED" },
|
||||
data: {
|
||||
status: "REDEEMED",
|
||||
redeemedById: userId,
|
||||
redeemedAt,
|
||||
},
|
||||
});
|
||||
if (claimed.count === 0) {
|
||||
throw new Error("这张充值卡刚刚已被兑换,请刷新后查看最新状态。");
|
||||
}
|
||||
|
||||
let result: RedeemRechargeCardResult;
|
||||
if (card.type === "BALANCE") {
|
||||
if (!card.balanceAmount || Number(card.balanceAmount) <= 0) {
|
||||
throw new Error("余额充值卡金额无效");
|
||||
}
|
||||
|
||||
await creditWallet(tx, {
|
||||
const wallet = await creditWallet(tx, {
|
||||
userId,
|
||||
amount: card.balanceAmount,
|
||||
type: "CARD_REDEEM",
|
||||
description: "兑换余额充值卡",
|
||||
rechargeCardId: card.id,
|
||||
});
|
||||
result = {
|
||||
type: "BALANCE",
|
||||
amount: Number(card.balanceAmount),
|
||||
balanceAfter: Number(wallet.balance),
|
||||
};
|
||||
} else {
|
||||
if (!card.plan) throw new Error("套餐充值卡绑定的套餐不存在");
|
||||
const { selectedInboundId, trafficGb } = resolvePlanCardConfig({
|
||||
@@ -445,17 +497,9 @@ export async function redeemRechargeCard(userId: string, rawCode: string) {
|
||||
});
|
||||
|
||||
await provisionSubscriptionWithDb(order, tx);
|
||||
result = { type: "PLAN", planName: card.plan.name };
|
||||
}
|
||||
|
||||
await tx.rechargeCard.update({
|
||||
where: { id: card.id },
|
||||
data: {
|
||||
status: "REDEEMED",
|
||||
redeemedById: userId,
|
||||
redeemedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
await createNotification(
|
||||
{
|
||||
userId,
|
||||
@@ -471,7 +515,7 @@ export async function redeemRechargeCard(userId: string, rawCode: string) {
|
||||
tx,
|
||||
);
|
||||
|
||||
return card.type;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user