feat: add admin wallet recharge order actions

This commit is contained in:
JetSprow
2026-05-01 05:28:20 +10:00
parent 8034392408
commit 1dd3157177
9 changed files with 333 additions and 21 deletions

View File

@@ -0,0 +1,153 @@
"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { prisma } from "@/lib/prisma";
import { requireAdmin } from "@/lib/require-auth";
import { actorFromSession, recordAuditLog } from "@/services/audit";
import { processWalletRechargeOrderSuccess } from "@/services/wallet";
const rechargeOrderIdSchema = z.string().trim().min(1, "充值订单不存在");
function manualTradeNo(orderId: string) {
return `WR-MANUAL-${Date.now()}-${orderId.slice(0, 8)}`;
}
function revalidateRechargeOrderViews(orderId: string) {
revalidatePath("/admin/orders");
revalidatePath("/wallet");
revalidatePath(`/wallet/recharge/${orderId}`);
}
export async function confirmAdminWalletRecharge(rechargeOrderId: string) {
const session = await requireAdmin();
const id = rechargeOrderIdSchema.parse(rechargeOrderId);
const recharge = await prisma.walletRechargeOrder.findUnique({
where: { id },
include: { user: { select: { email: true, name: true } } },
});
if (!recharge) {
throw new Error("充值订单不存在或已被删除");
}
if (recharge.status !== "PENDING") {
throw new Error("只能确认待支付充值订单");
}
const tradeNo = recharge.tradeNo ?? manualTradeNo(recharge.id);
const paymentRef = recharge.paymentRef ?? `manual:${session.user.id}:${Date.now()}`;
const result = await processWalletRechargeOrderSuccess({
rechargeOrderId: recharge.id,
paidAmount: Number(recharge.amount),
paymentMethod: recharge.paymentMethod ?? "manual",
paymentRef,
tradeNo,
description: "管理员手动确认余额充值",
metadata: {
manual: true,
adminUserId: session.user.id,
adminEmail: session.user.email ?? null,
},
});
if (result.finalStatus !== "PAID") {
throw new Error("充值订单状态已变化,请刷新后重试");
}
await recordAuditLog({
actor: actorFromSession(session),
action: "wallet_recharge.confirm",
targetType: "WalletRechargeOrder",
targetId: recharge.id,
targetLabel: recharge.user.email,
message: `手动确认充值订单 ${recharge.id},入账 ¥${Number(recharge.amount).toFixed(2)}`,
metadata: {
userEmail: recharge.user.email,
amount: Number(recharge.amount),
tradeNo,
paymentRef,
},
});
revalidateRechargeOrderViews(recharge.id);
}
export async function cancelAdminWalletRecharge(rechargeOrderId: string) {
const session = await requireAdmin();
const id = rechargeOrderIdSchema.parse(rechargeOrderId);
const recharge = await prisma.walletRechargeOrder.findUnique({
where: { id },
include: { user: { select: { email: true, name: true } } },
});
if (!recharge) {
throw new Error("充值订单不存在或已被删除");
}
if (recharge.status !== "PENDING") {
throw new Error("只能取消待支付充值订单");
}
await prisma.walletRechargeOrder.update({
where: { id: recharge.id },
data: {
status: "CANCELLED",
paymentUrl: null,
expireAt: null,
note: "管理员手动取消",
},
});
await recordAuditLog({
actor: actorFromSession(session),
action: "wallet_recharge.cancel",
targetType: "WalletRechargeOrder",
targetId: recharge.id,
targetLabel: recharge.user.email,
message: `取消充值订单 ${recharge.id}`,
metadata: {
userEmail: recharge.user.email,
amount: Number(recharge.amount),
tradeNo: recharge.tradeNo,
paymentMethod: recharge.paymentMethod,
},
});
revalidateRechargeOrderViews(recharge.id);
}
export async function deleteAdminWalletRecharge(rechargeOrderId: string) {
const session = await requireAdmin();
const id = rechargeOrderIdSchema.parse(rechargeOrderId);
const recharge = await prisma.walletRechargeOrder.findUnique({
where: { id },
include: {
user: { select: { email: true, name: true } },
transactions: { select: { id: true }, take: 5 },
},
});
if (!recharge) {
throw new Error("充值订单不存在或已被删除");
}
await prisma.walletRechargeOrder.delete({ where: { id: recharge.id } });
await recordAuditLog({
actor: actorFromSession(session),
action: "wallet_recharge.delete",
targetType: "WalletRechargeOrder",
targetId: recharge.id,
targetLabel: recharge.user.email,
message: `删除充值订单 ${recharge.id}`,
metadata: {
userEmail: recharge.user.email,
amount: Number(recharge.amount),
status: recharge.status,
tradeNo: recharge.tradeNo,
paymentMethod: recharge.paymentMethod,
keptWalletTransactions: recharge.transactions.length,
},
});
revalidateRechargeOrderViews(recharge.id);
}