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

195 lines
6.0 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
* 获取所有提现记录,处理提现审批
* 批准时如已配置微信转账则调用「商家转账到零钱」,否则仅更新为成功(需线下打款)
*/
import { NextResponse } from 'next/server'
import { query } from '@/lib/db'
import { createTransfer } from '@/lib/wechat-transfer'
// 获取所有提现记录
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const status = searchParams.get('status') // pending, success, failed, all
let sql = `
SELECT
w.*,
u.nickname as user_nickname,
u.phone as user_phone,
u.avatar as user_avatar,
u.referral_code
FROM withdrawals w
LEFT JOIN users u ON w.user_id = u.id
`
if (status && status !== 'all') {
sql += ` WHERE w.status = '${status}'`
}
sql += ` ORDER BY w.created_at DESC LIMIT 100`
const withdrawals = await query(sql) as any[]
// 统计信息
const statsResult = await query(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount,
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as success_count,
SUM(CASE WHEN status = 'success' THEN amount ELSE 0 END) as success_amount,
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count
FROM withdrawals
`) as any[]
const stats = statsResult[0] || {}
return NextResponse.json({
success: true,
withdrawals: withdrawals.map(w => ({
id: w.id,
userId: w.user_id,
userNickname: w.user_nickname || '未知用户',
userPhone: w.user_phone,
userAvatar: w.user_avatar,
referralCode: w.referral_code,
amount: parseFloat(w.amount),
status: w.status,
wechatOpenid: w.wechat_openid,
transactionId: w.transaction_id,
errorMessage: w.error_message,
createdAt: w.created_at,
processedAt: w.processed_at
})),
stats: {
total: parseInt(stats.total) || 0,
pendingCount: parseInt(stats.pending_count) || 0,
pendingAmount: parseFloat(stats.pending_amount) || 0,
successCount: parseInt(stats.success_count) || 0,
successAmount: parseFloat(stats.success_amount) || 0,
failedCount: parseInt(stats.failed_count) || 0
}
})
} catch (error) {
console.error('Get withdrawals error:', error)
return NextResponse.json({
success: false,
error: '获取提现记录失败'
}, { status: 500 })
}
}
// 处理提现(审批/拒绝)
export async function PUT(request: Request) {
try {
const body = await request.json()
const { id, action, reason } = body // action: approve, reject
if (!id || !action) {
return NextResponse.json({
success: false,
error: '缺少必要参数'
}, { status: 400 })
}
// 获取提现记录
const withdrawals = await query('SELECT * FROM withdrawals WHERE id = ?', [id]) as any[]
if (withdrawals.length === 0) {
return NextResponse.json({
success: false,
error: '提现记录不存在'
}, { status: 404 })
}
const withdrawal = withdrawals[0]
if (withdrawal.status !== 'pending') {
return NextResponse.json({
success: false,
error: '该提现记录已处理'
}, { status: 400 })
}
if (action === 'approve') {
const openid = withdrawal.wechat_openid || ''
const amountFen = Math.round(parseFloat(withdrawal.amount) * 100)
if (openid && amountFen > 0) {
const result = await createTransfer({
openid,
amountFen,
outDetailNo: id,
transferRemark: 'Soul创业派对-提现',
})
if (result.success) {
await query(`
UPDATE withdrawals
SET status = 'processing', transaction_id = ?
WHERE id = ?
`, [result.batchId || result.outBatchNo || '', id])
return NextResponse.json({
success: true,
message: '已发起微信转账,等待到账后自动更新状态',
batchId: result.batchId,
})
}
return NextResponse.json({
success: false,
error: result.errorMessage || '微信转账发起失败',
}, { status: 400 })
}
// 无 openid 或金额为 0仅标记为成功线下打款
await query(`
UPDATE withdrawals
SET status = 'success', processed_at = NOW(), transaction_id = ?
WHERE id = ?
`, [`manual_${Date.now()}`, id])
await query(`
UPDATE users
SET withdrawn_earnings = withdrawn_earnings + ?,
pending_earnings = pending_earnings - ?
WHERE id = ?
`, [withdrawal.amount, withdrawal.amount, withdrawal.user_id])
return NextResponse.json({
success: true,
message: '提现已批准(线下打款)',
})
} else if (action === 'reject') {
// 拒绝提现 - 返还用户余额
await query(`
UPDATE withdrawals
SET status = 'failed', processed_at = NOW(), error_message = ?
WHERE id = ?
`, [reason || '管理员拒绝', id])
// 返还用户余额
await query(`
UPDATE users
SET earnings = earnings + ?,
pending_earnings = pending_earnings - ?
WHERE id = ?
`, [withdrawal.amount, withdrawal.amount, withdrawal.user_id])
return NextResponse.json({
success: true,
message: '提现已拒绝,余额已返还'
})
}
return NextResponse.json({
success: false,
error: '无效的操作'
}, { status: 400 })
} catch (error) {
console.error('Process withdrawal error:', error)
return NextResponse.json({
success: false,
error: '处理提现失败'
}, { status: 500 })
}
}