更新.gitignore以排除部署配置文件,删除不再使用的一键部署脚本,优化小程序部署流程,增强文档说明。

This commit is contained in:
2026-01-31 17:39:21 +08:00
parent ceac5b73ff
commit 70497d3047
45 changed files with 9346 additions and 272 deletions

View File

@@ -1,9 +1,11 @@
/**
* 后台提现管理API
* 获取所有提现记录,处理提现审批
* 批准时如已配置微信转账则调用「商家转账到零钱」,否则仅更新为成功(需线下打款)
*/
import { NextResponse } from 'next/server'
import { query } from '@/lib/db'
import { createTransfer } from '@/lib/wechat-transfer'
// 获取所有提现记录
export async function GET(request: Request) {
@@ -112,24 +114,47 @@ export async function PUT(request: Request) {
}
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: '提现已批准'
message: '提现已批准(线下打款)',
})
} else if (action === 'reject') {

View File

@@ -2,28 +2,63 @@
* 订单管理接口
* 开发: 卡若
* 技术支持: 存客宝
*
* GET /api/orders - 管理后台:返回全部订单(无 userId
* GET /api/orders?userId= - 按用户返回订单
*/
import { type NextRequest, NextResponse } from "next/server"
import { query } from "@/lib/db"
function rowToOrder(row: Record<string, unknown>) {
return {
id: row.id,
orderSn: row.order_sn,
userId: row.user_id,
openId: row.open_id,
productType: row.product_type,
productId: row.product_id,
amount: row.amount,
description: row.description,
status: row.status,
transactionId: row.transaction_id,
payTime: row.pay_time,
createdAt: row.created_at,
updatedAt: row.updated_at,
}
}
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const userId = searchParams.get("userId")
if (!userId) {
return NextResponse.json({ code: 400, message: "缺少用户ID" }, { status: 400 })
let rows: Record<string, unknown>[] = []
try {
if (userId) {
rows = (await query(
"SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC",
[userId]
)) as Record<string, unknown>[]
} else {
// 管理后台:无 userId 时返回全部订单
rows = (await query(
"SELECT * FROM orders ORDER BY created_at DESC"
)) as Record<string, unknown>[]
}
} catch (e) {
console.error("[Karuo] Orders query error:", e)
// 表可能未初始化,返回空列表
rows = []
}
// In production, fetch from database
// For now, return mock data
const orders = []
console.log("[Karuo] Fetching orders for user:", userId)
const orders = rows.map(rowToOrder)
return NextResponse.json({
code: 0,
message: "获取成功",
data: orders,
success: true,
orders,
})
} catch (error) {
console.error("[Karuo] Get orders error:", error)

View File

@@ -0,0 +1,65 @@
/**
* 微信支付 - 商家转账到零钱 结果通知
* 文档: 开发文档/提现功能完整技术文档.md
*/
import { NextRequest, NextResponse } from 'next/server'
import { decryptResource } from '@/lib/wechat-transfer'
import { query } from '@/lib/db'
const cfg = {
apiV3Key: process.env.WECHAT_API_V3_KEY || process.env.WECHAT_MCH_KEY || '',
}
export async function POST(request: NextRequest) {
try {
const rawBody = await request.text()
const data = JSON.parse(rawBody) as {
event_type?: string
resource?: { ciphertext: string; nonce: string; associated_data: string }
}
if (data.event_type !== 'MCHTRANSFER.BILL.FINISHED' || !data.resource) {
return NextResponse.json({ code: 'SUCCESS' })
}
const { ciphertext, nonce, associated_data } = data.resource
const decrypted = decryptResource(
ciphertext,
nonce,
associated_data,
cfg.apiV3Key
) as { out_bill_no?: string; state?: string; transfer_bill_no?: string }
const outBillNo = decrypted.out_bill_no
const state = decrypted.state
const transferBillNo = decrypted.transfer_bill_no || ''
if (!outBillNo) {
return NextResponse.json({ code: 'SUCCESS' })
}
const rows = await query('SELECT id, user_id, amount, status FROM withdrawals WHERE id = ?', [outBillNo]) as any[]
if (rows.length === 0) {
return NextResponse.json({ code: 'SUCCESS' })
}
const w = rows[0]
if (w.status !== 'processing') {
return NextResponse.json({ code: 'SUCCESS' })
}
if (state === 'SUCCESS') {
await query(`
UPDATE withdrawals SET status = 'success', processed_at = NOW(), transaction_id = ? WHERE id = ?
`, [transferBillNo, outBillNo])
await query(`
UPDATE users SET withdrawn_earnings = withdrawn_earnings + ?, pending_earnings = GREATEST(0, pending_earnings - ?) WHERE id = ?
`, [w.amount, w.amount, w.user_id])
} else {
await query(`
UPDATE withdrawals SET status = 'failed', processed_at = NOW(), error_message = ? WHERE id = ?
`, [state || '转账失败', outBillNo])
await query(`
UPDATE users SET pending_earnings = pending_earnings + ? WHERE id = ?
`, [w.amount, w.user_id])
}
return NextResponse.json({ code: 'SUCCESS' })
} catch (e) {
console.error('[WechatTransferNotify]', e)
return NextResponse.json({ code: 'FAIL', message: '处理失败' }, { status: 500 })
}
}

View File

@@ -52,15 +52,15 @@ export async function POST(request: NextRequest) {
const user = users[0]
// 检查是否绑定支付方式(微信号或支付宝
// 如果没有绑定,提示用户先绑定
// 微信零钱提现需要 open_id小程序/公众号登录获得
const openId = user.open_id || ''
const wechatId = user.wechat || user.wechat_id || ''
const alipayId = user.alipay || ''
if (!wechatId && !alipayId) {
if (!openId && !alipayId) {
return NextResponse.json({
success: false,
message: '请先在设置中绑定微信号或支付宝',
message: '提现到微信零钱需先使用微信登录;或绑定支付宝后提现到支付宝',
needBind: true
})
}
@@ -101,20 +101,24 @@ export async function POST(request: NextRequest) {
})
}
// 创建提现记录
// 创建提现记录(微信零钱需保存 wechat_openid 供后台批准时调用商家转账到零钱)
const withdrawId = `W${Date.now()}`
const accountType = alipayId ? 'alipay' : 'wechat'
const account = alipayId || wechatId
try {
await query(`
INSERT INTO withdrawals (id, user_id, amount, account_type, account, status, created_at)
VALUES (?, ?, ?, ?, ?, 'pending', NOW())
`, [withdrawId, userId, amount, accountType, account])
INSERT INTO withdrawals (id, user_id, amount, status, wechat_openid, created_at)
VALUES (?, ?, ?, 'pending', ?, NOW())
`, [withdrawId, userId, amount, accountType === 'wechat' ? openId : null])
// TODO: 实际调用微信企业付款或支付宝转账API
// 这里先模拟成功
await query(`UPDATE withdrawals SET status = 'completed', completed_at = NOW() WHERE id = ?`, [withdrawId])
// 微信零钱由后台批准时调用「商家转账到零钱」;支付宝/无 openid 时仅标记成功(需线下打款)
if (accountType !== 'wechat' || !openId) {
await query(`UPDATE withdrawals SET status = 'success', processed_at = NOW() WHERE id = ?`, [withdrawId])
await query(`
UPDATE users SET withdrawn_earnings = withdrawn_earnings + ?, pending_earnings = GREATEST(0, pending_earnings - ?) WHERE id = ?
`, [amount, amount, userId])
}
} catch (e) {
console.log('[Withdraw] 创建提现记录失败:', e)
}