/** * 后台提现 API - 使用 Prisma ORM * GET: 查提现记录(包含用户信息、收款账号=微信号) * PUT: 审批提现 = 调用微信打款 + 更新状态 + 更新用户已提现金额 */ import { NextResponse } from 'next/server' import { prisma } from '@/lib/prisma' import { requireAdminResponse } from '@/lib/admin-auth' import { createTransferUserConfirm } 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 === 'pending_confirm' ? 'pending_confirm' : 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. 发起转账(用户确认模式)') console.log(STEP, '审核传入: id=', id, 'userId=', userId, 'amount=', amount, 'openid=', openid ? `${openid.slice(0, 8)}...` : '(空)') if (!openid) { return NextResponse.json({ success: false, error: '该提现记录无微信 openid,无法打款,请线下处理' }, { status: 400 }) } const amountFen = Math.round(amount * 100) console.log(STEP, '调用 createTransferUserConfirm: outBillNo=', id, 'amountFen=', amountFen, 'openid 长度=', openid.length) const transferResult = await createTransferUserConfirm({ openid, amountFen, outBillNo: 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 state = transferResult.state || '' const hasPackage = !!(transferResult.packageInfo && (state === 'WAIT_USER_CONFIRM' || state === 'TRANSFERING' || state === 'ACCEPTED' || state === 'PROCESSING')) if (hasPackage && transferResult.packageInfo) { console.log(STEP, '4. 待用户确认收款,保存 package_info') await prisma.withdrawals.update({ where: { id }, data: { status: 'pending_confirm', transfer_bill_no: transferResult.transferBillNo || null, package_info: transferResult.packageInfo, transaction_id: transferResult.transferBillNo || undefined, } }) return NextResponse.json({ success: true, message: '已发起转账,请通知用户在小程序「分销中心」或「我的」中点击「确认收款」完成到账' }) } if (state === 'SUCCESS') { 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.transferBillNo || undefined, transfer_bill_no: transferResult.transferBillNo || undefined, } }) 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 } }) }) return NextResponse.json({ success: true, message: '已批准并打款成功,款项将到账用户微信零钱' }) } await prisma.withdrawals.update({ where: { id }, data: { status: 'pending_confirm', transfer_bill_no: transferResult.transferBillNo || null, package_info: transferResult.packageInfo || null, transaction_id: transferResult.transferBillNo || undefined, } }) 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 } ) } }