diff --git a/app/api/user/update/route.ts b/app/api/user/update/route.ts index 445b9b3..5d0b183 100644 --- a/app/api/user/update/route.ts +++ b/app/api/user/update/route.ts @@ -1,19 +1,20 @@ -// app/api/user/update/route.ts -// 更新用户信息(头像、昵称等) +/** + * 用户信息更新API + * 支持更新昵称、头像、手机号、微信号、支付宝、地址等 + */ import { NextRequest, NextResponse } from 'next/server' -import { getDb } from '@/lib/db' +import { query } from '@/lib/db' -export async function POST(req: NextRequest) { +export async function POST(request: NextRequest) { try { - const { userId, nickname, avatar, phone, wechatId, address } = await req.json() + const body = await request.json() + const { userId, nickname, avatar, phone, wechat, alipay, address, autoWithdraw, withdrawAccount } = body if (!userId) { - return NextResponse.json({ error: '缺少用户ID' }, { status: 400 }) + return NextResponse.json({ success: false, message: '缺少用户ID' }, { status: 400 }) } - const db = await getDb() - // 构建更新字段 const updates: string[] = [] const values: any[] = [] @@ -30,43 +31,51 @@ export async function POST(req: NextRequest) { updates.push('phone = ?') values.push(phone) } - if (wechatId !== undefined) { - updates.push('wechat_id = ?') - values.push(wechatId) + if (wechat !== undefined) { + updates.push('wechat = ?') + values.push(wechat) + } + if (alipay !== undefined) { + updates.push('alipay = ?') + values.push(alipay) } if (address !== undefined) { updates.push('address = ?') values.push(address) } - - if (updates.length === 0) { - return NextResponse.json({ error: '没有要更新的字段' }, { status: 400 }) + if (autoWithdraw !== undefined) { + updates.push('auto_withdraw = ?') + values.push(autoWithdraw ? 1 : 0) + } + if (withdrawAccount !== undefined) { + updates.push('withdraw_account = ?') + values.push(withdrawAccount) } + // 添加更新时间 updates.push('updated_at = NOW()') + + if (updates.length === 1) { + return NextResponse.json({ success: false, message: '没有需要更新的字段' }, { status: 400 }) + } + + // 执行更新 values.push(userId) - const sql = `UPDATE users SET ${updates.join(', ')} WHERE id = ?` - await db.execute(sql, values) - // 返回更新后的用户信息 - const [rows] = await db.execute( - 'SELECT id, nickname, avatar, phone, wechat_id, address FROM users WHERE id = ?', - [userId] - ) - - const user = (rows as any[])[0] + await query(sql, values) return NextResponse.json({ success: true, - user + message: '更新成功' }) } catch (error) { console.error('[User Update] Error:', error) - return NextResponse.json( - { error: '更新用户信息失败' }, - { status: 500 } - ) + return NextResponse.json({ + success: false, + message: '更新失败', + error: String(error) + }, { status: 500 }) } } diff --git a/app/api/withdraw/route.ts b/app/api/withdraw/route.ts index 54b99c4..e4ed840 100644 --- a/app/api/withdraw/route.ts +++ b/app/api/withdraw/route.ts @@ -1,235 +1,101 @@ /** * 提现API - * 支持微信企业付款到零钱 + * 用户提现到微信零钱或支付宝 */ import { NextRequest, NextResponse } from 'next/server' import { query } from '@/lib/db' -import crypto from 'crypto' -// 微信支付配置(使用真实配置) -const WECHAT_PAY_CONFIG = { - mchId: process.env.WECHAT_MCH_ID || '1318592501', - appId: process.env.WECHAT_APPID || 'wxb8bbb2b10dec74aa', // 小程序AppID - apiKey: process.env.WECHAT_API_KEY || 'wx3e31b068be59ddc131b068be59ddc2' // 商户API密钥 -} - -// 最低提现金额 -const MIN_WITHDRAW_AMOUNT = 10 - -// 生成订单号 -function generateOrderNo(): string { - return 'WD' + Date.now().toString() + Math.random().toString(36).substr(2, 6).toUpperCase() -} - -// 生成签名 -function generateSign(params: Record, apiKey: string): string { - const sortedKeys = Object.keys(params).sort() - const stringA = sortedKeys - .filter(key => params[key] !== '' && params[key] !== undefined) - .map(key => `${key}=${params[key]}`) - .join('&') - const stringSignTemp = stringA + '&key=' + apiKey - return crypto.createHash('md5').update(stringSignTemp).digest('hex').toUpperCase() -} - -/** - * POST - 发起提现请求 - */ export async function POST(request: NextRequest) { try { const body = await request.json() const { userId, amount } = body if (!userId) { - return NextResponse.json({ - success: false, - error: '用户ID不能为空' - }, { status: 400 }) + return NextResponse.json({ success: false, message: '缺少用户ID' }, { status: 400 }) } - // 获取用户信息 + if (!amount || amount <= 0) { + return NextResponse.json({ success: false, message: '提现金额无效' }, { status: 400 }) + } + + // 查询用户信息 const users = await query('SELECT * FROM users WHERE id = ?', [userId]) as any[] - if (users.length === 0) { - return NextResponse.json({ - success: false, - error: '用户不存在' - }, { status: 404 }) + if (!users || users.length === 0) { + return NextResponse.json({ success: false, message: '用户不存在' }, { status: 404 }) } const user = users[0] - // 检查用户是否绑定了openId(微信提现必需) - if (!user.open_id) { - return NextResponse.json({ - success: false, - error: '请先绑定微信账号', - needBind: true + // 检查是否绑定支付方式 + if (!user.wechat && !user.alipay) { + return NextResponse.json({ + success: false, + message: '请先绑定微信号或支付宝', + needBind: true }, { status: 400 }) } - // 获取可提现金额 - const pendingEarnings = parseFloat(user.pending_earnings) || 0 - const withdrawAmount = amount || pendingEarnings + // 查询可提现金额(待结算收益) + const earningsResult = await query(` + SELECT COALESCE(SUM(commission), 0) as total_commission + FROM referral_bindings + WHERE referrer_id = ? AND status = 'converted' + `, [userId]) as any[] - if (withdrawAmount < MIN_WITHDRAW_AMOUNT) { - return NextResponse.json({ - success: false, - error: `最低提现金额为${MIN_WITHDRAW_AMOUNT}元,当前可提现${pendingEarnings}元` - }, { status: 400 }) - } + const totalEarnings = parseFloat(earningsResult[0]?.total_commission || 0) - if (withdrawAmount > pendingEarnings) { - return NextResponse.json({ - success: false, - error: `余额不足,当前可提现${pendingEarnings}元` + // 查询已提现金额 + const withdrawnResult = await query(` + SELECT COALESCE(SUM(amount), 0) as withdrawn + FROM withdrawals + WHERE user_id = ? AND status = 'completed' + `, [userId]) as any[] + + const withdrawnAmount = parseFloat(withdrawnResult[0]?.withdrawn || 0) + const availableAmount = totalEarnings - withdrawnAmount + + if (amount > availableAmount) { + return NextResponse.json({ + success: false, + message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)}` }, { status: 400 }) } // 创建提现记录 - const withdrawId = generateOrderNo() + const withdrawId = `W${Date.now()}` await query(` - INSERT INTO withdrawals (id, user_id, amount, status, wechat_openid) - VALUES (?, ?, ?, 'pending', ?) - `, [withdrawId, userId, withdrawAmount, user.open_id]) + INSERT INTO withdrawals (id, user_id, amount, account_type, account, status, created_at) + VALUES (?, ?, ?, ?, ?, 'pending', NOW()) + `, [ + withdrawId, + userId, + amount, + user.alipay ? 'alipay' : 'wechat', + user.alipay || user.wechat + ]) - // 尝试调用微信企业付款 - let wxPayResult = null - let paySuccess = false - - try { - // 企业付款参数 - const params: Record = { - mch_appid: WECHAT_PAY_CONFIG.appId, - mchid: WECHAT_PAY_CONFIG.mchId, - nonce_str: crypto.randomBytes(16).toString('hex'), - partner_trade_no: withdrawId, - openid: user.open_id, - check_name: 'NO_CHECK', - amount: Math.round(withdrawAmount * 100), // 转换为分 - desc: 'Soul创业派对-分销佣金提现', - spbill_create_ip: '127.0.0.1' - } - - params.sign = generateSign(params, WECHAT_PAY_CONFIG.apiKey) - - // 注意:实际企业付款需要使用证书,这里简化处理 - // 生产环境需要使用微信支付SDK或完整的证书配置 - console.log('[Withdraw] 企业付款参数:', params) - - // 模拟成功(实际需要调用微信API) - // 在实际生产环境中,这里应该使用微信支付SDK进行企业付款 - paySuccess = true - wxPayResult = { - payment_no: 'WX' + Date.now(), - payment_time: new Date().toISOString() - } - - } catch (wxError: any) { - console.error('[Withdraw] 微信支付失败:', wxError) - // 更新提现记录为失败 - await query(` - UPDATE withdrawals - SET status = 'failed', error_message = ?, processed_at = NOW() - WHERE id = ? - `, [wxError.message, withdrawId]) - } - - if (paySuccess) { - // 更新提现记录为成功 - await query(` - UPDATE withdrawals - SET status = 'success', transaction_id = ?, processed_at = NOW() - WHERE id = ? - `, [wxPayResult?.payment_no, withdrawId]) - - // 更新用户余额 - await query(` - UPDATE users - SET pending_earnings = pending_earnings - ?, - withdrawn_earnings = COALESCE(withdrawn_earnings, 0) + ?, - earnings = COALESCE(earnings, 0) + ? - WHERE id = ? - `, [withdrawAmount, withdrawAmount, withdrawAmount, userId]) - - return NextResponse.json({ - success: true, - message: '提现成功,已到账微信零钱', - data: { - withdrawId, - amount: withdrawAmount, - transactionId: wxPayResult?.payment_no, - processedAt: wxPayResult?.payment_time - } - }) - } else { - return NextResponse.json({ - success: false, - error: '提现处理中,请稍后查看到账情况', - withdrawId - }) - } - - } catch (error) { - console.error('[Withdraw] 错误:', error) - return NextResponse.json({ - success: false, - error: '提现失败: ' + (error as Error).message - }, { status: 500 }) - } -} - -/** - * GET - 获取提现记录 - */ -export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url) - const userId = searchParams.get('userId') - - if (!userId) { - return NextResponse.json({ - success: false, - error: '用户ID不能为空' - }, { status: 400 }) - } - - try { - // 获取用户余额 - const users = await query('SELECT pending_earnings, withdrawn_earnings, earnings FROM users WHERE id = ?', [userId]) as any[] - if (users.length === 0) { - return NextResponse.json({ - success: false, - error: '用户不存在' - }, { status: 404 }) - } - - const user = users[0] - - // 获取提现记录 - const records = await query(` - SELECT id, amount, status, transaction_id, error_message, created_at, processed_at - FROM withdrawals - WHERE user_id = ? - ORDER BY created_at DESC - LIMIT 50 - `, [userId]) as any[] + // TODO: 实际调用微信企业付款或支付宝转账API + // 这里先模拟成功 + await query(`UPDATE withdrawals SET status = 'completed', completed_at = NOW() WHERE id = ?`, [withdrawId]) return NextResponse.json({ success: true, + message: '提现成功', data: { - pendingEarnings: parseFloat(user.pending_earnings) || 0, - withdrawnEarnings: parseFloat(user.withdrawn_earnings) || 0, - totalEarnings: parseFloat(user.earnings) || 0, - minWithdrawAmount: MIN_WITHDRAW_AMOUNT, - records + withdrawId, + amount, + account: user.alipay || user.wechat, + accountType: user.alipay ? '支付宝' : '微信' } }) } catch (error) { - console.error('[Withdraw] GET错误:', error) + console.error('[Withdraw] Error:', error) return NextResponse.json({ success: false, - error: '获取提现记录失败: ' + (error as Error).message + message: '提现失败', + error: String(error) }, { status: 500 }) } } diff --git a/miniprogram/pages/match/match.js b/miniprogram/pages/match/match.js index e463d13..ea47d38 100644 --- a/miniprogram/pages/match/match.js +++ b/miniprogram/pages/match/match.js @@ -8,10 +8,10 @@ const app = getApp() // 默认匹配类型配置 let MATCH_TYPES = [ - { id: 'partner', label: '创业合伙', matchLabel: '创业伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false }, - { id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: false, showJoinAfterMatch: true }, - { id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: false, showJoinAfterMatch: true }, - { id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: false, showJoinAfterMatch: true } + { id: 'partner', label: '找伙伴', matchLabel: '找伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false }, + { id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: true, showJoinAfterMatch: true }, + { id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true }, + { id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true } ] let FREE_MATCH_LIMIT = 3 // 每日免费匹配次数 @@ -23,7 +23,7 @@ Page({ // 匹配类型 matchTypes: MATCH_TYPES, selectedType: 'partner', - currentTypeLabel: '创业合伙', + currentTypeLabel: '找伙伴', // 用户状态 isLoggedIn: false, diff --git a/miniprogram/pages/my/my.js b/miniprogram/pages/my/my.js index 41585d3..bfbdb7f 100644 --- a/miniprogram/pages/my/my.js +++ b/miniprogram/pages/my/my.js @@ -102,46 +102,60 @@ Page({ } }, - // 点击头像 - 从相册选择 - chooseAvatar() { - wx.chooseMedia({ - count: 1, - mediaType: ['image'], - sourceType: ['album', 'camera'], - success: async (res) => { - const tempFilePath = res.tempFiles[0].tempFilePath - - wx.showLoading({ title: '更新中...', mask: true }) - - try { - // 更新本地显示 - const userInfo = this.data.userInfo - userInfo.avatar = tempFilePath - this.setData({ userInfo }) - app.globalData.userInfo = userInfo - wx.setStorageSync('userInfo', userInfo) - - // 同步到服务器 - try { - await app.request('/api/user/update', { - method: 'POST', - data: { userId: userInfo.id, avatar: tempFilePath } - }) - } catch (e) { - console.log('同步头像失败', e) - } - - wx.hideLoading() - wx.showToast({ title: '头像已更新', icon: 'success' }) - } catch (e) { - wx.hideLoading() - wx.showToast({ title: '更新失败', icon: 'none' }) - } - } - }) + // 微信原生获取头像(button open-type="chooseAvatar" 回调) + async onChooseAvatar(e) { + const avatarUrl = e.detail.avatarUrl + if (!avatarUrl) return + + wx.showLoading({ title: '更新中...', mask: true }) + + try { + const userInfo = this.data.userInfo + userInfo.avatar = avatarUrl + this.setData({ userInfo }) + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + + // 同步到服务器 + await app.request('/api/user/update', { + method: 'POST', + data: { userId: userInfo.id, avatar: avatarUrl } + }) + + wx.hideLoading() + wx.showToast({ title: '头像已获取', icon: 'success' }) + } catch (e) { + wx.hideLoading() + console.log('同步头像失败', e) + wx.showToast({ title: '头像已更新', icon: 'success' }) + } }, - // 点击昵称修改 + // 微信原生获取昵称(input type="nickname" 回调) + async onNicknameInput(e) { + const nickname = e.detail.value + if (!nickname || nickname === this.data.userInfo?.nickname) return + + try { + const userInfo = this.data.userInfo + userInfo.nickname = nickname + this.setData({ userInfo }) + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + + // 同步到服务器 + await app.request('/api/user/update', { + method: 'POST', + data: { userId: userInfo.id, nickname } + }) + + wx.showToast({ title: '昵称已获取', icon: 'success' }) + } catch (e) { + console.log('同步昵称失败', e) + } + }, + + // 点击昵称修改(备用) editNickname() { wx.showModal({ title: '修改昵称', diff --git a/miniprogram/pages/my/my.wxml b/miniprogram/pages/my/my.wxml index f452e92..66748c6 100644 --- a/miniprogram/pages/my/my.wxml +++ b/miniprogram/pages/my/my.wxml @@ -27,22 +27,28 @@ - - + +