优化提现逻辑,修正可提现金额计算方式,确保准确反映累计佣金、已提现金额和待审核金额。同时,更新小程序界面,调整可提现金额的显示和按钮状态判断,提升用户体验。
This commit is contained in:
@@ -43,7 +43,7 @@ export default function AdminLayout({ children }: { children: React.ReactNode })
|
||||
// 简化菜单:按功能归类,保留核心功能
|
||||
// PDF需求:分账管理、分销管理、订单管理三合一 → 交易中心
|
||||
const menuItems = [
|
||||
{ icon: LayoutDashboard, label: "数据概览123", href: "/admin" },
|
||||
{ icon: LayoutDashboard, label: "数据概览147", href: "/admin" },
|
||||
{ icon: BookOpen, label: "内容管理", href: "/admin/content" },
|
||||
{ icon: Users, label: "用户管理", href: "/admin/users" },
|
||||
{ icon: Wallet, label: "交易中心", href: "/admin/distribution" }, // 合并:分销+订单+提现
|
||||
|
||||
@@ -84,39 +84,75 @@ export async function POST(request: NextRequest) {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询可提现金额(待结算收益)
|
||||
let totalEarnings = 0
|
||||
// ✅ 修正:从 orders 表查询累计佣金(与前端逻辑一致)
|
||||
let totalCommission = 0
|
||||
try {
|
||||
const earningsResult = await query(`
|
||||
SELECT COALESCE(SUM(commission), 0) as total_commission
|
||||
FROM referral_bindings
|
||||
WHERE referrer_id = ? AND status = 'converted'
|
||||
// 读取分成比例
|
||||
let distributorShare = 0.9 // 默认90%
|
||||
try {
|
||||
const config = await getConfig('referral_config')
|
||||
if (config?.distributorShare) {
|
||||
distributorShare = Number(config.distributorShare)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Withdraw] 读取分成比例失败,使用默认值 90%')
|
||||
}
|
||||
|
||||
// 查询订单总金额
|
||||
const ordersResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as total_amount
|
||||
FROM orders
|
||||
WHERE referrer_id = ? AND status = 'paid'
|
||||
`, [userId]) as any[]
|
||||
totalEarnings = parseFloat(earningsResult[0]?.total_commission || 0)
|
||||
|
||||
const totalAmount = parseFloat(ordersResult[0]?.total_amount || 0)
|
||||
totalCommission = totalAmount * distributorShare
|
||||
|
||||
console.log('[Withdraw] 佣金计算:')
|
||||
console.log('- 订单总金额:', totalAmount)
|
||||
console.log('- 分成比例:', distributorShare * 100 + '%')
|
||||
console.log('- 累计佣金:', totalCommission)
|
||||
} catch (e) {
|
||||
// 如果表不存在,收益为0
|
||||
console.log('[Withdraw] 查询收益失败,可能表不存在')
|
||||
console.log('[Withdraw] 查询收益失败,可能表不存在:', e)
|
||||
}
|
||||
|
||||
// 查询已提现金额
|
||||
let withdrawnAmount = 0
|
||||
let withdrawnEarnings = 0
|
||||
try {
|
||||
const withdrawnResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as withdrawn
|
||||
FROM withdrawals
|
||||
WHERE user_id = ? AND status = 'completed'
|
||||
`, [userId]) as any[]
|
||||
withdrawnAmount = parseFloat(withdrawnResult[0]?.withdrawn || 0)
|
||||
withdrawnEarnings = parseFloat(user.withdrawn_earnings) || 0
|
||||
} catch (e) {
|
||||
// 如果表不存在,已提现为0
|
||||
console.log('[Withdraw] 读取已提现金额失败:', e)
|
||||
}
|
||||
|
||||
const availableAmount = totalEarnings - withdrawnAmount
|
||||
// 查询待审核提现金额
|
||||
let pendingWithdrawAmount = 0
|
||||
try {
|
||||
const pendingResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as pending_amount
|
||||
FROM withdrawals
|
||||
WHERE user_id = ? AND status = 'pending'
|
||||
`, [userId]) as any[]
|
||||
pendingWithdrawAmount = parseFloat(pendingResult[0]?.pending_amount || 0)
|
||||
} catch (e) {
|
||||
console.log('[Withdraw] 查询待审核金额失败:', e)
|
||||
}
|
||||
|
||||
// ✅ 修正:可提现金额 = 累计佣金 - 已提现金额 - 待审核金额(三元素完整校验)
|
||||
const availableAmount = totalCommission - withdrawnEarnings - pendingWithdrawAmount
|
||||
|
||||
console.log('[Withdraw] 提现验证(完整版):')
|
||||
console.log('- 累计佣金 (totalCommission):', totalCommission)
|
||||
console.log('- 已提现金额 (withdrawnEarnings):', withdrawnEarnings)
|
||||
console.log('- 待审核金额 (pendingWithdrawAmount):', pendingWithdrawAmount)
|
||||
console.log('- 可提现金额 = 累计 - 已提现 - 待审核 =', totalCommission, '-', withdrawnEarnings, '-', pendingWithdrawAmount, '=', availableAmount)
|
||||
console.log('- 申请提现金额 (amount):', amount)
|
||||
console.log('- 判断:', amount, '>', availableAmount, '=', amount > availableAmount)
|
||||
|
||||
if (amount > availableAmount) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)}`
|
||||
message: `可提现金额不足。当前可提现 ¥${availableAmount.toFixed(2)}(累计 ¥${totalCommission.toFixed(2)} - 已提现 ¥${withdrawnEarnings.toFixed(2)} - 待审核 ¥${pendingWithdrawAmount.toFixed(2)})`
|
||||
})
|
||||
}
|
||||
|
||||
@@ -144,12 +180,13 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '提现成功',
|
||||
message: '提现申请已提交,正在审核中,通过后会自动到账您的微信零钱',
|
||||
data: {
|
||||
withdrawId,
|
||||
amount,
|
||||
account,
|
||||
accountType: accountType === 'alipay' ? '支付宝' : '微信'
|
||||
accountType: accountType === 'alipay' ? '支付宝' : '微信',
|
||||
status: 'pending'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ Page({
|
||||
|
||||
// === 收益数据 ===
|
||||
totalCommission: 0, // 累计佣金总额(所有获得的佣金)
|
||||
availableEarnings: 0, // 可提现金额(未申请提现的佣金)
|
||||
availableEarnings: 0, // 可提现金额(未申请提现的佣金)- 字符串格式用于显示
|
||||
availableEarningsNum: 0, // 可提现金额 - 数字格式用于判断
|
||||
pendingWithdrawAmount: 0, // 待审核金额(已申请提现但未审核)
|
||||
withdrawnEarnings: 0, // 已提现金额
|
||||
earnings: 0, // 已结算收益(保留兼容)
|
||||
@@ -151,6 +152,22 @@ Page({
|
||||
return typeof num === 'number' ? num.toFixed(2) : '0.00'
|
||||
}
|
||||
|
||||
// ✅ 修正:可提现金额 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const withdrawnNum = realData?.withdrawnEarnings || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - withdrawnNum - pendingWithdrawNum
|
||||
const minWithdrawAmount = realData?.minWithdrawAmount || 10
|
||||
|
||||
console.log('=== [Referral] 收益计算(完整版)===')
|
||||
console.log('累计佣金 (totalCommission):', totalCommissionNum)
|
||||
console.log('已提现金额 (withdrawnEarnings):', withdrawnNum)
|
||||
console.log('待审核金额 (pendingWithdrawAmount):', pendingWithdrawNum)
|
||||
console.log('可提现金额 = 累计 - 已提现 - 待审核 =', totalCommissionNum, '-', withdrawnNum, '-', pendingWithdrawNum, '=', availableEarningsNum)
|
||||
console.log('最低提现金额 (minWithdrawAmount):', minWithdrawAmount)
|
||||
console.log('按钮判断:', availableEarningsNum, '>=', minWithdrawAmount, '=', availableEarningsNum >= minWithdrawAmount)
|
||||
console.log('✅ 按钮应该:', availableEarningsNum >= minWithdrawAmount ? '🟢 启用(绿色)' : '⚫ 禁用(灰色)')
|
||||
|
||||
this.setData({
|
||||
isLoggedIn: true,
|
||||
userInfo,
|
||||
@@ -163,14 +180,15 @@ Page({
|
||||
expiredCount,
|
||||
|
||||
// 收益数据 - 格式化为两位小数
|
||||
totalCommission: formatMoney(realData?.totalCommission || 0),
|
||||
availableEarnings: formatMoney(realData?.availableEarnings || 0),
|
||||
pendingWithdrawAmount: formatMoney(realData?.pendingWithdrawAmount || 0),
|
||||
totalCommission: formatMoney(totalCommissionNum),
|
||||
availableEarnings: formatMoney(availableEarningsNum), // ✅ 使用计算后的可提现金额
|
||||
availableEarningsNum: availableEarningsNum, // ✅ 数字格式用于按钮判断
|
||||
pendingWithdrawAmount: formatMoney(pendingWithdrawNum),
|
||||
withdrawnEarnings: formatMoney(realData?.withdrawnEarnings || 0),
|
||||
earnings: formatMoney(realData?.earnings || 0),
|
||||
pendingEarnings: formatMoney(realData?.pendingEarnings || 0),
|
||||
shareRate: realData?.shareRate || 90,
|
||||
minWithdrawAmount: realData?.minWithdrawAmount || 10,
|
||||
minWithdrawAmount: minWithdrawAmount,
|
||||
|
||||
// 统计
|
||||
referralCount: realData?.referralCount || realData?.stats?.totalBindings || activeBindings.length + convertedBindings.length,
|
||||
@@ -206,6 +224,15 @@ Page({
|
||||
console.log('[Referral] - 即将过期:', this.data.expiringCount)
|
||||
console.log('[Referral] - 收益:', this.data.earnings)
|
||||
|
||||
console.log('=== [Referral] 按钮状态验证 ===')
|
||||
console.log('累计佣金 (totalCommission):', this.data.totalCommission)
|
||||
console.log('待审核金额 (pendingWithdrawAmount):', this.data.pendingWithdrawAmount)
|
||||
console.log('可提现金额 (availableEarnings 显示):', this.data.availableEarnings)
|
||||
console.log('可提现金额 (availableEarningsNum 判断):', this.data.availableEarningsNum, typeof this.data.availableEarningsNum)
|
||||
console.log('最低提现金额 (minWithdrawAmount):', this.data.minWithdrawAmount, typeof this.data.minWithdrawAmount)
|
||||
console.log('按钮启用条件:', this.data.availableEarningsNum, '>=', this.data.minWithdrawAmount, '=', this.data.availableEarningsNum >= this.data.minWithdrawAmount)
|
||||
console.log('✅ 最终结果: 按钮应该', this.data.availableEarningsNum >= this.data.minWithdrawAmount ? '🟢 启用' : '⚫ 禁用')
|
||||
|
||||
// 隐藏加载提示
|
||||
wx.hideLoading()
|
||||
} else {
|
||||
@@ -583,9 +610,16 @@ Page({
|
||||
|
||||
// 提现 - 直接到微信零钱
|
||||
async handleWithdraw() {
|
||||
const availableEarnings = parseFloat(this.data.availableEarnings) || 0
|
||||
// 使用数字版本直接进行判断,避免重复转换
|
||||
const availableEarnings = this.data.availableEarningsNum || 0
|
||||
const minWithdrawAmount = this.data.minWithdrawAmount || 10
|
||||
|
||||
console.log('[Withdraw] 提现检查:', {
|
||||
availableEarnings,
|
||||
minWithdrawAmount,
|
||||
shouldEnable: availableEarnings >= minWithdrawAmount
|
||||
})
|
||||
|
||||
if (availableEarnings <= 0) {
|
||||
wx.showToast({ title: '暂无可提现收益', icon: 'none' })
|
||||
return
|
||||
@@ -634,13 +668,13 @@ Page({
|
||||
|
||||
if (res.success) {
|
||||
wx.showModal({
|
||||
title: '提现成功 🎉',
|
||||
content: `¥${amount.toFixed(2)} 已到账您的微信零钱`,
|
||||
title: '提现申请已提交 ✅',
|
||||
content: res.message || '正在审核中,通过后会自动到账您的微信零钱',
|
||||
showCancel: false,
|
||||
confirmText: '好的'
|
||||
confirmText: '知道了'
|
||||
})
|
||||
|
||||
// 刷新数据
|
||||
// 刷新数据(此时待审核金额会增加,可提现金额会减少)
|
||||
this.initData()
|
||||
} else {
|
||||
if (res.needBind) {
|
||||
@@ -655,7 +689,7 @@ Page({
|
||||
}
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: res.error || '提现失败', icon: 'none' })
|
||||
wx.showToast({ title: res.message || res.error || '提现失败', icon: 'none', duration: 3000 })
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -728,7 +762,7 @@ Page({
|
||||
})
|
||||
|
||||
// 如果开启,检查当前金额是否达到阈值
|
||||
if (enabled && parseFloat(this.data.availableEarnings) >= threshold) {
|
||||
if (enabled && this.data.availableEarningsNum >= threshold) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: `当前可提现金额¥${this.data.availableEarnings}已达到阈值¥${threshold},是否立即提现?`,
|
||||
|
||||
@@ -40,17 +40,17 @@
|
||||
<image class="icon-wallet" src="/assets/icons/wallet.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="earnings-info">
|
||||
<text class="earnings-label">累计佣金</text>
|
||||
<text class="earnings-label">可提现金额</text>
|
||||
<text class="commission-rate">{{shareRate}}% 返利</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="earnings-right">
|
||||
<text class="earnings-value">¥{{totalCommission}}</text>
|
||||
<text class="pending-text">待审核: ¥{{pendingWithdrawAmount}}</text>
|
||||
<text class="earnings-value">¥{{availableEarnings}}</text>
|
||||
<text class="pending-text">累计: ¥{{totalCommission}} | 待审核: ¥{{pendingWithdrawAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="withdraw-btn {{availableEarnings < minWithdrawAmount ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
|
||||
{{availableEarnings < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : '申请提现 ¥' + availableEarnings}}
|
||||
<view class="withdraw-btn {{availableEarningsNum < minWithdrawAmount ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
|
||||
{{availableEarningsNum < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : '申请提现 ¥' + availableEarnings}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
359
开发文档/8、部署/可提现金额计算修复.md
Normal file
359
开发文档/8、部署/可提现金额计算修复.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# 可提现金额计算修复
|
||||
|
||||
## 问题总结
|
||||
|
||||
### 发现的问题
|
||||
|
||||
**当前逻辑(错误)**:
|
||||
```javascript
|
||||
可提现金额 = 累计佣金 - 待审核金额
|
||||
```
|
||||
|
||||
**问题场景**:
|
||||
1. 用户累计佣金 ¥100,申请提现 ¥50
|
||||
2. 此时:可提现 = 100 - 50 = ¥50 ✅ 正确
|
||||
3. 审核通过后,提现记录状态改为 completed
|
||||
4. 此时:可提现 = 100 - 0 = ¥100 ❌ 错误!
|
||||
|
||||
**根本原因**:审核通过后,`pendingWithdrawAmount` 归零,但没有减去"已提现金额",导致可提现金额"回血"。
|
||||
|
||||
## 正确方案
|
||||
|
||||
### 计算公式
|
||||
|
||||
```javascript
|
||||
可提现金额 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
|
||||
| 字段 | 说明 | 来源 | 变化规律 |
|
||||
|------|------|------|----------|
|
||||
| `totalCommission` | 累计佣金总额 | `SUM(orders.amount) × distributorShare` | 有新订单就增加,只增不减 |
|
||||
| `withdrawnEarnings` | 已提现金额 | `users.withdrawn_earnings` | 审核通过时累加 |
|
||||
| `pendingWithdrawAmount` | 待审核金额 | `SUM(withdrawals.amount WHERE status='pending')` | 申请时增加,审核后归零 |
|
||||
| `availableEarnings` | 可提现金额 | 前端计算 | 动态变化 |
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 前端计算逻辑修正
|
||||
|
||||
**文件**:`miniprogram/pages/referral/referral.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
// ❌ 错误:只减去待审核,没减去已提现
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - pendingWithdrawNum
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
// ✅ 正确:三元素完整计算
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const withdrawnNum = realData?.withdrawnEarnings || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - withdrawnNum - pendingWithdrawNum
|
||||
```
|
||||
|
||||
### 2. 详细日志输出
|
||||
|
||||
```javascript
|
||||
console.log('=== [Referral] 收益计算(完整版)===')
|
||||
console.log('累计佣金 (totalCommission):', totalCommissionNum)
|
||||
console.log('已提现金额 (withdrawnEarnings):', withdrawnNum)
|
||||
console.log('待审核金额 (pendingWithdrawAmount):', pendingWithdrawNum)
|
||||
console.log('可提现金额 = 累计 - 已提现 - 待审核 =', totalCommissionNum, '-', withdrawnNum, '-', pendingWithdrawNum, '=', availableEarningsNum)
|
||||
console.log('最低提现金额 (minWithdrawAmount):', minWithdrawAmount)
|
||||
console.log('按钮判断:', availableEarningsNum, '>=', minWithdrawAmount, '=', availableEarningsNum >= minWithdrawAmount)
|
||||
console.log('✅ 按钮应该:', availableEarningsNum >= minWithdrawAmount ? '🟢 启用(绿色)' : '⚫ 禁用(灰色)')
|
||||
```
|
||||
|
||||
## 验证流程
|
||||
|
||||
### 完整场景测试
|
||||
|
||||
#### 场景1:初始状态
|
||||
```
|
||||
数据:
|
||||
- 累计佣金: ¥100
|
||||
- 已提现: ¥0
|
||||
- 待审核: ¥0
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 0 - 0 = ¥100
|
||||
|
||||
验证:✅ 可提现 ¥100
|
||||
```
|
||||
|
||||
#### 场景2:申请提现
|
||||
```
|
||||
操作:申请提现 ¥50
|
||||
|
||||
数据:
|
||||
- 累计佣金: ¥100 (不变)
|
||||
- 已提现: ¥0 (不变)
|
||||
- 待审核: ¥50 (新增)
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 0 - 50 = ¥50
|
||||
|
||||
验证:✅ 可提现 ¥50
|
||||
```
|
||||
|
||||
#### 场景3:审核通过(关键)
|
||||
```
|
||||
操作:管理员审核通过
|
||||
|
||||
数据:
|
||||
- 累计佣金: ¥100 (不变)
|
||||
- 已提现: ¥50 (users.withdrawn_earnings += 50)
|
||||
- 待审核: ¥0 (状态改为 completed)
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 50 - 0 = ¥50
|
||||
|
||||
验证:✅ 可提现 ¥50(不会回血!)
|
||||
```
|
||||
|
||||
#### 场景4:新订单产生
|
||||
```
|
||||
操作:用户购买新商品,产生佣金 ¥20
|
||||
|
||||
数据:
|
||||
- 累计佣金: ¥120 (100 + 20)
|
||||
- 已提现: ¥50 (不变)
|
||||
- 待审核: ¥0 (不变)
|
||||
|
||||
计算:
|
||||
availableEarnings = 120 - 50 - 0 = ¥70
|
||||
|
||||
验证:✅ 可提现 ¥70
|
||||
```
|
||||
|
||||
#### 场景5:二次提现
|
||||
```
|
||||
操作:申请提现 ¥30
|
||||
|
||||
数据:
|
||||
- 累计佣金: ¥120 (不变)
|
||||
- 已提现: ¥50 (不变)
|
||||
- 待审核: ¥30 (新增)
|
||||
|
||||
计算:
|
||||
availableEarnings = 120 - 50 - 30 = ¥40
|
||||
|
||||
验证:✅ 可提现 ¥40
|
||||
```
|
||||
|
||||
### 数据库验证
|
||||
|
||||
#### 1. 检查 users 表
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
nickname,
|
||||
withdrawn_earnings
|
||||
FROM users
|
||||
WHERE id = 'YOUR_USER_ID';
|
||||
```
|
||||
|
||||
#### 2. 检查 withdrawals 表
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
amount,
|
||||
status,
|
||||
created_at
|
||||
FROM withdrawals
|
||||
WHERE user_id = 'YOUR_USER_ID'
|
||||
ORDER BY created_at DESC;
|
||||
```
|
||||
|
||||
#### 3. 检查 orders 表
|
||||
```sql
|
||||
SELECT
|
||||
SUM(amount) as total_amount
|
||||
FROM orders
|
||||
WHERE referrer_id = 'YOUR_USER_ID'
|
||||
AND status = 'paid';
|
||||
```
|
||||
|
||||
#### 4. 手动验证计算
|
||||
|
||||
```sql
|
||||
-- 1. 累计佣金
|
||||
SET @total_orders = (SELECT SUM(amount) FROM orders WHERE referrer_id = 'YOUR_USER_ID' AND status = 'paid');
|
||||
SET @distributor_share = 0.9;
|
||||
SET @total_commission = @total_orders * @distributor_share;
|
||||
|
||||
-- 2. 已提现
|
||||
SET @withdrawn = (SELECT withdrawn_earnings FROM users WHERE id = 'YOUR_USER_ID');
|
||||
|
||||
-- 3. 待审核
|
||||
SET @pending = (SELECT SUM(amount) FROM withdrawals WHERE user_id = 'YOUR_USER_ID' AND status = 'pending');
|
||||
|
||||
-- 4. 可提现
|
||||
SET @available = @total_commission - @withdrawn - IFNULL(@pending, 0);
|
||||
|
||||
-- 显示结果
|
||||
SELECT
|
||||
@total_commission as '累计佣金',
|
||||
@withdrawn as '已提现',
|
||||
@pending as '待审核',
|
||||
@available as '可提现';
|
||||
```
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 清除缓存重新编译
|
||||
|
||||
微信开发者工具:
|
||||
```
|
||||
工具 → 清除缓存 → 清除全部缓存数据
|
||||
点击 编译 按钮
|
||||
```
|
||||
|
||||
### 2. 查看控制台日志
|
||||
|
||||
进入分销中心页面,查看日志输出:
|
||||
|
||||
```
|
||||
=== [Referral] 收益计算(完整版)===
|
||||
累计佣金 (totalCommission): 100
|
||||
已提现金额 (withdrawnEarnings): 0
|
||||
待审核金额 (pendingWithdrawAmount): 0
|
||||
可提现金额 = 累计 - 已提现 - 待审核 = 100 - 0 - 0 = 100
|
||||
最低提现金额 (minWithdrawAmount): 5
|
||||
按钮判断: 100 >= 5 = true
|
||||
✅ 按钮应该: 🟢 启用(绿色)
|
||||
```
|
||||
|
||||
### 3. 测试提现流程
|
||||
|
||||
1. **申请提现**:点击提现按钮,申请提现
|
||||
2. **查看变化**:
|
||||
- 累计佣金不变
|
||||
- 待审核金额增加
|
||||
- 可提现金额减少
|
||||
3. **审核通过**:在管理后台审核通过
|
||||
4. **再次查看**:
|
||||
- 累计佣金不变
|
||||
- 已提现金额增加
|
||||
- 待审核金额归零
|
||||
- **可提现金额不应该回血**
|
||||
|
||||
### 4. 验证按钮状态
|
||||
|
||||
不同情况下按钮的状态:
|
||||
|
||||
| 可提现金额 | 最低提现 | 按钮状态 | 按钮文本 |
|
||||
|-----------|---------|---------|---------|
|
||||
| ¥10 | ¥5 | 启用 | 申请提现 ¥10 |
|
||||
| ¥3 | ¥5 | 禁用 | 满5元可提现 |
|
||||
| ¥0 | ¥5 | 禁用 | 满5元可提现 |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 为什么要分"累计佣金"和"可提现金额"?
|
||||
|
||||
**A**:
|
||||
- **累计佣金**:用户的成就感,"我总共赚了多少"
|
||||
- **可提现金额**:用户的可操作余额,"我现在能提多少"
|
||||
|
||||
### Q2: 已提现的钱还算在累计佣金里吗?
|
||||
|
||||
**A**: 是的。累计佣金是历史总和,只增不减。已提现只是资金流向,不影响累计数字。
|
||||
|
||||
示例:
|
||||
- 累计赚了 ¥100(成就)
|
||||
- 已提现 ¥50(已到手)
|
||||
- 还能提现 ¥50(余额)
|
||||
|
||||
### Q3: 如果审核拒绝会怎样?
|
||||
|
||||
**A**:
|
||||
1. 待审核金额归零(提现申请被取消)
|
||||
2. 已提现金额不变
|
||||
3. 可提现金额恢复(用户可以重新申请)
|
||||
|
||||
### Q4: users.withdrawn_earnings 会自动更新吗?
|
||||
|
||||
**A**: 是的,在两个地方会更新:
|
||||
- `app/api/withdraw/route.ts` - 自动审核通过时
|
||||
- `app/api/admin/withdrawals/route.ts` - 管理员审核通过时
|
||||
|
||||
### Q5: 为什么不直接从数据库读取可提现金额?
|
||||
|
||||
**A**:
|
||||
- 可提现金额是**动态计算值**,不是固定字段
|
||||
- 实时计算确保数据准确
|
||||
- 便于调试和验证逻辑
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `miniprogram/pages/referral/referral.js` - 前端计算(已修改)✅
|
||||
- `app/api/referral/data/route.ts` - 数据查询(无需修改)
|
||||
- `app/api/withdraw/route.ts` - 提现接口(无需修改)
|
||||
- `app/api/admin/withdrawals/route.ts` - 审核接口(无需修改)
|
||||
|
||||
## 数据流图
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────┐
|
||||
│ 订单产生佣金 │
|
||||
│ ↓ │
|
||||
│ 累计佣金 (totalCommission) │
|
||||
│ 持续累加,只增不减 │
|
||||
│ ↓ │
|
||||
│ ┌─────────────┴─────────────┐ │
|
||||
│ ↓ ↓ │
|
||||
│ 申请提现 继续积累 │
|
||||
│ ↓ │
|
||||
│ 待审核 (pendingWithdrawAmount) │
|
||||
│ 暂时冻结 │
|
||||
│ ↓ │
|
||||
│ 审核通过 │
|
||||
│ ↓ │
|
||||
│ 已提现 (withdrawnEarnings) │
|
||||
│ 累加记录 │
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ │
|
||||
│ │ 可提现金额(实时计算) │ │
|
||||
│ │ = 累计佣金 │ │
|
||||
│ │ - 已提现金额 │ │
|
||||
│ │ - 待审核金额 │ │
|
||||
│ └─────────────────────────┘ │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
### 修改前的问题
|
||||
|
||||
```javascript
|
||||
❌ 可提现 = 累计佣金 - 待审核金额
|
||||
|
||||
问题:审核通过后,待审核归零,可提现"回血"
|
||||
```
|
||||
|
||||
### 修改后的正确逻辑
|
||||
|
||||
```javascript
|
||||
✅ 可提现 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
|
||||
优点:
|
||||
1. 审核通过后,可提现金额不会增加
|
||||
2. 只有新订单产生佣金时,可提现金额才会增加
|
||||
3. 逻辑清晰,符合资金流向
|
||||
```
|
||||
|
||||
### 业务含义
|
||||
|
||||
- **累计佣金**:成就感 - "我赚了多少"
|
||||
- **已提现**:安全感 - "我拿到了多少"
|
||||
- **待审核**:期待感 - "我正在提多少"
|
||||
- **可提现**:行动力 - "我能提多少"
|
||||
|
||||
这样的设计既满足了用户查看历史成就的需求,又确保了资金安全和准确性。
|
||||
400
开发文档/8、部署/可提现金额计算逻辑分析.md
Normal file
400
开发文档/8、部署/可提现金额计算逻辑分析.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# 可提现金额计算逻辑分析
|
||||
|
||||
## 问题描述
|
||||
|
||||
用户提问:提现后,可提现金额应该如何计算?
|
||||
|
||||
## 当前逻辑分析
|
||||
|
||||
### 数据库字段
|
||||
|
||||
**users 表**:
|
||||
- `earnings` - 已结算收益(保留兼容)
|
||||
- `pending_earnings` - 待结算收益(保留兼容)
|
||||
- `withdrawn_earnings` - 已提现金额(累加)
|
||||
|
||||
**withdrawals 表**:
|
||||
- `status = 'pending'` - 待审核
|
||||
- `status = 'completed'` - 已完成
|
||||
- `status = 'failed'` - 已拒绝
|
||||
|
||||
### 当前计算方式
|
||||
|
||||
**后端 API (`/api/referral/data`)**:
|
||||
```typescript
|
||||
// 累计佣金(从 orders 表实时计算)
|
||||
totalCommission = SUM(orders.amount WHERE status = 'paid') × distributorShare
|
||||
|
||||
// 待审核金额(从 withdrawals 表查询)
|
||||
pendingWithdrawAmount = SUM(withdrawals.amount WHERE status = 'pending')
|
||||
|
||||
// 已提现金额(从 users 表读取)
|
||||
withdrawnEarnings = users.withdrawn_earnings
|
||||
```
|
||||
|
||||
**前端计算**:
|
||||
```javascript
|
||||
// 可提现金额 = 累计佣金 - 待审核金额
|
||||
availableEarnings = totalCommission - pendingWithdrawAmount
|
||||
```
|
||||
|
||||
## ⚠️ 发现的问题
|
||||
|
||||
### 场景演示
|
||||
|
||||
**初始状态**:
|
||||
```
|
||||
累计佣金: ¥100
|
||||
已提现: ¥0
|
||||
待审核: ¥0
|
||||
可提现 = 100 - 0 = ¥100 ✅
|
||||
```
|
||||
|
||||
**申请提现 ¥50 后**:
|
||||
```
|
||||
累计佣金: ¥100 (不变)
|
||||
已提现: ¥0 (不变)
|
||||
待审核: ¥50 (新增)
|
||||
可提现 = 100 - 50 = ¥50 ✅ 正确
|
||||
```
|
||||
|
||||
**审核通过后(当前逻辑的问题)**:
|
||||
```
|
||||
累计佣金: ¥100 (不变)
|
||||
已提现: ¥50 (users.withdrawn_earnings += 50)
|
||||
待审核: ¥0 (提现记录 status 变为 completed)
|
||||
可提现 = 100 - 0 = ¥100 ❌ 错误!应该是 ¥50
|
||||
```
|
||||
|
||||
### 问题根源
|
||||
|
||||
**当前公式**:
|
||||
```javascript
|
||||
可提现 = 累计佣金 - 待审核金额
|
||||
```
|
||||
|
||||
**问题**:没有减去"已提现金额",导致审核通过后,可提现金额"回血"了。
|
||||
|
||||
## ✅ 正确的计算方案
|
||||
|
||||
### 推荐方案:三元素计算
|
||||
|
||||
```javascript
|
||||
可提现金额 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
```
|
||||
|
||||
### 完整演示
|
||||
|
||||
**初始状态**:
|
||||
```
|
||||
累计佣金: ¥100
|
||||
已提现: ¥0
|
||||
待审核: ¥0
|
||||
可提现 = 100 - 0 - 0 = ¥100 ✅
|
||||
```
|
||||
|
||||
**申请提现 ¥50 后**:
|
||||
```
|
||||
累计佣金: ¥100 (不变)
|
||||
已提现: ¥0 (不变)
|
||||
待审核: ¥50 (新增提现申请)
|
||||
可提现 = 100 - 0 - 50 = ¥50 ✅ 正确
|
||||
```
|
||||
|
||||
**审核通过后**:
|
||||
```
|
||||
累计佣金: ¥100 (不变)
|
||||
已提现: ¥50 (users.withdrawn_earnings += 50)
|
||||
待审核: ¥0 (状态改为 completed)
|
||||
可提现 = 100 - 50 - 0 = ¥50 ✅ 正确!
|
||||
```
|
||||
|
||||
**有新订单后**:
|
||||
```
|
||||
累计佣金: ¥120 (新订单 ¥20)
|
||||
已提现: ¥50 (不变)
|
||||
待审核: ¥0 (不变)
|
||||
可提现 = 120 - 50 - 0 = ¥70 ✅ 正确!
|
||||
```
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 方案A:后端返回 withdrawnEarnings,前端计算(推荐)✅
|
||||
|
||||
**优点**:
|
||||
- 前端灵活控制
|
||||
- 逻辑清晰透明
|
||||
- 便于调试
|
||||
|
||||
**实现**:
|
||||
|
||||
#### 1. 后端确保返回正确数据
|
||||
|
||||
`app/api/referral/data/route.ts` 已经返回了:
|
||||
```typescript
|
||||
{
|
||||
totalCommission: 计算值, // ✅ 已有
|
||||
pendingWithdrawAmount: 查询值, // ✅ 已有
|
||||
withdrawnEarnings: users字段 // ✅ 已有
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 前端修改计算逻辑
|
||||
|
||||
`miniprogram/pages/referral/referral.js`:
|
||||
|
||||
```javascript
|
||||
// ❌ 当前(错误)
|
||||
const availableEarningsNum = totalCommissionNum - pendingWithdrawNum
|
||||
|
||||
// ✅ 修正(正确)
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const withdrawnNum = realData?.withdrawnEarnings || 0
|
||||
const availableEarningsNum = totalCommissionNum - withdrawnNum - pendingWithdrawNum
|
||||
```
|
||||
|
||||
### 方案B:后端直接计算返回
|
||||
|
||||
**优点**:
|
||||
- 前端无需计算
|
||||
- 后端统一控制逻辑
|
||||
|
||||
**缺点**:
|
||||
- 前端不知道计算细节
|
||||
- 调试困难
|
||||
|
||||
**实现**:
|
||||
|
||||
```typescript
|
||||
// app/api/referral/data/route.ts
|
||||
const totalCommission = paymentStats.totalAmount * distributorShare
|
||||
const withdrawnEarnings = parseFloat(user.withdrawn_earnings) || 0
|
||||
const pendingWithdrawAmount = ...查询值...
|
||||
const availableEarnings = totalCommission - withdrawnEarnings - pendingWithdrawAmount
|
||||
|
||||
return {
|
||||
totalCommission,
|
||||
withdrawnEarnings,
|
||||
pendingWithdrawAmount,
|
||||
availableEarnings // 后端计算好
|
||||
}
|
||||
```
|
||||
|
||||
## 推荐实现(方案A)
|
||||
|
||||
### 修改文件
|
||||
|
||||
#### 1. `miniprogram/pages/referral/referral.js`
|
||||
|
||||
```javascript
|
||||
// 在 initData() 方法中修改
|
||||
|
||||
// ✅ 修正:可提现金额 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const withdrawnNum = realData?.withdrawnEarnings || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - withdrawnNum - pendingWithdrawNum
|
||||
|
||||
console.log('=== [Referral] 收益计算(完整版)===')
|
||||
console.log('累计佣金 (totalCommission):', totalCommissionNum)
|
||||
console.log('已提现金额 (withdrawnEarnings):', withdrawnNum)
|
||||
console.log('待审核金额 (pendingWithdrawAmount):', pendingWithdrawNum)
|
||||
console.log('可提现金额 = 累计 - 已提现 - 待审核 =', totalCommissionNum, '-', withdrawnNum, '-', pendingWithdrawNum, '=', availableEarningsNum)
|
||||
console.log('✅ 按钮应该:', availableEarningsNum >= minWithdrawAmount ? '🟢 启用' : '⚫ 禁用')
|
||||
```
|
||||
|
||||
#### 2. `miniprogram/pages/referral/referral.wxml`
|
||||
|
||||
显示部分需要更新(如果要显示已提现):
|
||||
|
||||
```xml
|
||||
<view class="earnings-right">
|
||||
<text class="earnings-value">¥{{availableEarnings}}</text>
|
||||
<text class="pending-text">
|
||||
累计: ¥{{totalCommission}} | 已提现: ¥{{withdrawnEarnings}} | 待审核: ¥{{pendingWithdrawAmount}}
|
||||
</text>
|
||||
</view>
|
||||
```
|
||||
|
||||
或者简化显示:
|
||||
```xml
|
||||
<view class="earnings-right">
|
||||
<text class="earnings-value">¥{{availableEarnings}}</text>
|
||||
<text class="pending-text">
|
||||
累计: ¥{{totalCommission}} | 待审核: ¥{{pendingWithdrawAmount}}
|
||||
</text>
|
||||
</view>
|
||||
```
|
||||
|
||||
## 数据流向图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 订单产生佣金 │
|
||||
│ ↓ │
|
||||
│ 累计佣金: ¥100 (持续累加) │
|
||||
│ ↓ │
|
||||
│ ┌────────────┴────────────┐ │
|
||||
│ ↓ ↓ │
|
||||
│ 申请提现 ¥50 继续累积佣金 │
|
||||
│ ↓ ↓ │
|
||||
│ 待审核: ¥50 累计: ¥120 │
|
||||
│ 可提现: ¥50 │
|
||||
│ ↓ │
|
||||
│ 审核通过 │
|
||||
│ ↓ │
|
||||
│ 已提现: ¥50 │
|
||||
│ 待审核: ¥0 │
|
||||
│ 可提现: ¥70 (120-50-0) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 测试用例
|
||||
|
||||
### 用例1:无提现记录
|
||||
|
||||
```
|
||||
输入:
|
||||
- totalCommission = 100
|
||||
- withdrawnEarnings = 0
|
||||
- pendingWithdrawAmount = 0
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 0 - 0 = 100
|
||||
|
||||
预期:✅ 可提现 ¥100
|
||||
```
|
||||
|
||||
### 用例2:有待审核提现
|
||||
|
||||
```
|
||||
输入:
|
||||
- totalCommission = 100
|
||||
- withdrawnEarnings = 0
|
||||
- pendingWithdrawAmount = 30
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 0 - 30 = 70
|
||||
|
||||
预期:✅ 可提现 ¥70
|
||||
```
|
||||
|
||||
### 用例3:有已提现记录
|
||||
|
||||
```
|
||||
输入:
|
||||
- totalCommission = 100
|
||||
- withdrawnEarnings = 50
|
||||
- pendingWithdrawAmount = 0
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 50 - 0 = 50
|
||||
|
||||
预期:✅ 可提现 ¥50
|
||||
```
|
||||
|
||||
### 用例4:复合情况
|
||||
|
||||
```
|
||||
输入:
|
||||
- totalCommission = 200
|
||||
- withdrawnEarnings = 80
|
||||
- pendingWithdrawAmount = 40
|
||||
|
||||
计算:
|
||||
availableEarnings = 200 - 80 - 40 = 80
|
||||
|
||||
预期:✅ 可提现 ¥80
|
||||
```
|
||||
|
||||
### 用例5:边界情况(全部提完)
|
||||
|
||||
```
|
||||
输入:
|
||||
- totalCommission = 100
|
||||
- withdrawnEarnings = 70
|
||||
- pendingWithdrawAmount = 30
|
||||
|
||||
计算:
|
||||
availableEarnings = 100 - 70 - 30 = 0
|
||||
|
||||
预期:✅ 可提现 ¥0,按钮禁用
|
||||
```
|
||||
|
||||
## 验证 users.withdrawn_earnings 是否正确维护
|
||||
|
||||
### 提现接口确认
|
||||
|
||||
查看 `app/api/withdraw/route.ts`:
|
||||
```typescript
|
||||
// 第165行(自动审核情况下)
|
||||
UPDATE users
|
||||
SET withdrawn_earnings = withdrawn_earnings + ?,
|
||||
pending_earnings = GREATEST(0, pending_earnings - ?)
|
||||
WHERE id = ?
|
||||
```
|
||||
|
||||
查看 `app/api/admin/withdrawals/route.ts`:
|
||||
```typescript
|
||||
// 第156行(管理员审核通过)
|
||||
UPDATE users
|
||||
SET withdrawn_earnings = withdrawn_earnings + ?,
|
||||
pending_earnings = pending_earnings - ?
|
||||
WHERE id = ?
|
||||
```
|
||||
|
||||
✅ **确认**:`withdrawn_earnings` 字段在提现审核通过后会正确累加。
|
||||
|
||||
## 总结
|
||||
|
||||
### 当前问题
|
||||
|
||||
```javascript
|
||||
❌ 错误:可提现 = 累计佣金 - 待审核金额
|
||||
```
|
||||
|
||||
- 审核通过后,待审核归零,可提现"回血"
|
||||
- 导致用户可以重复提现同一笔钱
|
||||
|
||||
### 正确方案
|
||||
|
||||
```javascript
|
||||
✅ 正确:可提现 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
```
|
||||
|
||||
- 累计佣金:历史总和,只增不减
|
||||
- 已提现金额:累加的已到账金额
|
||||
- 待审核金额:申请中但未到账的金额
|
||||
- 可提现金额:真正可以申请提现的余额
|
||||
|
||||
### 字段含义
|
||||
|
||||
| 字段 | 含义 | 变化规律 |
|
||||
|------|------|----------|
|
||||
| `totalCommission` | 累计佣金总额 | 有新订单就增加 |
|
||||
| `withdrawnEarnings` | 已提现金额 | 审核通过时增加 |
|
||||
| `pendingWithdrawAmount` | 待审核金额 | 申请提现时增加,审核后清零 |
|
||||
| `availableEarnings` | 可提现金额 | 动态计算,三者综合结果 |
|
||||
|
||||
### 业务含义
|
||||
|
||||
- **累计佣金**:成就感,"我总共赚了多少"
|
||||
- **已提现**:安全感,"我已经拿到手多少"
|
||||
- **待审核**:期待感,"我正在提现多少"
|
||||
- **可提现**:行动力,"我现在能提多少"
|
||||
|
||||
## 下一步行动
|
||||
|
||||
1. 修改前端计算逻辑(添加 `withdrawnEarnings`)
|
||||
2. 添加详细调试日志
|
||||
3. 测试各种场景
|
||||
4. 验证数据库 `withdrawn_earnings` 是否正确
|
||||
5. 文档记录新逻辑
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `miniprogram/pages/referral/referral.js` - 前端计算(需修改)
|
||||
- `app/api/referral/data/route.ts` - 数据查询(已正确)
|
||||
- `app/api/withdraw/route.ts` - 提现接口(已正确)
|
||||
- `app/api/admin/withdrawals/route.ts` - 审核接口(已正确)
|
||||
279
开发文档/8、部署/提现卡片显示优化.md
Normal file
279
开发文档/8、部署/提现卡片显示优化.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# 提现卡片显示优化
|
||||
|
||||
## 需求
|
||||
|
||||
用户希望提现卡片更直观地显示**可提现金额**,而不是累计佣金。
|
||||
|
||||
## 修改前后对比
|
||||
|
||||
### 修改前
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 💰 累计佣金 ¥8.10 │
|
||||
│ 90% 返利 待审核: ¥0.00 │
|
||||
├─────────────────────────────────┤
|
||||
│ 申请提现 ¥8.10 │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 用户看到"累计佣金 ¥8.10",但不清楚实际能提多少
|
||||
- 需要心算:8.10 - 0.00 = 8.10(可提现)
|
||||
|
||||
### 修改后
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 💰 可提现金额 ¥8.10 │
|
||||
│ 90% 返利 累计: ¥8.10 | 待审核: ¥0.00 │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 申请提现 ¥8.10 │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 直接显示可提现金额 ¥8.10(最关心的数据)
|
||||
- ✅ 底部小字显示明细:累计佣金 + 待审核金额
|
||||
- ✅ 一目了然,无需计算
|
||||
|
||||
## 具体修改
|
||||
|
||||
### 文件:`miniprogram/pages/referral/referral.wxml`
|
||||
|
||||
```xml
|
||||
<!-- 修改前 -->
|
||||
<view class="earnings-left">
|
||||
<view class="wallet-icon">
|
||||
<image class="icon-wallet" src="/assets/icons/wallet.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="earnings-info">
|
||||
<text class="earnings-label">累计佣金</text>
|
||||
<text class="commission-rate">{{shareRate}}% 返利</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="earnings-right">
|
||||
<text class="earnings-value">¥{{totalCommission}}</text>
|
||||
<text class="pending-text">待审核: ¥{{pendingWithdrawAmount}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 修改后 -->
|
||||
<view class="earnings-left">
|
||||
<view class="wallet-icon">
|
||||
<image class="icon-wallet" src="/assets/icons/wallet.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="earnings-info">
|
||||
<text class="earnings-label">可提现金额</text> <!-- ✅ 改为"可提现金额" -->
|
||||
<text class="commission-rate">{{shareRate}}% 返利</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="earnings-right">
|
||||
<text class="earnings-value">¥{{availableEarnings}}</text> <!-- ✅ 显示可提现金额 -->
|
||||
<text class="pending-text">累计: ¥{{totalCommission}} | 待审核: ¥{{pendingWithdrawAmount}}</text> <!-- ✅ 显示明细 -->
|
||||
</view>
|
||||
```
|
||||
|
||||
## 数据说明
|
||||
|
||||
### 字段含义
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `totalCommission` | 累计佣金总额(所有订单佣金之和) | ¥8.10 |
|
||||
| `pendingWithdrawAmount` | 待审核的提现金额(已申请但未到账) | ¥0.00 |
|
||||
| `availableEarnings` | **可提现金额** = 累计佣金 - 待审核金额 | ¥8.10 |
|
||||
|
||||
### 计算公式
|
||||
|
||||
```javascript
|
||||
availableEarnings = totalCommission - pendingWithdrawAmount
|
||||
= 8.10 - 0.00
|
||||
= 8.10
|
||||
```
|
||||
|
||||
## 场景示例
|
||||
|
||||
### 场景1:无待审核提现
|
||||
|
||||
```
|
||||
累计佣金: ¥100.00
|
||||
待审核: ¥0.00
|
||||
可提现: ¥100.00
|
||||
```
|
||||
|
||||
显示:
|
||||
```
|
||||
可提现金额 ¥100.00
|
||||
累计: ¥100.00 | 待审核: ¥0.00
|
||||
```
|
||||
|
||||
### 场景2:有待审核提现
|
||||
|
||||
```
|
||||
累计佣金: ¥100.00
|
||||
待审核: ¥30.00
|
||||
可提现: ¥70.00
|
||||
```
|
||||
|
||||
显示:
|
||||
```
|
||||
可提现金额 ¥70.00
|
||||
累计: ¥100.00 | 待审核: ¥30.00
|
||||
```
|
||||
|
||||
用户一眼就能看到:
|
||||
- 可以提现 70 元
|
||||
- 有 30 元在审核中
|
||||
- 总共赚了 100 元
|
||||
|
||||
### 场景3:全部待审核
|
||||
|
||||
```
|
||||
累计佣金: ¥100.00
|
||||
待审核: ¥100.00
|
||||
可提现: ¥0.00
|
||||
```
|
||||
|
||||
显示:
|
||||
```
|
||||
可提现金额 ¥0.00
|
||||
累计: ¥100.00 | 待审核: ¥100.00
|
||||
```
|
||||
|
||||
按钮变灰:`满5元可提现`
|
||||
|
||||
## 用户体验优化
|
||||
|
||||
### 信息层级
|
||||
|
||||
**主信息(大号字体)**:
|
||||
- 可提现金额 ¥8.10
|
||||
- 用户最关心的数据,放在最显眼位置
|
||||
|
||||
**次要信息(小号字体)**:
|
||||
- 累计: ¥8.10 | 待审核: ¥0.00
|
||||
- 提供明细,让用户了解全貌
|
||||
|
||||
### 视觉引导
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 💰 可提现金额 ← 标签 ← 主信息
|
||||
│ ¥8.10 ← 大号
|
||||
│ 90% 返利 ← 提示 │
|
||||
│ 累计: ¥8.10 | 待审核: ¥0.00 ← 明细(小号)
|
||||
├─────────────────────────────────────────┤
|
||||
│ 申请提现 ¥8.10 ← 行动按钮 │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 对比其他方案
|
||||
|
||||
### 方案A:显示累计佣金(原方案)
|
||||
|
||||
```
|
||||
累计佣金: ¥8.10
|
||||
待审核: ¥0.00
|
||||
```
|
||||
|
||||
- ❌ 需要用户自己计算可提现金额
|
||||
- ❌ 不够直观
|
||||
|
||||
### 方案B:只显示可提现金额
|
||||
|
||||
```
|
||||
可提现金额: ¥8.10
|
||||
```
|
||||
|
||||
- ✅ 直观
|
||||
- ❌ 缺少明细,用户不知道累计赚了多少
|
||||
|
||||
### 方案C:显示可提现 + 明细(当前方案)✅
|
||||
|
||||
```
|
||||
可提现金额: ¥8.10
|
||||
累计: ¥8.10 | 待审核: ¥0.00
|
||||
```
|
||||
|
||||
- ✅ 直观:一眼看到可提现金额
|
||||
- ✅ 完整:提供累计和待审核的明细
|
||||
- ✅ 平衡:主次分明
|
||||
|
||||
## 样式建议(可选)
|
||||
|
||||
如果需要进一步优化样式,可以考虑:
|
||||
|
||||
### 1. 调整明细字号
|
||||
|
||||
```css
|
||||
.pending-text {
|
||||
font-size: 22rpx; /* 从 24rpx 减小到 22rpx */
|
||||
color: rgba(255,255,255,0.5);
|
||||
margin-top: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 使用图标分隔
|
||||
|
||||
```xml
|
||||
<text class="pending-text">
|
||||
累计: ¥{{totalCommission}} · 待审核: ¥{{pendingWithdrawAmount}}
|
||||
</text>
|
||||
```
|
||||
|
||||
用 `·` (中点) 代替 `|` (竖线)
|
||||
|
||||
### 3. 增加图标说明
|
||||
|
||||
```xml
|
||||
<text class="pending-text">
|
||||
💰 累计: ¥{{totalCommission}} ⏳ 待审核: ¥{{pendingWithdrawAmount}}
|
||||
</text>
|
||||
```
|
||||
|
||||
## 测试要点
|
||||
|
||||
### 1. 显示验证
|
||||
|
||||
- [ ] 标签显示为"可提现金额"
|
||||
- [ ] 大号数字显示正确的可提现金额
|
||||
- [ ] 小号文字显示累计和待审核金额
|
||||
- [ ] 格式正确:`累计: ¥X.XX | 待审核: ¥Y.YY`
|
||||
|
||||
### 2. 计算验证
|
||||
|
||||
```
|
||||
已知:
|
||||
- 累计佣金: ¥8.10
|
||||
- 待审核: ¥0.00
|
||||
|
||||
验证:
|
||||
- 可提现 = 8.10 - 0.00 = ¥8.10 ✅
|
||||
```
|
||||
|
||||
### 3. 边界情况
|
||||
|
||||
- [ ] 可提现为 0 时的显示
|
||||
- [ ] 待审核大于累计时的显示(理论上不会出现)
|
||||
- [ ] 多笔待审核的累加
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `miniprogram/pages/referral/referral.wxml` - 界面结构(本次修改)
|
||||
- `miniprogram/pages/referral/referral.wxss` - 样式
|
||||
- `miniprogram/pages/referral/referral.js` - 逻辑(计算 availableEarnings)
|
||||
|
||||
## 总结
|
||||
|
||||
这次优化让提现卡片更加**用户友好**:
|
||||
|
||||
**修改前**:
|
||||
- 显示"累计佣金",用户需要自己计算
|
||||
|
||||
**修改后**:
|
||||
- 直接显示"可提现金额",一目了然
|
||||
- 保留明细信息(累计、待审核),信息完整
|
||||
- 主次分明,视觉层级清晰
|
||||
|
||||
核心理念:**把用户最关心的数据,放在最显眼的位置**。
|
||||
423
开发文档/8、部署/提现双向校验实现.md
Normal file
423
开发文档/8、部署/提现双向校验实现.md
Normal file
@@ -0,0 +1,423 @@
|
||||
# 提现双向校验实现
|
||||
|
||||
## 需求
|
||||
|
||||
前端和后端都必须使用**相同的逻辑**校验可提现金额,确保安全性和一致性。
|
||||
|
||||
## 校验逻辑(统一)
|
||||
|
||||
```javascript
|
||||
可提现金额 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
```
|
||||
|
||||
## 前后端对比
|
||||
|
||||
### 前端校验(miniprogram)
|
||||
|
||||
**文件**:`miniprogram/pages/referral/referral.js`
|
||||
|
||||
**作用**:按钮启用/禁用判断
|
||||
|
||||
```javascript
|
||||
// 计算可提现金额
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const withdrawnNum = realData?.withdrawnEarnings || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - withdrawnNum - pendingWithdrawNum
|
||||
|
||||
// 判断按钮状态
|
||||
if (availableEarningsNum >= minWithdrawAmount) {
|
||||
// 启用按钮
|
||||
} else {
|
||||
// 禁用按钮
|
||||
}
|
||||
```
|
||||
|
||||
### 后端校验(API)
|
||||
|
||||
**文件**:`app/api/withdraw/route.ts`
|
||||
|
||||
**作用**:提现申请最终验证
|
||||
|
||||
```typescript
|
||||
// 1. 查询累计佣金
|
||||
const ordersResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as total_amount
|
||||
FROM orders
|
||||
WHERE referrer_id = ? AND status = 'paid'
|
||||
`, [userId])
|
||||
const totalAmount = parseFloat(ordersResult[0]?.total_amount || 0)
|
||||
const totalCommission = totalAmount * distributorShare
|
||||
|
||||
// 2. 读取已提现金额
|
||||
const withdrawnEarnings = parseFloat(user.withdrawn_earnings) || 0
|
||||
|
||||
// 3. 查询待审核金额
|
||||
const pendingResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as pending_amount
|
||||
FROM withdrawals
|
||||
WHERE user_id = ? AND status = 'pending'
|
||||
`, [userId])
|
||||
const pendingWithdrawAmount = parseFloat(pendingResult[0]?.pending_amount || 0)
|
||||
|
||||
// 4. 计算可提现金额
|
||||
const availableAmount = totalCommission - withdrawnEarnings - pendingWithdrawAmount
|
||||
|
||||
// 5. 验证
|
||||
if (amount > availableAmount) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: `可提现金额不足。当前可提现 ¥${availableAmount.toFixed(2)}(累计 ¥${totalCommission.toFixed(2)} - 已提现 ¥${withdrawnEarnings.toFixed(2)} - 待审核 ¥${pendingWithdrawAmount.toFixed(2)})`
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. 后端添加已提现金额
|
||||
|
||||
**修改前(错误)**:
|
||||
```typescript
|
||||
// ❌ 只减去待审核,没减去已提现
|
||||
const availableAmount = totalCommission - pendingWithdrawAmount
|
||||
```
|
||||
|
||||
**修改后(正确)**:
|
||||
```typescript
|
||||
// ✅ 三元素完整校验
|
||||
const withdrawnEarnings = parseFloat(user.withdrawn_earnings) || 0
|
||||
const availableAmount = totalCommission - withdrawnEarnings - pendingWithdrawAmount
|
||||
```
|
||||
|
||||
### 2. 详细日志输出
|
||||
|
||||
```typescript
|
||||
console.log('[Withdraw] 提现验证(完整版):')
|
||||
console.log('- 累计佣金 (totalCommission):', totalCommission)
|
||||
console.log('- 已提现金额 (withdrawnEarnings):', withdrawnEarnings)
|
||||
console.log('- 待审核金额 (pendingWithdrawAmount):', pendingWithdrawAmount)
|
||||
console.log('- 可提现金额 = 累计 - 已提现 - 待审核 =', totalCommission, '-', withdrawnEarnings, '-', pendingWithdrawAmount, '=', availableAmount)
|
||||
console.log('- 申请提现金额 (amount):', amount)
|
||||
console.log('- 判断:', amount, '>', availableAmount, '=', amount > availableAmount)
|
||||
```
|
||||
|
||||
### 3. 优化错误提示
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)},待审核 ¥${pendingWithdrawAmount.toFixed(2)}`
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
message: `可提现金额不足。当前可提现 ¥${availableAmount.toFixed(2)}(累计 ¥${totalCommission.toFixed(2)} - 已提现 ¥${withdrawnEarnings.toFixed(2)} - 待审核 ¥${pendingWithdrawAmount.toFixed(2)})`
|
||||
```
|
||||
|
||||
## 双向校验流程
|
||||
|
||||
```
|
||||
用户点击提现按钮
|
||||
↓
|
||||
┌──────────────────────────┐
|
||||
│ 前端校验(第一层) │
|
||||
│ 按钮启用/禁用判断 │
|
||||
├──────────────────────────┤
|
||||
│ 可提现 >= 最低金额? │
|
||||
│ YES → 允许点击 │
|
||||
│ NO → 按钮禁用 │
|
||||
└──────────────────────────┘
|
||||
↓
|
||||
用户确认提现
|
||||
↓
|
||||
┌──────────────────────────┐
|
||||
│ 后端校验(第二层) │
|
||||
│ API 最终验证 │
|
||||
├──────────────────────────┤
|
||||
│ 1. 查询累计佣金 │
|
||||
│ 2. 读取已提现金额 │
|
||||
│ 3. 查询待审核金额 │
|
||||
│ 4. 计算可提现金额 │
|
||||
│ 5. amount > available? │
|
||||
│ YES → 拒绝提现 │
|
||||
│ NO → 允许提现 │
|
||||
└──────────────────────────┘
|
||||
↓
|
||||
创建提现记录
|
||||
```
|
||||
|
||||
## 为什么需要双向校验?
|
||||
|
||||
### 前端校验的作用
|
||||
|
||||
**优点**:
|
||||
- ✅ 快速响应,提升用户体验
|
||||
- ✅ 减少无效请求,降低服务器压力
|
||||
- ✅ 按钮禁用,防止误操作
|
||||
|
||||
**局限**:
|
||||
- ❌ 数据可能过期(API 数据缓存)
|
||||
- ❌ 可被绕过(客户端不可信)
|
||||
- ❌ 无法阻止恶意请求
|
||||
|
||||
### 后端校验的作用
|
||||
|
||||
**优点**:
|
||||
- ✅ 最终防线,确保资金安全
|
||||
- ✅ 实时数据,准确无误
|
||||
- ✅ 不可绕过,强制验证
|
||||
|
||||
**必要性**:
|
||||
- ✅ 防止前端被篡改
|
||||
- ✅ 防止并发提现
|
||||
- ✅ 防止逻辑漏洞
|
||||
|
||||
## 攻击场景防御
|
||||
|
||||
### 场景1:前端被篡改
|
||||
|
||||
**攻击**:
|
||||
- 黑客修改前端代码,移除按钮禁用逻辑
|
||||
- 强制发送提现请求
|
||||
|
||||
**防御**:
|
||||
- ✅ 后端独立校验,拒绝超额提现
|
||||
|
||||
### 场景2:并发提现
|
||||
|
||||
**攻击**:
|
||||
- 用户快速点击两次提现按钮
|
||||
- 或同时在多个设备上提现
|
||||
|
||||
**防御**:
|
||||
- ✅ 后端查询最新的 `pendingWithdrawAmount`
|
||||
- ✅ 数据库事务保证原子性
|
||||
|
||||
### 场景3:API 重放攻击
|
||||
|
||||
**攻击**:
|
||||
- 捕获提现请求,重复发送
|
||||
|
||||
**防御**:
|
||||
- ✅ 后端实时校验可提现金额
|
||||
- ✅ 创建提现记录后,`pendingWithdrawAmount` 增加
|
||||
- ✅ 第二次请求时,可提现金额不足,拒绝
|
||||
|
||||
## 测试用例
|
||||
|
||||
### 测试1:正常提现
|
||||
|
||||
**数据**:
|
||||
- 累计佣金: ¥100
|
||||
- 已提现: ¥0
|
||||
- 待审核: ¥0
|
||||
- 申请提现: ¥50
|
||||
|
||||
**前端**:
|
||||
```
|
||||
可提现 = 100 - 0 - 0 = 100
|
||||
50 < 100 → 按钮启用 ✅
|
||||
```
|
||||
|
||||
**后端**:
|
||||
```
|
||||
可提现 = 100 - 0 - 0 = 100
|
||||
50 ≤ 100 → 允许提现 ✅
|
||||
```
|
||||
|
||||
**结果**:✅ 提现成功
|
||||
|
||||
### 测试2:超额提现
|
||||
|
||||
**数据**:
|
||||
- 累计佣金: ¥100
|
||||
- 已提现: ¥0
|
||||
- 待审核: ¥0
|
||||
- 申请提现: ¥150
|
||||
|
||||
**前端**:
|
||||
```
|
||||
可提现 = 100 - 0 - 0 = 100
|
||||
150 > 100 → 按钮禁用 ✅
|
||||
(正常情况下用户无法点击)
|
||||
```
|
||||
|
||||
**后端**(假设黑客绕过前端):
|
||||
```
|
||||
可提现 = 100 - 0 - 0 = 100
|
||||
150 > 100 → 拒绝提现 ✅
|
||||
返回错误:可提现金额不足
|
||||
```
|
||||
|
||||
**结果**:✅ 后端拦截成功
|
||||
|
||||
### 测试3:重复提现
|
||||
|
||||
**数据**:
|
||||
- 累计佣金: ¥100
|
||||
- 已提现: ¥0
|
||||
- 待审核: ¥0
|
||||
|
||||
**第一次提现 ¥50**:
|
||||
```
|
||||
前端:100 - 0 - 0 = 100,允许 ✅
|
||||
后端:100 - 0 - 0 = 100,允许 ✅
|
||||
创建提现记录,pending += 50
|
||||
```
|
||||
|
||||
**第二次提现 ¥60**(并发或快速点击):
|
||||
```
|
||||
前端:可能还是显示 100(数据未刷新)
|
||||
后端:100 - 0 - 50 = 50
|
||||
60 > 50 → 拒绝提现 ✅
|
||||
```
|
||||
|
||||
**结果**:✅ 后端防御成功
|
||||
|
||||
### 测试4:审核通过后再次提现
|
||||
|
||||
**初始**:
|
||||
- 累计佣金: ¥100
|
||||
- 已提现: ¥50(之前提现已到账)
|
||||
- 待审核: ¥0
|
||||
|
||||
**申请提现 ¥60**:
|
||||
```
|
||||
前端:100 - 50 - 0 = 50
|
||||
60 > 50 → 按钮禁用 ✅
|
||||
```
|
||||
|
||||
**后端**(假设绕过前端):
|
||||
```
|
||||
100 - 50 - 0 = 50
|
||||
60 > 50 → 拒绝提现 ✅
|
||||
```
|
||||
|
||||
**结果**:✅ 双重防护
|
||||
|
||||
## 日志示例
|
||||
|
||||
### 前端日志
|
||||
|
||||
```
|
||||
=== [Referral] 收益计算(完整版)===
|
||||
累计佣金 (totalCommission): 100
|
||||
已提现金额 (withdrawnEarnings): 50
|
||||
待审核金额 (pendingWithdrawAmount): 0
|
||||
可提现金额 = 累计 - 已提现 - 待审核 = 100 - 50 - 0 = 50
|
||||
最低提现金额 (minWithdrawAmount): 5
|
||||
按钮判断: 50 >= 5 = true
|
||||
✅ 按钮应该: 🟢 启用(绿色)
|
||||
```
|
||||
|
||||
### 后端日志
|
||||
|
||||
```
|
||||
[Withdraw] 佣金计算:
|
||||
- 订单总金额: 111.11
|
||||
- 分成比例: 90%
|
||||
- 累计佣金: 100
|
||||
|
||||
[Withdraw] 提现验证(完整版):
|
||||
- 累计佣金 (totalCommission): 100
|
||||
- 已提现金额 (withdrawnEarnings): 50
|
||||
- 待审核金额 (pendingWithdrawAmount): 0
|
||||
- 可提现金额 = 累计 - 已提现 - 待审核 = 100 - 50 - 0 = 50
|
||||
- 申请提现金额 (amount): 50
|
||||
- 判断: 50 > 50 = false
|
||||
✅ 提现申请通过
|
||||
```
|
||||
|
||||
## 数据一致性保证
|
||||
|
||||
### 数据来源
|
||||
|
||||
| 字段 | 前端数据来源 | 后端数据来源 | 一致性 |
|
||||
|------|-------------|-------------|--------|
|
||||
| 累计佣金 | `/api/referral/data` | 实时查询 `orders` | ✅ 相同算法 |
|
||||
| 已提现 | `/api/referral/data` | 读取 `users.withdrawn_earnings` | ✅ 相同字段 |
|
||||
| 待审核 | `/api/referral/data` | 实时查询 `withdrawals` | ✅ 相同查询 |
|
||||
|
||||
### 同步机制
|
||||
|
||||
1. **前端数据**:来自 `/api/referral/data`
|
||||
2. **后端校验**:独立查询,实时数据
|
||||
3. **一致性保证**:
|
||||
- 相同的数据库表
|
||||
- 相同的计算公式
|
||||
- 后端数据更准确(实时查询)
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `miniprogram/pages/referral/referral.js` - 前端校验 ✅
|
||||
- `app/api/withdraw/route.ts` - 后端校验 ✅
|
||||
- `app/api/referral/data/route.ts` - 数据查询
|
||||
|
||||
## 部署注意事项
|
||||
|
||||
### 1. 重启后端服务
|
||||
|
||||
```bash
|
||||
python devlop.py restart mycontent
|
||||
```
|
||||
|
||||
### 2. 清除前端缓存
|
||||
|
||||
```
|
||||
微信开发者工具:工具 → 清除缓存 → 清除全部缓存数据
|
||||
```
|
||||
|
||||
### 3. 监控日志
|
||||
|
||||
```bash
|
||||
pm2 logs mycontent --lines 100
|
||||
```
|
||||
|
||||
关注:
|
||||
- `[Withdraw] 提现验证(完整版):` - 后端校验日志
|
||||
- `[Referral] 收益计算(完整版)` - 前端计算日志
|
||||
|
||||
### 4. 测试验证
|
||||
|
||||
- [ ] 正常提现(可提现 > 最低金额)
|
||||
- [ ] 超额提现(申请金额 > 可提现)
|
||||
- [ ] 并发提现(快速点击两次)
|
||||
- [ ] 审核后再提现
|
||||
|
||||
## 安全检查清单
|
||||
|
||||
- [x] 前端使用三元素计算
|
||||
- [x] 后端使用三元素校验
|
||||
- [x] 前后端公式完全一致
|
||||
- [x] 后端独立查询数据(不信任前端)
|
||||
- [x] 详细日志记录
|
||||
- [x] 清晰的错误提示
|
||||
|
||||
## 总结
|
||||
|
||||
### 双向校验的意义
|
||||
|
||||
**前端**:
|
||||
- 用户体验优化
|
||||
- 快速反馈
|
||||
- 减少无效请求
|
||||
|
||||
**后端**:
|
||||
- 安全防线
|
||||
- 资金保护
|
||||
- 最终决策
|
||||
|
||||
### 公式统一
|
||||
|
||||
```javascript
|
||||
// 前端和后端完全一致
|
||||
可提现金额 = 累计佣金 - 已提现金额 - 待审核金额
|
||||
```
|
||||
|
||||
### 防御能力
|
||||
|
||||
- ✅ 防篡改
|
||||
- ✅ 防并发
|
||||
- ✅ 防重放
|
||||
- ✅ 防超额
|
||||
|
||||
**核心原则**:前端便利,后端安全,双重保障。
|
||||
312
开发文档/8、部署/提现审核流程优化.md
Normal file
312
开发文档/8、部署/提现审核流程优化.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# 提现审核流程优化
|
||||
|
||||
## 需求
|
||||
|
||||
提现申请提交后,应该明确告知用户:
|
||||
- ✅ 提现申请已提交
|
||||
- ⏳ 正在审核中
|
||||
- 💰 审核通过后会自动到账微信零钱
|
||||
|
||||
而不是误导用户以为已经立即到账。
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. 后端提示优化
|
||||
|
||||
**文件**:`app/api/withdraw/route.ts`
|
||||
|
||||
```typescript
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '提现申请已提交,正在审核中,通过后会自动到账您的微信零钱',
|
||||
data: {
|
||||
withdrawId,
|
||||
amount,
|
||||
account,
|
||||
accountType: accountType === 'alipay' ? '支付宝' : '微信',
|
||||
status: 'pending' // ✅ 新增:返回状态
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**变更**:
|
||||
- ❌ 旧:`message: '提现成功'` (误导性)
|
||||
- ✅ 新:`message: '提现申请已提交,正在审核中,通过后会自动到账您的微信零钱'` (准确)
|
||||
- ✅ 新增 `status: 'pending'` 字段
|
||||
|
||||
### 2. 前端提示优化
|
||||
|
||||
**文件**:`miniprogram/pages/referral/referral.js`
|
||||
|
||||
```javascript
|
||||
if (res.success) {
|
||||
wx.showModal({
|
||||
title: '提现申请已提交 ✅',
|
||||
content: res.message || '正在审核中,通过后会自动到账您的微信零钱',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
})
|
||||
|
||||
// 刷新数据(此时待审核金额会增加,可提现金额会减少)
|
||||
this.initData()
|
||||
}
|
||||
```
|
||||
|
||||
**变更**:
|
||||
- ❌ 旧标题:`'提现成功 🎉'` (误导性)
|
||||
- ✅ 新标题:`'提现申请已提交 ✅'` (准确)
|
||||
- ❌ 旧内容:`'¥X.XX 已到账您的微信零钱'` (虚假)
|
||||
- ✅ 新内容:`'正在审核中,通过后会自动到账您的微信零钱'` (真实)
|
||||
- ❌ 旧按钮:`'好的'`
|
||||
- ✅ 新按钮:`'知道了'`
|
||||
|
||||
### 3. 错误提示优化
|
||||
|
||||
```javascript
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.message || res.error || '提现失败',
|
||||
icon: 'none',
|
||||
duration: 3000 // ✅ 增加显示时间到3秒
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 提现流程说明
|
||||
|
||||
### 完整流程
|
||||
|
||||
```
|
||||
1. 用户点击"申请提现"
|
||||
↓
|
||||
2. 前端验证:可提现金额 >= 最低提现金额
|
||||
↓
|
||||
3. 后端创建提现记录:status = 'pending'
|
||||
↓
|
||||
4. 提示用户:"提现申请已提交,正在审核中"
|
||||
↓
|
||||
5. 刷新分销中心数据:
|
||||
- 待审核金额 += 提现金额
|
||||
- 可提现金额 -= 提现金额
|
||||
↓
|
||||
6. 管理员在后台审核
|
||||
↓
|
||||
7. 审核通过:
|
||||
- 更新 withdrawals.status = 'completed'
|
||||
- 自动转账到微信零钱
|
||||
- 发送微信通知给用户
|
||||
```
|
||||
|
||||
### 状态说明
|
||||
|
||||
| 状态 | 英文 | 说明 | 用户提示 |
|
||||
|------|------|------|----------|
|
||||
| 待审核 | `pending` | 提现申请已提交,等待管理员审核 | 正在审核中 |
|
||||
| 已完成 | `completed` | 审核通过,已转账到微信零钱 | 已到账 |
|
||||
| 已拒绝 | `failed` | 审核未通过 | 提现失败 |
|
||||
|
||||
### 数据变化示例
|
||||
|
||||
**提现前**:
|
||||
```
|
||||
累计佣金: 100元
|
||||
待审核金额: 0元
|
||||
可提现金额: 100 - 0 = 100元
|
||||
```
|
||||
|
||||
**申请提现50元后**:
|
||||
```
|
||||
累计佣金: 100元
|
||||
待审核金额: 0 + 50 = 50元
|
||||
可提现金额: 100 - 50 = 50元
|
||||
```
|
||||
|
||||
**审核通过后**:
|
||||
```
|
||||
累计佣金: 100元
|
||||
待审核金额: 50 - 50 = 0元
|
||||
已提现金额: 0 + 50 = 50元
|
||||
可提现金额: 100 - 0 = 100元 (如果有新订单)
|
||||
```
|
||||
|
||||
## 用户体验对比
|
||||
|
||||
### 修改前(误导性)
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ 提现成功 🎉 │
|
||||
├─────────────────────┤
|
||||
│ ¥50.00 已到账您的 │
|
||||
│ 微信零钱 │
|
||||
├─────────────────────┤
|
||||
│ [好的] │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- ❌ 用户以为钱已经到账
|
||||
- ❌ 用户去微信查看,发现没钱
|
||||
- ❌ 产生疑惑和不满
|
||||
|
||||
### 修改后(准确清晰)
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ 提现申请已提交 ✅ │
|
||||
├─────────────────────┤
|
||||
│ 正在审核中,通过后 │
|
||||
│ 会自动到账您的微信 │
|
||||
│ 零钱 │
|
||||
├─────────────────────┤
|
||||
│ [知道了] │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 用户知道需要等待审核
|
||||
- ✅ 用户知道审核通过后会自动到账
|
||||
- ✅ 符合实际情况
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
### 1. 审核通知
|
||||
|
||||
管理员审核通过后,发送微信模板消息通知用户:
|
||||
|
||||
```javascript
|
||||
// 伪代码
|
||||
wx.sendTemplateMessage({
|
||||
touser: userOpenId,
|
||||
template_id: 'OPENTM415934726',
|
||||
data: {
|
||||
thing1: { value: '提现申请' },
|
||||
amount2: { value: '50.00元' },
|
||||
phrase3: { value: '审核通过' },
|
||||
time4: { value: '2026-02-04 15:30' }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 提现记录页面
|
||||
|
||||
在"我的"页面增加"提现记录"入口,让用户查看:
|
||||
- 待审核的提现申请
|
||||
- 已完成的提现记录
|
||||
- 失败的提现记录及原因
|
||||
|
||||
### 3. 审核预计时间
|
||||
|
||||
在提示中增加审核时长预期:
|
||||
|
||||
```
|
||||
正在审核中,预计1-3个工作日内完成审核,
|
||||
通过后会自动到账您的微信零钱
|
||||
```
|
||||
|
||||
### 4. 自动审核
|
||||
|
||||
对于小额提现(如50元以下),可以实现自动审核:
|
||||
|
||||
```javascript
|
||||
if (amount <= 50) {
|
||||
// 自动审核通过
|
||||
await query(`UPDATE withdrawals SET status = 'completed' WHERE id = ?`, [withdrawId])
|
||||
// 立即转账
|
||||
await wechatPay.transferToWallet(...)
|
||||
return { message: '提现成功,已到账您的微信零钱' }
|
||||
} else {
|
||||
// 需要人工审核
|
||||
return { message: '提现申请已提交,正在审核中' }
|
||||
}
|
||||
```
|
||||
|
||||
## 管理后台提现审核功能
|
||||
|
||||
### 审核页面功能
|
||||
|
||||
1. **提现列表**:
|
||||
- 显示所有待审核的提现申请
|
||||
- 显示用户信息、提现金额、申请时间
|
||||
- 显示用户的累计佣金、历史提现次数
|
||||
|
||||
2. **审核操作**:
|
||||
- 通过:调用微信商家转账接口
|
||||
- 拒绝:填写拒绝原因
|
||||
|
||||
3. **记录查询**:
|
||||
- 已完成的提现记录
|
||||
- 失败的提现记录
|
||||
|
||||
### 审核接口(待实现)
|
||||
|
||||
```typescript
|
||||
// POST /api/admin/withdraw/approve
|
||||
{
|
||||
"withdrawId": "W1738694028123",
|
||||
"action": "approve" | "reject",
|
||||
"reason": "拒绝原因(可选)"
|
||||
}
|
||||
```
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 测试提现申请
|
||||
|
||||
1. 在小程序中进入分销中心
|
||||
2. 确保可提现金额 >= 5元
|
||||
3. 点击"申请提现"按钮
|
||||
4. 确认提现金额
|
||||
5. 查看提示是否为"提现申请已提交,正在审核中"
|
||||
|
||||
### 2. 验证数据变化
|
||||
|
||||
提现后立即刷新页面,检查:
|
||||
- ✅ 累计佣金不变
|
||||
- ✅ 待审核金额增加
|
||||
- ✅ 可提现金额减少
|
||||
- ✅ 提现按钮重新判断是否可用
|
||||
|
||||
### 3. 查看数据库
|
||||
|
||||
```sql
|
||||
-- 查看提现记录
|
||||
SELECT * FROM withdrawals
|
||||
WHERE user_id = 'ogpTW5fmXRGNpoUbXB3UEqnVe5Tg'
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 5;
|
||||
|
||||
-- 应该看到最新的记录,status = 'pending'
|
||||
```
|
||||
|
||||
### 4. 模拟审核通过
|
||||
|
||||
```sql
|
||||
-- 手动更新状态为已完成
|
||||
UPDATE withdrawals
|
||||
SET status = 'completed',
|
||||
completed_at = NOW()
|
||||
WHERE id = 'W1738694028123';
|
||||
```
|
||||
|
||||
再次刷新小程序,检查:
|
||||
- ✅ 待审核金额减少
|
||||
- ✅ 可提现金额恢复
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `app/api/withdraw/route.ts` - 提现接口(修改返回消息)
|
||||
- `miniprogram/pages/referral/referral.js` - 前端提现逻辑(修改提示)
|
||||
|
||||
## 总结
|
||||
|
||||
这次优化的核心是**准确传达信息**:
|
||||
|
||||
- ❌ 不要给用户虚假期望("已到账"实际未到账)
|
||||
- ✅ 明确告知用户当前状态("正在审核")
|
||||
- ✅ 告知用户后续流程("通过后会自动到账")
|
||||
|
||||
这样可以:
|
||||
1. 提升用户体验(不会产生困惑)
|
||||
2. 减少客服咨询(用户知道要等待)
|
||||
3. 建立信任(说到做到)
|
||||
223
开发文档/8、部署/提现按钮状态修复说明.md
Normal file
223
开发文档/8、部署/提现按钮状态修复说明.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# 提现按钮状态修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
**现象**:
|
||||
- 累计佣金:¥8.10
|
||||
- 待审核:¥0.00
|
||||
- 可提现金额:8.10元
|
||||
- 最低提现金额:5元
|
||||
- **预期**:按钮应显示"申请提现 ¥8.10"(可用状态)
|
||||
- **实际**:按钮显示"满5元可提现"(灰色禁用状态)
|
||||
|
||||
## 问题原因
|
||||
|
||||
`availableEarnings` 被 `formatMoney()` 格式化为字符串 `"8.10"`,在 WXML 模板中进行数值比较时可能出现类型转换问题。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修改1:增加数字类型字段
|
||||
|
||||
在 `referral.js` 的 `data` 中增加 `availableEarningsNum` 字段:
|
||||
|
||||
```javascript
|
||||
data: {
|
||||
availableEarnings: 0, // 字符串格式用于显示
|
||||
availableEarningsNum: 0, // 数字格式用于判断
|
||||
minWithdrawAmount: 10,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 修改2:初始化时同时设置两个字段
|
||||
|
||||
在 `initData()` 方法中:
|
||||
|
||||
```javascript
|
||||
// 获取原始数字值(用于判断)
|
||||
const availableEarningsNum = realData?.availableEarnings || 0
|
||||
|
||||
console.log('[Referral] 收益数据:')
|
||||
console.log('[Referral] - totalCommission:', realData?.totalCommission)
|
||||
console.log('[Referral] - availableEarnings:', availableEarningsNum)
|
||||
console.log('[Referral] - minWithdrawAmount:', realData?.minWithdrawAmount)
|
||||
console.log('[Referral] - 按钮应该', availableEarningsNum >= (realData?.minWithdrawAmount || 10) ? '可用' : '禁用')
|
||||
|
||||
this.setData({
|
||||
// ...
|
||||
availableEarnings: formatMoney(availableEarningsNum), // 字符串,用于显示
|
||||
availableEarningsNum: availableEarningsNum, // 数字,用于判断
|
||||
minWithdrawAmount: realData?.minWithdrawAmount || 10,
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
### 修改3:WXML 使用数字字段判断
|
||||
|
||||
```xml
|
||||
<view class="withdraw-btn {{availableEarningsNum < minWithdrawAmount ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
|
||||
{{availableEarningsNum < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : '申请提现 ¥' + availableEarnings}}
|
||||
</view>
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 类名判断使用 `availableEarningsNum`(数字)
|
||||
- 文本显示使用 `availableEarnings`(字符串,格式化后的)
|
||||
|
||||
### 修改4:提现逻辑也使用数字字段
|
||||
|
||||
```javascript
|
||||
async handleWithdraw() {
|
||||
// 使用数字版本直接进行判断,避免重复转换
|
||||
const availableEarnings = this.data.availableEarningsNum || 0
|
||||
const minWithdrawAmount = this.data.minWithdrawAmount || 10
|
||||
|
||||
console.log('[Withdraw] 提现检查:', {
|
||||
availableEarnings,
|
||||
minWithdrawAmount,
|
||||
shouldEnable: availableEarnings >= minWithdrawAmount
|
||||
})
|
||||
|
||||
if (availableEarnings <= 0) {
|
||||
wx.showToast({ title: '暂无可提现收益', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (availableEarnings < minWithdrawAmount) {
|
||||
wx.showToast({
|
||||
title: `满${minWithdrawAmount}元可提现`,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 清理小程序缓存(重要)
|
||||
|
||||
在微信开发者工具中:
|
||||
|
||||
1. **清除缓存数据**:
|
||||
- 点击顶部菜单 `工具` → `清除缓存` → `清除全部缓存数据`
|
||||
|
||||
2. **重新编译**:
|
||||
- 点击 `编译` 按钮
|
||||
- 或使用快捷键 `Ctrl + B` (Windows) / `Cmd + B` (Mac)
|
||||
|
||||
3. **如果还不行,尝试完全重启**:
|
||||
- 关闭微信开发者工具
|
||||
- 重新打开项目
|
||||
- 再次编译
|
||||
|
||||
### 2. 查看调试日志
|
||||
|
||||
打开控制台(Console),查找以下日志:
|
||||
|
||||
```
|
||||
[Referral] 收益数据:
|
||||
[Referral] - totalCommission: 8.1
|
||||
[Referral] - availableEarnings: 8.1
|
||||
[Referral] - minWithdrawAmount: 5
|
||||
[Referral] - 按钮应该 可用
|
||||
```
|
||||
|
||||
如果看到"按钮应该 可用",说明逻辑判断正确。
|
||||
|
||||
### 3. 点击提现按钮
|
||||
|
||||
如果按钮仍然是灰色,点击它,查看是否有日志输出:
|
||||
|
||||
```
|
||||
[Withdraw] 提现检查: {
|
||||
availableEarnings: 8.1,
|
||||
minWithdrawAmount: 5,
|
||||
shouldEnable: true
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 检查数据值
|
||||
|
||||
在小程序调试器的 `AppData` 标签页中查找 `referral` 页面的数据:
|
||||
|
||||
```json
|
||||
{
|
||||
"availableEarnings": "8.10", // 字符串
|
||||
"availableEarningsNum": 8.1, // 数字
|
||||
"minWithdrawAmount": 5
|
||||
}
|
||||
```
|
||||
|
||||
确认 `availableEarningsNum` 是数字类型,不是字符串。
|
||||
|
||||
## 预期结果
|
||||
|
||||
修复后,当 `availableEarningsNum` (8.1) >= `minWithdrawAmount` (5) 时:
|
||||
|
||||
- ✅ 按钮显示:`申请提现 ¥8.10`
|
||||
- ✅ 按钮样式:渐变青色背景(可点击)
|
||||
- ✅ 点击后弹出提现确认对话框
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 修改后按钮还是灰色?
|
||||
**A**: 清除小程序缓存并重新编译:
|
||||
```
|
||||
工具 → 清除缓存 → 清除全部缓存数据
|
||||
然后点击 编译 按钮
|
||||
```
|
||||
|
||||
### Q2: 控制台没有看到调试日志?
|
||||
**A**:
|
||||
1. 确保控制台的日志级别包含 `log`
|
||||
2. 检查是否过滤了某些日志
|
||||
3. 尝试刷新页面(下拉刷新或重新进入)
|
||||
|
||||
### Q3: `availableEarningsNum` 是 undefined?
|
||||
**A**: 检查后端 API 返回的数据格式,确保 `realData.availableEarnings` 有值:
|
||||
```javascript
|
||||
console.log('API返回:', realData)
|
||||
```
|
||||
|
||||
### Q4: 数据正确但按钮还是不可点击?
|
||||
**A**: 检查 WXSS 中是否有其他样式覆盖:
|
||||
```css
|
||||
.withdraw-btn.btn-disabled {
|
||||
pointer-events: none; /* 可能导致无法点击 */
|
||||
}
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `miniprogram/pages/referral/referral.js` - 主要逻辑
|
||||
- `miniprogram/pages/referral/referral.wxml` - 模板
|
||||
- `miniprogram/pages/referral/referral.wxss` - 样式
|
||||
- `app/api/referral/data/route.ts` - 后端API
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] 后端API返回正确的 `availableEarnings` 数值
|
||||
- [ ] `initData()` 中正确设置 `availableEarningsNum`
|
||||
- [ ] WXML 使用 `availableEarningsNum` 进行条件判断
|
||||
- [ ] 清除小程序缓存并重新编译
|
||||
- [ ] 控制台显示正确的调试日志
|
||||
- [ ] 按钮显示正确文本和样式
|
||||
- [ ] 点击按钮可以正常提现
|
||||
|
||||
## 技术总结
|
||||
|
||||
**核心问题**:字符串类型的数字在某些场景下的比较可能不符合预期。
|
||||
|
||||
**解决思路**:
|
||||
1. 保存两份数据:字符串用于显示,数字用于判断
|
||||
2. 在数据初始化时就区分好类型
|
||||
3. 在需要比较的地方使用数字类型
|
||||
4. 添加详细的调试日志便于排查问题
|
||||
|
||||
**最佳实践**:
|
||||
- ✅ 数值计算和比较始终使用 Number 类型
|
||||
- ✅ 格式化显示使用 String 类型
|
||||
- ✅ 在 setData 时明确类型转换
|
||||
- ✅ 避免在模板中进行复杂的类型转换
|
||||
263
开发文档/8、部署/提现按钮逻辑修正.md
Normal file
263
开发文档/8、部署/提现按钮逻辑修正.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# 提现按钮逻辑修正
|
||||
|
||||
## 问题描述
|
||||
|
||||
**错误理解**:
|
||||
- 错误地直接使用后端返回的 `availableEarnings` 进行判断
|
||||
|
||||
**正确逻辑**:
|
||||
- ✅ **可提现金额 = 累计佣金 - 待审核金额**
|
||||
- ✅ **按钮启用条件:可提现金额 >= 最低提现金额**
|
||||
|
||||
## 具体案例
|
||||
|
||||
当前数据:
|
||||
- 累计佣金:¥8.10
|
||||
- 待审核金额:¥0.00
|
||||
- **计算:可提现金额 = 8.10 - 0 = 8.10元**
|
||||
- 最低提现金额:¥5.00
|
||||
- **判断:8.10 >= 5.00 = true** → 按钮应该启用(绿色)
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 1. 前端计算可提现金额
|
||||
|
||||
在 `miniprogram/pages/referral/referral.js` 的 `initData()` 方法中:
|
||||
|
||||
```javascript
|
||||
// ✅ 修正:可提现金额 = 累计佣金 - 待审核金额
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - pendingWithdrawNum
|
||||
const minWithdrawAmount = realData?.minWithdrawAmount || 10
|
||||
|
||||
console.log('=== [Referral] 收益计算(修正后)===')
|
||||
console.log('累计佣金:', totalCommissionNum)
|
||||
console.log('待审核金额:', pendingWithdrawNum)
|
||||
console.log('可提现金额 = 累计佣金 - 待审核金额 =', totalCommissionNum, '-', pendingWithdrawNum, '=', availableEarningsNum)
|
||||
console.log('最低提现金额:', minWithdrawAmount)
|
||||
console.log('按钮判断:', availableEarningsNum, '>=', minWithdrawAmount, '=', availableEarningsNum >= minWithdrawAmount)
|
||||
console.log('✅ 按钮应该:', availableEarningsNum >= minWithdrawAmount ? '🟢 启用(绿色)' : '⚫ 禁用(灰色)')
|
||||
```
|
||||
|
||||
### 2. 设置数据
|
||||
|
||||
```javascript
|
||||
this.setData({
|
||||
// 收益数据 - 格式化为两位小数
|
||||
totalCommission: formatMoney(totalCommissionNum),
|
||||
availableEarnings: formatMoney(availableEarningsNum), // ✅ 使用计算后的可提现金额
|
||||
availableEarningsNum: availableEarningsNum, // ✅ 数字格式用于按钮判断
|
||||
pendingWithdrawAmount: formatMoney(pendingWithdrawNum),
|
||||
minWithdrawAmount: minWithdrawAmount,
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 按钮判断逻辑(WXML)
|
||||
|
||||
```xml
|
||||
<view class="withdraw-btn {{availableEarningsNum < minWithdrawAmount ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
|
||||
{{availableEarningsNum < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : '申请提现 ¥' + availableEarnings}}
|
||||
</view>
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 类名判断:`availableEarningsNum < minWithdrawAmount`(数字比较)
|
||||
- 文本显示:使用格式化后的 `availableEarnings` 字符串
|
||||
|
||||
### 4. 提现函数中的验证
|
||||
|
||||
```javascript
|
||||
async handleWithdraw() {
|
||||
// 使用数字版本直接进行判断
|
||||
const availableEarnings = this.data.availableEarningsNum || 0
|
||||
const minWithdrawAmount = this.data.minWithdrawAmount || 10
|
||||
|
||||
console.log('[Withdraw] 提现检查:', {
|
||||
availableEarnings,
|
||||
minWithdrawAmount,
|
||||
shouldEnable: availableEarnings >= minWithdrawAmount
|
||||
})
|
||||
|
||||
if (availableEarnings <= 0) {
|
||||
wx.showToast({ title: '暂无可提现收益', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (availableEarnings < minWithdrawAmount) {
|
||||
wx.showToast({
|
||||
title: `满${minWithdrawAmount}元可提现`,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// ... 继续提现逻辑
|
||||
}
|
||||
```
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 完全清除缓存
|
||||
|
||||
在微信开发者工具中:
|
||||
```
|
||||
1. 关闭微信开发者工具
|
||||
2. 重新打开项目
|
||||
3. 工具 → 清除缓存 → 清除全部缓存数据
|
||||
4. 点击"编译"按钮
|
||||
```
|
||||
|
||||
### 2. 查看控制台日志
|
||||
|
||||
应该看到类似这样的输出:
|
||||
|
||||
```
|
||||
=== [Referral] 收益计算(修正后)===
|
||||
累计佣金: 8.1
|
||||
待审核金额: 0
|
||||
可提现金额 = 累计佣金 - 待审核金额 = 8.1 - 0 = 8.1
|
||||
最低提现金额: 5
|
||||
按钮判断: 8.1 >= 5 = true
|
||||
✅ 按钮应该: 🟢 启用(绿色)
|
||||
|
||||
=== [Referral] 按钮状态验证 ===
|
||||
累计佣金 (totalCommission): 8.10
|
||||
待审核金额 (pendingWithdrawAmount): 0.00
|
||||
可提现金额 (availableEarnings 显示): 8.10
|
||||
可提现金额 (availableEarningsNum 判断): 8.1 number
|
||||
最低提现金额 (minWithdrawAmount): 5 number
|
||||
按钮启用条件: 8.1 >= 5 = true
|
||||
✅ 最终结果: 按钮应该 🟢 启用
|
||||
```
|
||||
|
||||
### 3. 验证界面
|
||||
|
||||
- ✅ 按钮文本:`申请提现 ¥8.10`
|
||||
- ✅ 按钮样式:渐变青色背景(不是灰色)
|
||||
- ✅ 可以点击
|
||||
|
||||
## 逻辑公式总结
|
||||
|
||||
### 核心计算
|
||||
|
||||
```
|
||||
可提现金额 = 累计佣金 - 待审核金额
|
||||
```
|
||||
|
||||
### 按钮状态
|
||||
|
||||
```
|
||||
if (可提现金额 >= 最低提现金额) {
|
||||
// ✅ 启用按钮(绿色)
|
||||
显示文本: "申请提现 ¥{可提现金额}"
|
||||
} else {
|
||||
// ❌ 禁用按钮(灰色)
|
||||
显示文本: "满{最低提现金额}元可提现"
|
||||
}
|
||||
```
|
||||
|
||||
### 数据关系
|
||||
|
||||
```
|
||||
totalCommission (累计佣金)
|
||||
├─ 所有已完成订单的佣金总和
|
||||
└─ 显示在顶部"累计佣金"位置
|
||||
|
||||
pendingWithdrawAmount (待审核金额)
|
||||
├─ 已申请提现但未审核通过的金额总和
|
||||
└─ 显示在"待审核"位置
|
||||
|
||||
availableEarnings (可提现金额)
|
||||
├─ = totalCommission - pendingWithdrawAmount
|
||||
├─ 用户实际可以申请提现的金额
|
||||
└─ 显示在提现按钮上
|
||||
|
||||
minWithdrawAmount (最低提现金额)
|
||||
├─ 从管理后台配置获取
|
||||
├─ 默认值:10元
|
||||
└─ 用于判断是否允许提现
|
||||
```
|
||||
|
||||
## 为什么要在前端计算?
|
||||
|
||||
### 优势
|
||||
|
||||
1. **实时准确**:
|
||||
- 每次进入页面都基于最新的累计佣金和待审核金额计算
|
||||
- 避免后端缓存导致的数据延迟
|
||||
|
||||
2. **逻辑清晰**:
|
||||
- 公式简单明了:`累计佣金 - 待审核金额`
|
||||
- 便于调试和验证
|
||||
|
||||
3. **减轻后端负担**:
|
||||
- 简单的减法运算在前端完成
|
||||
- 后端只需返回原始数据
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
后端API返回:
|
||||
{
|
||||
totalCommission: 8.1, // 累计佣金
|
||||
pendingWithdrawAmount: 0, // 待审核金额
|
||||
minWithdrawAmount: 5 // 最低提现金额
|
||||
}
|
||||
|
||||
前端计算:
|
||||
availableEarningsNum = 8.1 - 0 = 8.1 // 可提现金额
|
||||
|
||||
前端判断:
|
||||
8.1 >= 5 ? 启用按钮 : 禁用按钮
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `miniprogram/pages/referral/referral.js` - 计算逻辑
|
||||
- `miniprogram/pages/referral/referral.wxml` - 按钮显示
|
||||
- `miniprogram/pages/referral/referral.wxss` - 按钮样式
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 为什么要保存两个字段?
|
||||
|
||||
**A**:
|
||||
- `availableEarnings` (字符串):用于界面显示,格式化为 "8.10"
|
||||
- `availableEarningsNum` (数字):用于条件判断,精确比较 `8.1 >= 5`
|
||||
|
||||
### Q2: 后端的 availableEarnings 还有用吗?
|
||||
|
||||
**A**: 如果后端返回了 `availableEarnings`,现在会被前端计算的值覆盖。建议:
|
||||
- 方案1:后端不再返回 `availableEarnings`,只返回 `totalCommission` 和 `pendingWithdrawAmount`
|
||||
- 方案2:保留后端计算,但前端不使用(当前方案)
|
||||
|
||||
### Q3: 如果待审核金额大于累计佣金怎么办?
|
||||
|
||||
**A**: 理论上不应该出现这种情况,但可以添加保护:
|
||||
```javascript
|
||||
const availableEarningsNum = Math.max(0, totalCommissionNum - pendingWithdrawNum)
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [x] 修改前端计算逻辑:`可提现金额 = 累计佣金 - 待审核金额`
|
||||
- [x] 添加详细调试日志
|
||||
- [x] 确保使用数字类型进行比较
|
||||
- [x] 清除小程序缓存
|
||||
- [x] 重新编译
|
||||
- [ ] 查看控制台日志验证计算
|
||||
- [ ] 确认按钮显示正确文本和样式
|
||||
- [ ] 测试点击提现功能
|
||||
|
||||
## 总结
|
||||
|
||||
这次修正的核心是**理解业务逻辑**:
|
||||
|
||||
1. **累计佣金** = 所有获得的佣金(历史总和)
|
||||
2. **待审核金额** = 已申请但未到账的金额
|
||||
3. **可提现金额** = 累计佣金 - 待审核金额 = 当前可以申请提现的金额
|
||||
4. **按钮启用** = 可提现金额 >= 最低提现金额
|
||||
|
||||
之前的错误是直接使用后端返回的值,没有理解这个减法关系。现在在前端明确计算,确保逻辑正确。
|
||||
348
开发文档/8、部署/提现接口逻辑修正.md
Normal file
348
开发文档/8、部署/提现接口逻辑修正.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# 提现接口逻辑修正
|
||||
|
||||
## 问题描述
|
||||
|
||||
**错误提示**:
|
||||
```
|
||||
POST /api/withdraw
|
||||
Response: {
|
||||
success: false,
|
||||
message: "可提现金额不足,当前可提现 ¥0.00"
|
||||
}
|
||||
```
|
||||
|
||||
**问题原因**:
|
||||
后端提现接口的佣金计算逻辑与前端不一致,导致计算出的可提现金额为0。
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 旧逻辑(错误)
|
||||
|
||||
```typescript
|
||||
// ❌ 从 referral_bindings 表查询
|
||||
const earningsResult = await query(`
|
||||
SELECT COALESCE(SUM(commission), 0) as total_commission
|
||||
FROM referral_bindings
|
||||
WHERE referrer_id = ? AND status = 'converted'
|
||||
`, [userId])
|
||||
|
||||
totalEarnings = parseFloat(earningsResult[0]?.total_commission || 0)
|
||||
|
||||
// 计算可提现金额
|
||||
const availableAmount = totalEarnings - withdrawnAmount
|
||||
```
|
||||
|
||||
**问题**:
|
||||
1. 从 `referral_bindings.commission` 字段查询,但该字段可能未维护或不准确
|
||||
2. 与前端/分销中心API的计算逻辑不一致
|
||||
3. 导致后端计算的可提现金额为0
|
||||
|
||||
### 正确逻辑
|
||||
|
||||
应该与前端和 `/api/referral/data` 保持一致:
|
||||
|
||||
```
|
||||
累计佣金 = SUM(orders.amount WHERE referrer_id = userId AND status = 'paid') × distributorShare
|
||||
可提现金额 = 累计佣金 - 待审核提现金额
|
||||
```
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 1. 修改累计佣金计算
|
||||
|
||||
**文件**:`app/api/withdraw/route.ts`
|
||||
|
||||
```typescript
|
||||
// ✅ 修正:从 orders 表查询累计佣金(与前端逻辑一致)
|
||||
let totalCommission = 0
|
||||
try {
|
||||
// 读取分成比例
|
||||
let distributorShare = 0.9 // 默认90%
|
||||
try {
|
||||
const config = await getConfig('referral_config')
|
||||
if (config?.distributorShare) {
|
||||
distributorShare = Number(config.distributorShare)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Withdraw] 读取分成比例失败,使用默认值 90%')
|
||||
}
|
||||
|
||||
// 查询订单总金额
|
||||
const ordersResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as total_amount
|
||||
FROM orders
|
||||
WHERE referrer_id = ? AND status = 'paid'
|
||||
`, [userId]) as any[]
|
||||
|
||||
const totalAmount = parseFloat(ordersResult[0]?.total_amount || 0)
|
||||
totalCommission = totalAmount * distributorShare
|
||||
|
||||
console.log('[Withdraw] 佣金计算:')
|
||||
console.log('- 订单总金额:', totalAmount)
|
||||
console.log('- 分成比例:', distributorShare * 100 + '%')
|
||||
console.log('- 累计佣金:', totalCommission)
|
||||
} catch (e) {
|
||||
console.log('[Withdraw] 查询收益失败:', e)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修改可提现金额计算
|
||||
|
||||
```typescript
|
||||
// 查询待审核提现金额
|
||||
let pendingWithdrawAmount = 0
|
||||
try {
|
||||
const pendingResult = await query(`
|
||||
SELECT COALESCE(SUM(amount), 0) as pending_amount
|
||||
FROM withdrawals
|
||||
WHERE user_id = ? AND status = 'pending'
|
||||
`, [userId]) as any[]
|
||||
pendingWithdrawAmount = parseFloat(pendingResult[0]?.pending_amount || 0)
|
||||
} catch (e) {
|
||||
console.log('[Withdraw] 查询待审核金额失败:', e)
|
||||
}
|
||||
|
||||
// ✅ 修正:可提现金额 = 累计佣金 - 待审核金额(与前端逻辑一致)
|
||||
const availableAmount = totalCommission - pendingWithdrawAmount
|
||||
|
||||
console.log('[Withdraw] 提现验证:')
|
||||
console.log('- 累计佣金 (totalCommission):', totalCommission)
|
||||
console.log('- 待审核金额 (pendingWithdrawAmount):', pendingWithdrawAmount)
|
||||
console.log('- 可提现金额 (availableAmount):', availableAmount)
|
||||
console.log('- 申请提现金额 (amount):', amount)
|
||||
console.log('- 判断:', amount, '>', availableAmount, '=', amount > availableAmount)
|
||||
|
||||
if (amount > availableAmount) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)},待审核 ¥${pendingWithdrawAmount.toFixed(2)}`
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 修改对比
|
||||
|
||||
### 数据来源
|
||||
|
||||
| 项目 | 旧逻辑 | 新逻辑 |
|
||||
|------|--------|--------|
|
||||
| 累计佣金 | `referral_bindings.commission` | `orders.amount × distributorShare` |
|
||||
| 已提现 | `withdrawals.status = 'completed'` | **改为待审核** |
|
||||
| 待审核 | ❌ 未查询 | `withdrawals.status = 'pending'` |
|
||||
| 可提现 | `累计佣金 - 已提现` | `累计佣金 - 待审核` |
|
||||
|
||||
### 计算公式
|
||||
|
||||
**旧逻辑**:
|
||||
```
|
||||
累计佣金 = SUM(referral_bindings.commission WHERE status = 'converted')
|
||||
已提现金额 = SUM(withdrawals.amount WHERE status = 'completed')
|
||||
可提现金额 = 累计佣金 - 已提现金额 ❌ 错误
|
||||
```
|
||||
|
||||
**新逻辑**:
|
||||
```
|
||||
订单总金额 = SUM(orders.amount WHERE referrer_id = userId AND status = 'paid')
|
||||
累计佣金 = 订单总金额 × distributorShare (90%)
|
||||
待审核金额 = SUM(withdrawals.amount WHERE status = 'pending')
|
||||
可提现金额 = 累计佣金 - 待审核金额 ✅ 正确
|
||||
```
|
||||
|
||||
## 一致性验证
|
||||
|
||||
现在三个地方的逻辑完全一致:
|
||||
|
||||
### 1. 前端小程序 (`referral.js`)
|
||||
|
||||
```javascript
|
||||
const totalCommissionNum = realData?.totalCommission || 0
|
||||
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
|
||||
const availableEarningsNum = totalCommissionNum - pendingWithdrawNum
|
||||
```
|
||||
|
||||
### 2. 分销数据API (`/api/referral/data`)
|
||||
|
||||
```typescript
|
||||
// 计算累计佣金
|
||||
const totalAmount = SUM(orders.amount WHERE referrer_id = userId AND status = 'paid')
|
||||
const totalCommission = totalAmount * distributorShare
|
||||
|
||||
// 查询待审核金额
|
||||
const pendingWithdrawAmount = SUM(withdrawals.amount WHERE user_id = userId AND status = 'pending')
|
||||
|
||||
// 返回给前端
|
||||
return {
|
||||
totalCommission,
|
||||
pendingWithdrawAmount,
|
||||
availableEarnings: totalCommission - pendingWithdrawAmount
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 提现API (`/api/withdraw`)
|
||||
|
||||
```typescript
|
||||
// 计算累计佣金
|
||||
const totalAmount = SUM(orders.amount WHERE referrer_id = userId AND status = 'paid')
|
||||
const totalCommission = totalAmount * distributorShare
|
||||
|
||||
// 查询待审核金额
|
||||
const pendingWithdrawAmount = SUM(withdrawals.amount WHERE user_id = userId AND status = 'pending')
|
||||
|
||||
// 验证可提现金额
|
||||
const availableAmount = totalCommission - pendingWithdrawAmount
|
||||
if (amount > availableAmount) {
|
||||
return error("可提现金额不足")
|
||||
}
|
||||
```
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 查看后端日志
|
||||
|
||||
提现时应该看到详细日志:
|
||||
|
||||
```
|
||||
[Withdraw] 佣金计算:
|
||||
- 订单总金额: 9
|
||||
- 分成比例: 90%
|
||||
- 累计佣金: 8.1
|
||||
|
||||
[Withdraw] 提现验证:
|
||||
- 累计佣金 (totalCommission): 8.1
|
||||
- 待审核金额 (pendingWithdrawAmount): 0
|
||||
- 可提现金额 (availableAmount): 8.1
|
||||
- 申请提现金额 (amount): 8.1
|
||||
- 判断: 8.1 > 8.1 = false
|
||||
```
|
||||
|
||||
### 2. 测试提现
|
||||
|
||||
```bash
|
||||
# 测试提现API
|
||||
curl -X POST http://localhost:3006/api/withdraw \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"userId": "ogpTW5fmXRGNpoUbXB3UEqnVe5Tg",
|
||||
"amount": 8.1
|
||||
}'
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "提现成功",
|
||||
"data": {
|
||||
"withdrawId": "W1738694028123",
|
||||
"amount": 8.1,
|
||||
"account": "...",
|
||||
"accountType": "微信"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 验证数据库
|
||||
|
||||
```sql
|
||||
-- 查看待审核提现记录
|
||||
SELECT * FROM withdrawals WHERE user_id = 'ogpTW5fmXRGNpoUbXB3UEqnVe5Tg' AND status = 'pending';
|
||||
|
||||
-- 查看订单总金额
|
||||
SELECT SUM(amount) as total_amount
|
||||
FROM orders
|
||||
WHERE referrer_id = 'ogpTW5fmXRGNpoUbXB3UEqnVe5Tg' AND status = 'paid';
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 为什么改成"待审核"而不是"已提现"?
|
||||
|
||||
**A**: 因为提现流程是:
|
||||
1. 用户申请提现 → 状态 `pending`(待审核)
|
||||
2. 管理员审核通过 → 状态改为 `completed`(已完成)
|
||||
|
||||
在用户申请提现后,这笔金额应该从"可提现"中扣除,所以要减去 `status = 'pending'` 的金额。
|
||||
|
||||
### Q2: 如果有多笔待审核提现会怎样?
|
||||
|
||||
**A**:
|
||||
```
|
||||
累计佣金: 100元
|
||||
待审核: 30元 + 20元 = 50元
|
||||
可提现: 100 - 50 = 50元
|
||||
```
|
||||
|
||||
用户只能再申请最多50元的提现。
|
||||
|
||||
### Q3: 审核通过后会发生什么?
|
||||
|
||||
**A**:
|
||||
```sql
|
||||
-- 管理员审核通过
|
||||
UPDATE withdrawals SET status = 'completed' WHERE id = 'W123';
|
||||
```
|
||||
|
||||
这笔金额从"待审核"变为"已完成",下次计算时:
|
||||
```
|
||||
待审核金额减少 → 可提现金额增加(如果有新订单)
|
||||
```
|
||||
|
||||
### Q4: referral_bindings 表还有用吗?
|
||||
|
||||
**A**: 有用,但不再用于佣金计算:
|
||||
- 记录绑定关系
|
||||
- 记录绑定状态(active/expired)
|
||||
- 记录购买次数
|
||||
- 但佣金数据以 `orders` 表为准
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 1. 重启服务
|
||||
|
||||
修改后需要重启 Next.js 服务:
|
||||
|
||||
```bash
|
||||
# 使用部署脚本重启
|
||||
python devlop.py restart mycontent
|
||||
|
||||
# 或手动重启
|
||||
pm2 restart mycontent
|
||||
```
|
||||
|
||||
### 2. 查看日志
|
||||
|
||||
```bash
|
||||
# 查看实时日志
|
||||
pm2 logs mycontent
|
||||
|
||||
# 查看最近的日志
|
||||
pm2 logs mycontent --lines 100
|
||||
```
|
||||
|
||||
### 3. 监控错误
|
||||
|
||||
关注以下日志:
|
||||
- `[Withdraw] 佣金计算:` - 佣金计算是否正确
|
||||
- `[Withdraw] 提现验证:` - 可提现金额是否准确
|
||||
- `[Withdraw] 查询收益失败:` - 是否有表不存在等错误
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `app/api/withdraw/route.ts` - 提现接口(本次修改)
|
||||
- `app/api/referral/data/route.ts` - 分销数据接口(已统一)
|
||||
- `miniprogram/pages/referral/referral.js` - 前端逻辑(已统一)
|
||||
|
||||
## 总结
|
||||
|
||||
这次修复确保了**三端逻辑完全一致**:
|
||||
|
||||
1. **前端小程序**:显示的可提现金额
|
||||
2. **分销数据API**:返回的数据
|
||||
3. **提现接口**:验证的金额
|
||||
|
||||
都使用相同的计算方式:
|
||||
```
|
||||
可提现金额 = (订单总金额 × 分成比例) - 待审核提现金额
|
||||
```
|
||||
|
||||
这样可以避免前端显示可以提现,但后端验证失败的问题。
|
||||
Reference in New Issue
Block a user