368 lines
11 KiB
Markdown
368 lines
11 KiB
Markdown
|
|
# 提现卡片数据优化说明
|
|||
|
|
|
|||
|
|
## 一、修改需求
|
|||
|
|
|
|||
|
|
**用户需求**:
|
|||
|
|
1. **累计佣金**:显示用户获得的所有佣金总额(包括可提现、待审核、已提现的所有佣金)
|
|||
|
|
2. **待审核金额**:显示当前已发起提现申请但还未审核的金额累计总和(`withdrawals` 表中 `status = 'pending'` 的金额)
|
|||
|
|
3. **可提现金额**:显示可以发起提现的金额(即 `users.pending_earnings`)
|
|||
|
|
|
|||
|
|
## 二、数据定义
|
|||
|
|
|
|||
|
|
### 1. 原数据结构
|
|||
|
|
|
|||
|
|
| 字段 | 原定义 | 问题 |
|
|||
|
|
|------|--------|------|
|
|||
|
|
| `users.earnings` | 已结算收益 | 不够直观 |
|
|||
|
|
| `users.pending_earnings` | 待结算收益 | 命名容易误解,实际是可提现金额 |
|
|||
|
|
| `users.withdrawn_earnings` | 已提现金额 | ✅ 正确 |
|
|||
|
|
|
|||
|
|
### 2. 新数据结构
|
|||
|
|
|
|||
|
|
| 字段 | 新定义 | 说明 |
|
|||
|
|
|------|--------|------|
|
|||
|
|
| **累计佣金** (`totalCommission`) | `earnings` + `pending_earnings` + `withdrawn_earnings` | 所有获得的佣金总额 |
|
|||
|
|
| **可提现金额** (`availableEarnings`) | `pending_earnings` | 未申请提现的佣金,可以发起提现 |
|
|||
|
|
| **待审核金额** (`pendingWithdrawAmount`) | `SUM(withdrawals.amount) WHERE status='pending'` | 已发起提现但未审核的金额 |
|
|||
|
|
| **已提现金额** (`withdrawnEarnings`) | `withdrawn_earnings` | 已成功提现的金额 |
|
|||
|
|
|
|||
|
|
### 3. 业务流程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户获得佣金
|
|||
|
|
↓
|
|||
|
|
累计佣金 +X
|
|||
|
|
可提现金额 +X (pending_earnings)
|
|||
|
|
↓
|
|||
|
|
用户发起提现申请
|
|||
|
|
↓
|
|||
|
|
可提现金额 -X (pending_earnings)
|
|||
|
|
待审核金额 +X (withdrawals.status='pending')
|
|||
|
|
↓
|
|||
|
|
管理员审核通过
|
|||
|
|
↓
|
|||
|
|
待审核金额 -X (withdrawals.status='success')
|
|||
|
|
已提现金额 +X (withdrawn_earnings)
|
|||
|
|
累计佣金不变
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 三、代码修改
|
|||
|
|
|
|||
|
|
### 1. 后端 API 修改 (`app/api/referral/data/route.ts`)
|
|||
|
|
|
|||
|
|
#### 添加待审核金额查询
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 7. 获取待审核提现金额
|
|||
|
|
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('[ReferralData] 获取待审核提现金额失败:', e)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改返回数据
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// === 收益数据 ===
|
|||
|
|
// 累计佣金总额(所有获得的佣金)
|
|||
|
|
totalCommission: Math.round((
|
|||
|
|
(parseFloat(user.earnings) || 0) +
|
|||
|
|
(parseFloat(user.pending_earnings) || 0) +
|
|||
|
|
(parseFloat(user.withdrawn_earnings) || 0)
|
|||
|
|
) * 100) / 100,
|
|||
|
|
// 可提现金额(pending_earnings)
|
|||
|
|
availableEarnings: parseFloat(user.pending_earnings) || 0,
|
|||
|
|
// 待审核金额(提现申请中的金额)
|
|||
|
|
pendingWithdrawAmount: Math.round(pendingWithdrawAmount * 100) / 100,
|
|||
|
|
// 已提现金额
|
|||
|
|
withdrawnEarnings: parseFloat(user.withdrawn_earnings) || 0,
|
|||
|
|
// 已结算收益(保留兼容)
|
|||
|
|
earnings: parseFloat(user.earnings) || 0,
|
|||
|
|
// 待结算收益(保留兼容)
|
|||
|
|
pendingEarnings: parseFloat(user.pending_earnings) || 0,
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 小程序前端修改
|
|||
|
|
|
|||
|
|
#### 数据字段 (`miniprogram/pages/referral/referral.js`)
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
data: {
|
|||
|
|
// === 收益数据 ===
|
|||
|
|
totalCommission: 0, // 累计佣金总额(所有获得的佣金)
|
|||
|
|
availableEarnings: 0, // 可提现金额(未申请提现的佣金)
|
|||
|
|
pendingWithdrawAmount: 0, // 待审核金额(已申请提现但未审核)
|
|||
|
|
withdrawnEarnings: 0, // 已提现金额
|
|||
|
|
earnings: 0, // 已结算收益(保留兼容)
|
|||
|
|
pendingEarnings: 0, // 待结算收益(保留兼容)
|
|||
|
|
shareRate: 90, // 分成比例(90%)
|
|||
|
|
minWithdrawAmount: 10, // 最低提现金额(从后端获取)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 数据更新逻辑
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
this.setData({
|
|||
|
|
// 收益数据 - 格式化为两位小数
|
|||
|
|
totalCommission: formatMoney(realData?.totalCommission || 0),
|
|||
|
|
availableEarnings: formatMoney(realData?.availableEarnings || 0),
|
|||
|
|
pendingWithdrawAmount: formatMoney(realData?.pendingWithdrawAmount || 0),
|
|||
|
|
withdrawnEarnings: formatMoney(realData?.withdrawnEarnings || 0),
|
|||
|
|
earnings: formatMoney(realData?.earnings || 0),
|
|||
|
|
pendingEarnings: formatMoney(realData?.pendingEarnings || 0),
|
|||
|
|
shareRate: realData?.shareRate || 90,
|
|||
|
|
minWithdrawAmount: realData?.minWithdrawAmount || 10,
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 提现逻辑修改
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
async handleWithdraw() {
|
|||
|
|
const availableEarnings = parseFloat(this.data.availableEarnings) || 0
|
|||
|
|
const minWithdrawAmount = this.data.minWithdrawAmount || 10
|
|||
|
|
|
|||
|
|
if (availableEarnings <= 0) {
|
|||
|
|
wx.showToast({ title: '暂无可提现收益', icon: 'none' })
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否达到最低提现金额
|
|||
|
|
if (availableEarnings < minWithdrawAmount) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: `满${minWithdrawAmount}元可提现`,
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认提现
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '确认提现',
|
|||
|
|
content: `将提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱`,
|
|||
|
|
confirmText: '立即提现',
|
|||
|
|
success: async (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
await this.doWithdraw(availableEarnings)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. UI 界面修改 (`miniprogram/pages/referral/referral.wxml`)
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<!-- 收益卡片 - 对齐 Next.js -->
|
|||
|
|
<view class="earnings-card">
|
|||
|
|
<view class="earnings-bg"></view>
|
|||
|
|
<view class="earnings-main">
|
|||
|
|
<view class="earnings-header">
|
|||
|
|
<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>
|
|||
|
|
<view class="withdraw-btn {{availableEarnings < minWithdrawAmount ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
|
|||
|
|
{{availableEarnings < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : '申请提现 ¥' + availableEarnings}}
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 界面变化对比
|
|||
|
|
|
|||
|
|
| 位置 | 原显示 | 新显示 | 说明 |
|
|||
|
|
|------|--------|--------|------|
|
|||
|
|
| 卡片标题 | 累计收益 | 累计佣金 | 更准确的描述 |
|
|||
|
|
| 主金额 | `earnings` | `totalCommission` | 显示所有佣金总和 |
|
|||
|
|
| 副金额标签 | "待结算" | "待审核" | 更明确的状态描述 |
|
|||
|
|
| 副金额 | `pendingEarnings` | `pendingWithdrawAmount` | 显示提现申请中的金额 |
|
|||
|
|
| 按钮文案 | "申请提现" | "申请提现 ¥XX" | 显示可提现金额 |
|
|||
|
|
| 按钮禁用 | `pendingEarnings < minWithdrawAmount` | `availableEarnings < minWithdrawAmount` | 使用可提现金额判断 |
|
|||
|
|
|
|||
|
|
## 四、验证方法
|
|||
|
|
|
|||
|
|
### 1. 数据库检查
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 查看用户收益数据
|
|||
|
|
SELECT
|
|||
|
|
id,
|
|||
|
|
nickname,
|
|||
|
|
earnings,
|
|||
|
|
pending_earnings,
|
|||
|
|
withdrawn_earnings,
|
|||
|
|
(earnings + pending_earnings + withdrawn_earnings) as total_commission
|
|||
|
|
FROM users
|
|||
|
|
WHERE id = 'user_xxx';
|
|||
|
|
|
|||
|
|
-- 查看待审核提现金额
|
|||
|
|
SELECT
|
|||
|
|
user_id,
|
|||
|
|
SUM(amount) as pending_amount
|
|||
|
|
FROM withdrawals
|
|||
|
|
WHERE status = 'pending'
|
|||
|
|
GROUP BY user_id;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. API 测试
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 测试接口
|
|||
|
|
curl -X GET "http://localhost:3000/api/referral/data" \
|
|||
|
|
-H "Cookie: auth_token=xxx"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**期望返回数据**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"success": true,
|
|||
|
|
"data": {
|
|||
|
|
"totalCommission": 100.00, // 累计佣金 = 30 + 50 + 20
|
|||
|
|
"availableEarnings": 50.00, // 可提现 = pending_earnings
|
|||
|
|
"pendingWithdrawAmount": 20.00, // 待审核 = SUM(withdrawals WHERE status='pending')
|
|||
|
|
"withdrawnEarnings": 30.00, // 已提现
|
|||
|
|
"earnings": 30.00, // 已结算(保留兼容)
|
|||
|
|
"pendingEarnings": 50.00, // 待结算(保留兼容)
|
|||
|
|
"shareRate": 90,
|
|||
|
|
"minWithdrawAmount": 10
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 小程序测试
|
|||
|
|
|
|||
|
|
1. **查看提现卡片**
|
|||
|
|
- ✅ 累计佣金显示正确(所有佣金总和)
|
|||
|
|
- ✅ 待审核金额显示正确(提现申请中的金额)
|
|||
|
|
- ✅ 提现按钮显示可提现金额
|
|||
|
|
|
|||
|
|
2. **发起提现**
|
|||
|
|
- ✅ 提现按钮使用 `availableEarnings` 判断是否可用
|
|||
|
|
- ✅ 提现金额为 `availableEarnings`
|
|||
|
|
- ✅ 提现后,`availableEarnings` 减少,`pendingWithdrawAmount` 增加
|
|||
|
|
|
|||
|
|
3. **管理员审核后**
|
|||
|
|
- ✅ `pendingWithdrawAmount` 减少
|
|||
|
|
- ✅ `withdrawnEarnings` 增加
|
|||
|
|
- ✅ `totalCommission` 保持不变
|
|||
|
|
|
|||
|
|
## 五、注意事项
|
|||
|
|
|
|||
|
|
### 1. 向后兼容
|
|||
|
|
|
|||
|
|
为了保证系统稳定,保留了原有的 `earnings` 和 `pendingEarnings` 字段,仅在小程序中使用新字段。
|
|||
|
|
|
|||
|
|
### 2. 提现流程
|
|||
|
|
|
|||
|
|
用户发起提现时,系统会:
|
|||
|
|
1. 扣减 `users.pending_earnings`
|
|||
|
|
2. 创建 `withdrawals` 记录(`status = 'pending'`)
|
|||
|
|
3. 管理员审核通过后,`withdrawals.status` 改为 `'success'`
|
|||
|
|
4. 同时增加 `users.withdrawn_earnings`
|
|||
|
|
|
|||
|
|
### 3. 数据一致性
|
|||
|
|
|
|||
|
|
确保以下等式始终成立:
|
|||
|
|
```
|
|||
|
|
totalCommission = availableEarnings + pendingWithdrawAmount + withdrawnEarnings
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 前端显示
|
|||
|
|
|
|||
|
|
所有金额都使用 `formatMoney()` 函数格式化为两位小数。
|
|||
|
|
|
|||
|
|
## 六、影响范围
|
|||
|
|
|
|||
|
|
### 修改文件
|
|||
|
|
|
|||
|
|
1. **后端**
|
|||
|
|
- `app/api/referral/data/route.ts` - 添加 `pendingWithdrawAmount` 查询和返回字段
|
|||
|
|
|
|||
|
|
2. **小程序**
|
|||
|
|
- `miniprogram/pages/referral/referral.js` - 数据字段和提现逻辑
|
|||
|
|
- `miniprogram/pages/referral/referral.wxml` - UI 显示
|
|||
|
|
|
|||
|
|
### 不影响
|
|||
|
|
|
|||
|
|
- ❌ 提现流程逻辑(`/api/withdraw`)
|
|||
|
|
- ❌ 管理后台(仍使用原字段)
|
|||
|
|
- ❌ 佣金计算逻辑(`/api/payment/*/notify`)
|
|||
|
|
- ❌ 数据库表结构(无需修改)
|
|||
|
|
|
|||
|
|
## 七、测试场景
|
|||
|
|
|
|||
|
|
### 场景 1:新用户获得佣金
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
初始状态:
|
|||
|
|
- totalCommission = 0
|
|||
|
|
- availableEarnings = 0
|
|||
|
|
- pendingWithdrawAmount = 0
|
|||
|
|
- withdrawnEarnings = 0
|
|||
|
|
|
|||
|
|
用户 A 购买了 100 元的书籍,推荐人 B 获得 90 元佣金:
|
|||
|
|
- totalCommission = 90
|
|||
|
|
- availableEarnings = 90
|
|||
|
|
- pendingWithdrawAmount = 0
|
|||
|
|
- withdrawnEarnings = 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 场景 2:用户发起提现
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户 B 发起提现 90 元:
|
|||
|
|
- totalCommission = 90(不变)
|
|||
|
|
- availableEarnings = 0(减少 90)
|
|||
|
|
- pendingWithdrawAmount = 90(增加 90)
|
|||
|
|
- withdrawnEarnings = 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 场景 3:管理员审核通过
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
管理员审核通过,打款成功:
|
|||
|
|
- totalCommission = 90(不变)
|
|||
|
|
- availableEarnings = 0
|
|||
|
|
- pendingWithdrawAmount = 0(减少 90)
|
|||
|
|
- withdrawnEarnings = 90(增加 90)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 场景 4:管理员拒绝提现
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
管理员拒绝提现:
|
|||
|
|
- totalCommission = 90(不变)
|
|||
|
|
- availableEarnings = 90(恢复 90)
|
|||
|
|
- pendingWithdrawAmount = 0(减少 90)
|
|||
|
|
- withdrawnEarnings = 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 八、总结
|
|||
|
|
|
|||
|
|
此次优化主要解决了提现卡片数据定义不清晰的问题:
|
|||
|
|
|
|||
|
|
1. **累计佣金**:直观展示用户获得的所有佣金
|
|||
|
|
2. **可提现金额**:明确告知用户可以发起提现的金额
|
|||
|
|
3. **待审核金额**:让用户清楚知道有多少提现申请正在处理中
|
|||
|
|
|
|||
|
|
优化后的界面更加清晰、易懂,用户体验更佳!
|