547 lines
17 KiB
Markdown
547 lines
17 KiB
Markdown
# 后台提现审核功能完善说明
|
||
|
||
## 概述
|
||
|
||
完善后台管理的"提现审核"页面,增加用户佣金信息展示,帮助管理员做出准确的审核决策。
|
||
|
||
## 功能增强
|
||
|
||
### 1. 新增用户佣金信息展示
|
||
|
||
在提现记录表格中,新增"用户佣金信息"列,显示:
|
||
|
||
| 字段 | 说明 | 计算方式 | 颜色 |
|
||
|------|------|----------|------|
|
||
| 累计佣金 | 用户的历史总佣金 | `SUM(orders.amount) × 90%` | 青色 |
|
||
| 已提现 | 已到账的金额 | `users.withdrawn_earnings` | 灰色 |
|
||
| 待审核 | 所有待审核的提现申请 | `SUM(withdrawals.amount WHERE status='pending')` | 橙色 |
|
||
| 审核后余额 | 如果通过该申请,用户剩余的可提现金额 | `累计 - 已提现 - 待审核` | 绿色(≥0) / 红色(<0) |
|
||
|
||
### 2. 超额提现风险警告
|
||
|
||
当"审核后余额"为负数时:
|
||
- 以红色显示
|
||
- 批准时弹出二次确认警告
|
||
|
||
```
|
||
⚠️ 风险警告:该用户审核后余额为负数(¥-20.00),可能存在超额提现。
|
||
|
||
确认已核实用户账户并完成打款?
|
||
```
|
||
|
||
### 3. 用户头像显示
|
||
|
||
优化用户信息展示:
|
||
- ✅ 如果有头像,显示真实头像
|
||
- ✅ 如果无头像,显示昵称首字母
|
||
- ✅ 显示推荐码(如果有)
|
||
|
||
## 技术实现
|
||
|
||
### 后端API (`app/api/admin/withdrawals/route.ts`)
|
||
|
||
#### 查询优化
|
||
|
||
```typescript
|
||
SELECT
|
||
w.*,
|
||
u.nickname as user_nickname,
|
||
u.phone as user_phone,
|
||
u.avatar as user_avatar,
|
||
u.referral_code,
|
||
u.withdrawn_earnings,
|
||
u.earnings,
|
||
u.pending_earnings,
|
||
|
||
-- 计算累计佣金(从 orders 表)
|
||
(SELECT COALESCE(SUM(o.amount), 0)
|
||
FROM orders o
|
||
WHERE o.referrer_id = w.user_id AND o.status = 'paid') as total_order_amount,
|
||
|
||
-- 计算待审核提现金额(不包括当前这条)
|
||
(SELECT COALESCE(SUM(w2.amount), 0)
|
||
FROM withdrawals w2
|
||
WHERE w2.user_id = w.user_id AND w2.status = 'pending' AND w2.id != w.id) as other_pending_amount
|
||
|
||
FROM withdrawals w
|
||
LEFT JOIN users u ON w.user_id = u.id
|
||
```
|
||
|
||
#### 返回数据结构
|
||
|
||
```typescript
|
||
{
|
||
id: string,
|
||
userId: string,
|
||
userNickname: string,
|
||
userPhone: string,
|
||
userAvatar: string,
|
||
referralCode: string,
|
||
amount: number,
|
||
status: string,
|
||
createdAt: string,
|
||
processedAt: string,
|
||
|
||
// ✅ 新增:用户佣金信息
|
||
userCommissionInfo: {
|
||
totalCommission: number, // 累计佣金
|
||
withdrawnEarnings: number, // 已提现
|
||
pendingWithdrawals: number, // 待审核(包括当前)
|
||
availableAfterThis: number // 审核后余额
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 计算逻辑
|
||
|
||
```typescript
|
||
// 累计佣金(90%分成)
|
||
const totalCommission = parseFloat(w.total_order_amount) * 0.9
|
||
|
||
// 已提现金额
|
||
const withdrawnEarnings = parseFloat(w.withdrawn_earnings) || 0
|
||
|
||
// 其他待审核金额(不包括当前这笔)
|
||
const otherPendingAmount = parseFloat(w.other_pending_amount) || 0
|
||
|
||
// 当前提现金额
|
||
const currentWithdrawAmount = parseFloat(w.amount)
|
||
|
||
// ✅ 审核后余额 = 累计佣金 - 已提现 - 其他待审核 - 当前提现
|
||
const availableAfterThis = totalCommission - withdrawnEarnings - otherPendingAmount - currentWithdrawAmount
|
||
```
|
||
|
||
### 前端页面 (`app/admin/withdrawals/page.tsx`)
|
||
|
||
#### 界面布局
|
||
|
||
```tsx
|
||
<td className="p-4">
|
||
{w.userCommissionInfo ? (
|
||
<div className="text-xs space-y-1">
|
||
{/* 累计佣金 */}
|
||
<div className="flex justify-between gap-4">
|
||
<span className="text-gray-500">累计佣金:</span>
|
||
<span className="text-[#38bdac] font-medium">
|
||
¥{w.userCommissionInfo.totalCommission.toFixed(2)}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 已提现 */}
|
||
<div className="flex justify-between gap-4">
|
||
<span className="text-gray-500">已提现:</span>
|
||
<span className="text-gray-400">
|
||
¥{w.userCommissionInfo.withdrawnEarnings.toFixed(2)}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 待审核 */}
|
||
<div className="flex justify-between gap-4">
|
||
<span className="text-gray-500">待审核:</span>
|
||
<span className="text-orange-400">
|
||
¥{w.userCommissionInfo.pendingWithdrawals.toFixed(2)}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 审核后余额(带边框分隔) */}
|
||
<div className="flex justify-between gap-4 pt-1 border-t border-gray-700/30">
|
||
<span className="text-gray-500">审核后余额:</span>
|
||
<span className={
|
||
w.userCommissionInfo.availableAfterThis >= 0
|
||
? "text-green-400 font-medium"
|
||
: "text-red-400 font-medium"
|
||
}>
|
||
¥{w.userCommissionInfo.availableAfterThis.toFixed(2)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<span className="text-gray-500 text-xs">暂无数据</span>
|
||
)}
|
||
</td>
|
||
```
|
||
|
||
#### 风险警告
|
||
|
||
```typescript
|
||
const handleApprove = async (id: string) => {
|
||
const withdrawal = withdrawals.find(w => w.id === id)
|
||
|
||
// 检查超额提现风险
|
||
if (withdrawal?.userCommissionInfo?.availableAfterThis < 0) {
|
||
if (!confirm(`⚠️ 风险警告:该用户审核后余额为负数...`)) {
|
||
return
|
||
}
|
||
} else {
|
||
if (!confirm("确认已完成打款?批准后将更新用户提现记录。")) {
|
||
return
|
||
}
|
||
}
|
||
|
||
// 执行批准操作
|
||
// ...
|
||
}
|
||
```
|
||
|
||
## 使用场景
|
||
|
||
### 场景1:正常提现
|
||
|
||
**用户A的提现申请**:
|
||
- 累计佣金: ¥100.00
|
||
- 已提现: ¥30.00
|
||
- 待审核: ¥40.00(包括当前 ¥40)
|
||
- 审核后余额: ¥30.00 ✅ 绿色
|
||
|
||
**管理员判断**:
|
||
- ✅ 数据正常
|
||
- ✅ 审核通过后还有 ¥30 余额
|
||
- ✅ 可以批准
|
||
|
||
### 场景2:超额提现(风险)
|
||
|
||
**用户B的提现申请**:
|
||
- 累计佣金: ¥100.00
|
||
- 已提现: ¥30.00
|
||
- 待审核: ¥80.00(当前 ¥60 + 其他 ¥20)
|
||
- 审核后余额: **-¥10.00** ❌ 红色
|
||
|
||
**管理员判断**:
|
||
- ⚠️ 超额提现风险!
|
||
- ⚠️ 待审核总额 ¥80 > 可提现 ¥70
|
||
- ⚠️ 可能是并发提现或恶意提现
|
||
- 🛑 建议拒绝,或核实后只批准部分
|
||
|
||
### 场景3:多笔待审核
|
||
|
||
**用户C的多笔提现**:
|
||
|
||
**第一笔申请 ¥40**:
|
||
- 累计佣金: ¥100.00
|
||
- 已提现: ¥0.00
|
||
- 待审核: ¥40.00
|
||
- 审核后余额: ¥60.00 ✅
|
||
|
||
**第二笔申请 ¥30**:
|
||
- 累计佣金: ¥100.00
|
||
- 已提现: ¥0.00
|
||
- 待审核: ¥70.00(¥40 + ¥30)
|
||
- 审核后余额: ¥30.00 ✅
|
||
|
||
**第三笔申请 ¥40**:
|
||
- 累计佣金: ¥100.00
|
||
- 已提现: ¥0.00
|
||
- 待审核: ¥110.00(¥40 + ¥30 + ¥40)
|
||
- 审核后余额: **-¥10.00** ❌ 红色
|
||
|
||
**管理员策略**:
|
||
- ✅ 批准第一笔(¥40)
|
||
- ✅ 批准第二笔(¥30)
|
||
- ❌ 拒绝第三笔(¥40)- 余额不足
|
||
|
||
## 测试验证
|
||
|
||
### 1. 准备测试数据
|
||
|
||
```sql
|
||
-- 用户A:正常用户
|
||
INSERT INTO users (id, nickname, phone, withdrawn_earnings, referral_code)
|
||
VALUES ('user_a', '测试用户A', '13800138000', 0, 'SOULA001');
|
||
|
||
INSERT INTO orders (id, user_id, amount, referrer_id, status, pay_time)
|
||
VALUES ('order_a1', 'buyer_1', 100, 'user_a', 'paid', NOW());
|
||
|
||
INSERT INTO withdrawals (id, user_id, amount, status)
|
||
VALUES ('W_A1', 'user_a', 50, 'pending');
|
||
|
||
-- 用户B:超额提现用户
|
||
INSERT INTO users (id, nickname, phone, withdrawn_earnings, referral_code)
|
||
VALUES ('user_b', '测试用户B', '13900139000', 30, 'SOULB001');
|
||
|
||
INSERT INTO orders (id, user_id, amount, referrer_id, status, pay_time)
|
||
VALUES ('order_b1', 'buyer_2', 100, 'user_b', 'paid', NOW());
|
||
|
||
INSERT INTO withdrawals (id, user_id, amount, status)
|
||
VALUES
|
||
('W_B1', 'user_b', 20, 'pending'),
|
||
('W_B2', 'user_b', 60, 'pending');
|
||
```
|
||
|
||
### 2. 访问页面
|
||
|
||
```
|
||
http://localhost:3006/admin/withdrawals
|
||
```
|
||
|
||
### 3. 验证显示
|
||
|
||
**用户A的记录**:
|
||
- 累计佣金: ¥90.00(100 × 90%)
|
||
- 已提现: ¥0.00
|
||
- 待审核: ¥50.00
|
||
- 审核后余额: ¥40.00 ✅ 绿色
|
||
|
||
**用户B的记录(W_B1)**:
|
||
- 累计佣金: ¥90.00
|
||
- 已提现: ¥30.00
|
||
- 待审核: ¥80.00(20 + 60)
|
||
- 审核后余额: **-¥20.00** ❌ 红色
|
||
|
||
**用户B的记录(W_B2)**:
|
||
- 累计佣金: ¥90.00
|
||
- 已提现: ¥30.00
|
||
- 待审核: ¥80.00(20 + 60)
|
||
- 审核后余额: **-¥20.00** ❌ 红色
|
||
|
||
### 4. 测试批准操作
|
||
|
||
**批准用户A的提现**:
|
||
- 正常弹出确认框
|
||
- 批准成功
|
||
|
||
**批准用户B的提现**:
|
||
- ⚠️ 弹出风险警告
|
||
- 显示负数余额
|
||
- 需要二次确认
|
||
|
||
## 管理员决策指南
|
||
|
||
### 何时批准?
|
||
|
||
- ✅ 审核后余额 ≥ 0
|
||
- ✅ 用户信息真实完整
|
||
- ✅ 累计佣金合理(有订单支持)
|
||
- ✅ 已完成线下打款(或准备自动转账)
|
||
|
||
### 何时拒绝?
|
||
|
||
- ❌ 审核后余额 < 0
|
||
- ❌ 累计佣金异常(没有订单但有佣金)
|
||
- ❌ 同一用户有多笔待审核且总额超额
|
||
- ❌ 用户信息不全或异常
|
||
|
||
### 如何处理并发提现?
|
||
|
||
**发现场景**:同一用户有多笔待审核,总额超出可提现金额
|
||
|
||
**处理策略**:
|
||
1. 按时间顺序查看
|
||
2. 计算每笔的"审核后余额"
|
||
3. 批准最早的几笔(余额充足的)
|
||
4. 拒绝超额的后续申请
|
||
5. 联系用户说明情况
|
||
|
||
## 数据完整性说明
|
||
|
||
### 数据来源
|
||
|
||
| 数据 | 来源 | 可靠性 |
|
||
|------|------|--------|
|
||
| 累计佣金 | `orders` 表实时计算 | ⭐⭐⭐⭐⭐ 最准确 |
|
||
| 已提现 | `users.withdrawn_earnings` | ⭐⭐⭐⭐ 需要提现接口正确维护 |
|
||
| 待审核 | `withdrawals` 表实时查询 | ⭐⭐⭐⭐⭐ 实时准确 |
|
||
|
||
### 计算公式
|
||
|
||
```
|
||
累计佣金 = Σ(已付款订单金额) × 分成比例(90%)
|
||
已提现金额 = users.withdrawn_earnings
|
||
待审核金额 = Σ(status='pending'的提现申请)
|
||
审核后余额 = 累计佣金 - 已提现金额 - 待审核金额
|
||
```
|
||
|
||
## 界面效果
|
||
|
||
### 提现记录表格
|
||
|
||
```
|
||
┌──────────────┬───────────┬──────────┬─────────────────────┬────────┬──────────┬────────┐
|
||
│ 申请时间 │ 用户 │ 提现金额 │ 用户佣金信息 │ 状态 │ 处理时间 │ 操作 │
|
||
├──────────────┼───────────┼──────────┼─────────────────────┼────────┼──────────┼────────┤
|
||
│ 2026-02-04 │ [头像] │ ¥50.00 │ 累计佣金: ¥100.00 │ 待处理 │ - │ 批准 │
|
||
│ 15:30 │ 张三 │ │ 已提现: ¥30.00 │ │ │ 拒绝 │
|
||
│ │ 138***00 │ │ 待审核: ¥50.00 │ │ │ │
|
||
│ │ │ │ ───────────────── │ │ │ │
|
||
│ │ │ │ 审核后余额: ¥20.00 │ │ │ │
|
||
└──────────────┴───────────┴──────────┴─────────────────────┴────────┴──────────┴────────┘
|
||
```
|
||
|
||
### 风险提示(审核后余额<0)
|
||
|
||
```
|
||
┌──────────────┬───────────┬──────────┬─────────────────────┬────────┬──────────┬────────┐
|
||
│ 申请时间 │ 用户 │ 提现金额 │ 用户佣金信息 │ 状态 │ 处理时间 │ 操作 │
|
||
├──────────────┼───────────┼──────────┼─────────────────────┼────────┼──────────┼────────┤
|
||
│ 2026-02-04 │ [头像] │ ¥80.00 │ 累计佣金: ¥100.00 │ 待处理 │ - │ 批准 │
|
||
│ 15:35 │ 李四 │ │ 已提现: ¥30.00 │ │ │ 拒绝 │
|
||
│ │ 139***00 │ │ 待审核: ¥80.00 │ │ │ │
|
||
│ │ │ │ ───────────────── │ │ │ │
|
||
│ │ │ │ 审核后余额: -¥10.00 │ │ │ │
|
||
│ │ │ │ ↑ [红色] │ │ │ │
|
||
└──────────────┴───────────┴──────────┴─────────────────────┴────────┴──────────┴────────┘
|
||
```
|
||
|
||
**点击批准时**:
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ ⚠️ 风险警告 │
|
||
├─────────────────────────────────────────┤
|
||
│ 该用户审核后余额为负数(¥-10.00), │
|
||
│ 可能存在超额提现。 │
|
||
│ │
|
||
│ 确认已核实用户账户并完成打款? │
|
||
├─────────────────────────────────────────┤
|
||
│ [取消] [确定] │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
## 审核流程示例
|
||
|
||
### 正常流程
|
||
|
||
1. **管理员查看**:
|
||
- 用户:张三
|
||
- 提现金额:¥50
|
||
- 累计佣金:¥100,已提现:¥30,待审核:¥50
|
||
- 审核后余额:¥20 ✅
|
||
|
||
2. **判断**:数据正常,余额充足
|
||
|
||
3. **操作**:点击"批准"
|
||
|
||
4. **结果**:
|
||
- 提现记录状态改为 success
|
||
- 用户的 withdrawn_earnings += 50
|
||
- 用户收到微信零钱(自动转账)
|
||
|
||
### 风险流程
|
||
|
||
1. **管理员查看**:
|
||
- 用户:李四
|
||
- 提现金额:¥80
|
||
- 累计佣金:¥100,已提现:¥30,待审核:¥80
|
||
- 审核后余额:**-¥10** ❌
|
||
|
||
2. **判断**:超额提现,存在风险
|
||
|
||
3. **操作**:
|
||
- 方案A:直接拒绝,备注"余额不足"
|
||
- 方案B:核实数据后,只批准部分金额
|
||
|
||
4. **结果**:
|
||
- 拒绝:用户的待审核金额归零,可重新申请
|
||
- 部分批准:需要先拒绝原申请,等用户重新申请合理金额
|
||
|
||
## 相关文件
|
||
|
||
### 后端文件
|
||
- `app/api/admin/withdrawals/route.ts` - 提现审核API ✅
|
||
- `app/api/withdraw/route.ts` - 用户提现接口
|
||
- `app/api/referral/data/route.ts` - 分销数据接口
|
||
|
||
### 前端文件
|
||
- `app/admin/withdrawals/page.tsx` - 提现审核页面 ✅
|
||
- `miniprogram/pages/referral/referral.js` - 小程序提现页面
|
||
|
||
### 脚本文件
|
||
- `scripts/verify-withdrawal-data.sql` - 数据验证SQL脚本 ✅
|
||
|
||
## 部署步骤
|
||
|
||
### 1. 重启后端服务
|
||
|
||
```bash
|
||
python devlop.py restart mycontent
|
||
```
|
||
|
||
### 2. 访问管理后台
|
||
|
||
```
|
||
http://localhost:3006/admin/withdrawals
|
||
```
|
||
|
||
### 3. 验证数据显示
|
||
|
||
- [ ] 用户信息完整(昵称、头像、电话)
|
||
- [ ] 提现金额正确
|
||
- [ ] 佣金信息完整(累计、已提现、待审核、审核后余额)
|
||
- [ ] 审核后余额颜色正确(绿色/红色)
|
||
- [ ] 批准/拒绝按钮可用
|
||
|
||
### 4. 测试审核操作
|
||
|
||
- [ ] 批准正常提现(审核后余额≥0)
|
||
- [ ] 批准风险提现(会弹出警告)
|
||
- [ ] 拒绝提现(余额返还)
|
||
|
||
### 5. 验证数据更新
|
||
|
||
批准后:
|
||
```sql
|
||
-- 检查提现记录状态
|
||
SELECT status FROM withdrawals WHERE id = 'W_XXX';
|
||
-- 应该是 'success'
|
||
|
||
-- 检查用户已提现金额
|
||
SELECT withdrawn_earnings FROM users WHERE id = 'user_xxx';
|
||
-- 应该增加了提现金额
|
||
```
|
||
|
||
## 后续优化建议
|
||
|
||
### 1. 批量审核
|
||
|
||
选中多条记录,一键批准所有:
|
||
```tsx
|
||
<Button onClick={handleBatchApprove}>
|
||
批量批准已勾选项
|
||
</Button>
|
||
```
|
||
|
||
### 2. 自动审核
|
||
|
||
小额提现(如 ≤ ¥50)自动审核通过:
|
||
```typescript
|
||
if (amount <= 50 && availableAmount >= amount) {
|
||
// 自动批准
|
||
}
|
||
```
|
||
|
||
### 3. 审核备注
|
||
|
||
添加备注字段,记录审核原因:
|
||
```sql
|
||
ALTER TABLE withdrawals ADD COLUMN admin_note VARCHAR(500);
|
||
```
|
||
|
||
### 4. 审核历史
|
||
|
||
记录审核人和审核时间:
|
||
```sql
|
||
ALTER TABLE withdrawals
|
||
ADD COLUMN approved_by VARCHAR(64),
|
||
ADD COLUMN approved_at TIMESTAMP;
|
||
```
|
||
|
||
## 总结
|
||
|
||
这次数据对接实现了:
|
||
|
||
### 信息完整性
|
||
- ✅ 显示用户的累计佣金
|
||
- ✅ 显示用户的已提现金额
|
||
- ✅ 显示用户的待审核金额
|
||
- ✅ 计算审核后余额
|
||
|
||
### 风险防控
|
||
- ✅ 超额提现自动标红
|
||
- ✅ 批准时弹出风险警告
|
||
- ✅ 帮助管理员做出正确决策
|
||
|
||
### 用户体验
|
||
- ✅ 界面清晰,数据一目了然
|
||
- ✅ 颜色区分(绿色安全,红色风险)
|
||
- ✅ 详细的计算明细
|
||
|
||
**核心价值**:让管理员能够**快速准确地审核提现申请**,同时**有效防范超额提现风险**。
|