Files
soul-yongping/app/api/admin/withdrawals/route.ts

188 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 后台提现 API - 使用 Prisma ORM
* GET: 查提现记录(包含用户信息、收款账号=微信号)
* PUT: 审批提现 = 调用微信打款 + 更新状态 + 更新用户已提现金额
*/
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { requireAdminResponse } from '@/lib/admin-auth'
import { createTransfer } from '@/lib/wechat-transfer'
// ========== GET查询提现记录带用户信息、收款账号=微信号)==========
export async function GET(request: Request) {
console.log('[Withdrawals] GET 开始')
try {
const authErr = requireAdminResponse(request)
if (authErr) return authErr
const withdrawalsData = await prisma.withdrawals.findMany({
take: 100,
orderBy: { created_at: 'desc' },
select: {
id: true,
user_id: true,
amount: true,
status: true,
created_at: true,
wechat_id: true,
}
})
const userIds = [...new Set(withdrawalsData.map(w => w.user_id))]
const users = await prisma.users.findMany({
where: { id: { in: userIds } },
select: { id: true, nickname: true, avatar: true, wechat_id: true }
})
const userMap = new Map(users.map(u => [u.id, u]))
const withdrawals = withdrawalsData.map(w => {
const user = userMap.get(w.user_id)
return {
id: w.id,
user_id: w.user_id,
user_name: user?.nickname || '未知用户',
userAvatar: user?.avatar,
amount: Number(w.amount),
status: w.status === 'success' ? 'completed' :
w.status === 'failed' ? 'rejected' :
w.status,
created_at: w.created_at,
method: 'wechat',
account: w.wechat_id || user?.wechat_id || '未绑定微信号',
}
})
return NextResponse.json({
success: true,
withdrawals,
stats: { total: withdrawals.length },
})
} catch (error: any) {
console.error('[Withdrawals] GET 失败:', error?.message)
return NextResponse.json(
{ success: false, error: '获取提现记录失败: ' + (error?.message || String(error)) },
{ status: 500 }
)
}
}
// ========== PUT审批提现使用 Prisma 事务)==========
export async function PUT(request: Request) {
const STEP = '[Withdrawals PUT]'
try {
console.log(STEP, '1. 开始')
const authErr = requireAdminResponse(request)
if (authErr) return authErr
const body = await request.json()
const { id, action, errorMessage, reason } = body
const rejectReason = errorMessage || reason || '管理员拒绝'
if (!id || !action) {
return NextResponse.json({ success: false, error: '缺少 id 或 action' }, { status: 400 })
}
console.log(STEP, '2. id/action 有效', id, action)
const withdrawal = await prisma.withdrawals.findUnique({
where: { id },
select: { id: true, user_id: true, amount: true, status: true, wechat_openid: true }
})
if (!withdrawal) {
return NextResponse.json({ success: false, error: '提现记录不存在' }, { status: 404 })
}
if (withdrawal.status !== 'pending') {
return NextResponse.json({ success: false, error: '该记录已处理,不可重复审批' }, { status: 400 })
}
const amount = Number(withdrawal.amount)
const userId = withdrawal.user_id
const openid = withdrawal.wechat_openid
if (action === 'approve') {
console.log(STEP, '3. 发起微信打款')
if (!openid) {
return NextResponse.json({
success: false,
error: '该提现记录无微信 openid无法打款请线下处理'
}, { status: 400 })
}
const amountFen = Math.round(amount * 100)
const transferResult = await createTransfer({
openid,
amountFen,
outDetailNo: id,
transferRemark: '提现',
})
if (!transferResult.success) {
console.error(STEP, '微信打款失败:', transferResult.errorCode, transferResult.errorMessage)
await prisma.withdrawals.update({
where: { id },
data: {
status: 'failed',
processed_at: new Date(),
error_message: transferResult.errorMessage || transferResult.errorCode || '打款失败'
}
})
return NextResponse.json({
success: false,
error: '微信打款失败: ' + (transferResult.errorMessage || transferResult.errorCode || '未知错误')
}, { status: 500 })
}
const batchNo = transferResult.outBatchNo || `B${Date.now()}`
console.log(STEP, '4. 打款成功,更新数据库')
await prisma.$transaction(async (tx) => {
await tx.withdrawals.update({
where: { id },
data: {
status: 'success',
processed_at: new Date(),
transaction_id: transferResult.batchId || batchNo
}
})
const user = await tx.users.findUnique({
where: { id: userId },
select: { withdrawn_earnings: true }
})
await tx.users.update({
where: { id: userId },
data: {
withdrawn_earnings: Number(user?.withdrawn_earnings || 0) + amount
}
})
})
console.log(STEP, '5. 批准完成,钱已打到用户微信零钱')
return NextResponse.json({
success: true,
message: '已批准并打款成功,款项将到账用户微信零钱'
})
}
if (action === 'reject') {
console.log(STEP, '5. 执行拒绝操作')
await prisma.withdrawals.update({
where: { id },
data: {
status: 'failed',
processed_at: new Date(),
error_message: rejectReason
}
})
console.log(STEP, '6. 拒绝完成')
return NextResponse.json({ success: true, message: '已拒绝该提现申请' })
}
return NextResponse.json({ success: false, error: '无效的 action' }, { status: 400 })
} catch (error: any) {
console.error(STEP, '!!! 崩溃 !!!', error?.message)
console.error(STEP, '堆栈:', error?.stack)
return NextResponse.json(
{ success: false, error: '审批操作失败: ' + (error?.message || String(error)) },
{ status: 500 }
)
}
}