Files
soul-yongping/next-project/app/api/withdraw/route.ts
2026-02-09 14:43:35 +08:00

182 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 - 使用 Prisma ORM + 原始查询(避免枚举未同步导致 500
* 用户提现到微信零钱
*/
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
// 读取系统配置(使用 Prisma
async function getPrismaConfig(key: string): Promise<any> {
try {
const config = await prisma.system_config.findUnique({
where: { config_key: key }
})
return config?.config_value
} catch (e) {
console.warn(`[Config] 读取配置 ${key} 失败:`, e)
return null
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { userId, amount } = body
if (!userId) {
return NextResponse.json({ success: false, message: '缺少用户ID' }, { status: 400 })
}
if (!amount || amount <= 0) {
return NextResponse.json({ success: false, message: '提现金额无效' }, { status: 400 })
}
// 1. 读取最低提现门槛
let minWithdrawAmount = 10 // 默认值
try {
const config = await getPrismaConfig('referral_config')
if (config?.minWithdrawAmount) {
minWithdrawAmount = Number(config.minWithdrawAmount)
}
} catch (e) {
console.warn('[Withdraw] 读取配置失败,使用默认值 10 元')
}
// 检查最低提现门槛
if (amount < minWithdrawAmount) {
return NextResponse.json({
success: false,
message: `最低提现金额为 ¥${minWithdrawAmount},当前 ¥${amount}`
}, { status: 400 })
}
// 2. 查询用户信息Prisma 保证类型安全)
const user = await prisma.users.findUnique({
where: { id: userId },
select: {
id: true,
open_id: true,
wechat_id: true,
withdrawn_earnings: true
}
})
if (!user) {
return NextResponse.json({ success: false, message: '用户不存在' }, { status: 404 })
}
const openId = user.open_id || ''
if (!openId) {
return NextResponse.json({
success: false,
message: '提现到微信零钱需先使用微信登录',
needBind: true
})
}
// 提现需绑定微信号(用于后台展示收款账号)
const wechatId = user.wechat_id?.trim() || ''
if (!wechatId) {
return NextResponse.json({
success: false,
message: '请先到「设置」中绑定微信号后再提现',
needBindWechat: true
})
}
// 3. 计算累计佣金(从 orders 表查询)
let totalCommission = 0
try {
// 读取分成比例
let distributorShare = 0.9 // 默认90%
try {
const config = await getPrismaConfig('referral_config')
if (config?.distributorShare) {
distributorShare = Number(config.distributorShare)
}
} catch (e) {
console.warn('[Withdraw] 读取分成比例失败,使用默认值 90%')
}
// 使用 Prisma 聚合查询
const ordersResult = await prisma.orders.aggregate({
where: {
referrer_id: userId,
status: 'paid'
},
_sum: {
amount: true
}
})
const totalAmount = Number(ordersResult._sum.amount || 0)
totalCommission = totalAmount * distributorShare
console.log('[Withdraw] 佣金计算:')
console.log('- 订单总金额:', totalAmount)
console.log('- 分成比例:', distributorShare * 100 + '%')
console.log('- 累计佣金:', totalCommission)
} catch (e) {
console.log('[Withdraw] 查询收益失败:', e)
}
// 4. 已提现金额与待审核金额(原始查询避免 withdrawals_status 枚举校验)
const [pendingRows, successRows] = await Promise.all([
prisma.$queryRaw<[{ sum_amount: unknown }]>`
SELECT COALESCE(SUM(amount), 0) AS sum_amount FROM withdrawals
WHERE user_id = ${userId} AND status = 'pending'
`,
prisma.$queryRaw<[{ sum_amount: unknown }]>`
SELECT COALESCE(SUM(amount), 0) AS sum_amount FROM withdrawals
WHERE user_id = ${userId} AND status = 'success'
`
])
const pendingWithdrawAmount = Number((pendingRows[0] as { sum_amount: unknown })?.sum_amount ?? 0)
const withdrawnEarnings = Number((successRows[0] as { sum_amount: unknown })?.sum_amount ?? 0)
// 5. 计算可提现金额(不低于 0
const availableAmount = Math.max(0, totalCommission - withdrawnEarnings - pendingWithdrawAmount)
console.log('[Withdraw] 提现验证(完整版):')
console.log('- 累计佣金:', totalCommission)
console.log('- 已提现金额:', withdrawnEarnings)
console.log('- 待审核金额:', pendingWithdrawAmount)
console.log('- 可提现金额 =', availableAmount)
console.log('- 申请提现金额:', amount)
if (amount > availableAmount) {
return NextResponse.json({
success: false,
message: `可提现金额不足。当前可提现 ¥${availableAmount.toFixed(2)}(累计 ¥${totalCommission.toFixed(2)} - 已提现 ¥${withdrawnEarnings.toFixed(2)} - 待审核 ¥${pendingWithdrawAmount.toFixed(2)}`
})
}
// 7. 创建提现记录(原始插入避免 status 枚举校验)
const withdrawId = `W${Date.now()}`
const amountNum = Number(amount)
await prisma.$executeRaw`
INSERT INTO withdrawals (id, user_id, amount, status, wechat_openid, wechat_id, created_at)
VALUES (${withdrawId}, ${userId}, ${amountNum}, 'pending', ${openId}, ${wechatId}, NOW())
`
return NextResponse.json({
success: true,
message: '提现申请已提交,通过后会在我的页面提醒您收款!',
data: {
withdrawId,
amount: amountNum,
accountType: '微信',
status: 'pending'
}
})
} catch (error) {
console.error('[Withdraw] Error:', error)
return NextResponse.json({
success: false,
message: '提现失败: ' + String(error)
}, { status: 500 })
}
}