538 lines
12 KiB
Markdown
538 lines
12 KiB
Markdown
# 新分销逻辑 - 部署步骤
|
||
|
||
## 📋 部署前检查
|
||
|
||
### 确认新逻辑
|
||
- ✅ 点击谁的链接,立即绑定谁(无条件切换)
|
||
- ✅ 购买时,佣金给当前推荐人
|
||
- ✅ 30天内无购买 → 自动解绑
|
||
- ✅ 方案A:购买后不重置30天
|
||
|
||
### 备份数据
|
||
```bash
|
||
# 1. 备份数据库
|
||
mysqldump -u root -p mycontent_db > backup_before_referral_$(date +%Y%m%d).sql
|
||
|
||
# 2. 备份代码
|
||
cd /www/wwwroot/soul
|
||
tar -czf backup_code_$(date +%Y%m%d).tar.gz app/ lib/ scripts/
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 部署步骤
|
||
|
||
### Step 1: 数据库迁移
|
||
|
||
#### 方式1:使用 Python 脚本(推荐)
|
||
|
||
```bash
|
||
# 1. 上传脚本到服务器
|
||
cd /www/wwwroot/soul
|
||
# 将 scripts/migrate_binding_fields.py 上传到服务器
|
||
|
||
# 2. 确保环境变量正确(.env 文件)
|
||
cat .env | grep DB_
|
||
|
||
# 3. 执行迁移
|
||
python3 scripts/migrate_binding_fields.py
|
||
```
|
||
|
||
**预期输出**:
|
||
```
|
||
==========================================================
|
||
数据库迁移:referral_bindings 表字段升级
|
||
==========================================================
|
||
|
||
✅ 已连接到数据库: mycontent_db
|
||
|
||
步骤 1: 添加新字段
|
||
------------------------------------------------------------
|
||
✅ 添加字段 last_purchase_date
|
||
✅ 添加字段 purchase_count
|
||
✅ 添加字段 total_commission
|
||
|
||
步骤 2: 添加索引
|
||
------------------------------------------------------------
|
||
✅ 添加索引 idx_referee_status
|
||
✅ 添加索引 idx_expiry_purchase
|
||
|
||
步骤 3: 更新 status 枚举(添加 cancelled)
|
||
------------------------------------------------------------
|
||
✅ 更新 status 枚举类型
|
||
|
||
步骤 4: 验证迁移结果
|
||
------------------------------------------------------------
|
||
✅ 字段 last_purchase_date 已存在
|
||
✅ 字段 purchase_count 已存在
|
||
✅ 字段 total_commission 已存在
|
||
|
||
==========================================================
|
||
✅ 迁移完成!
|
||
==========================================================
|
||
```
|
||
|
||
#### 方式2:直接执行 SQL
|
||
|
||
```bash
|
||
# 连接数据库
|
||
mysql -u root -p mycontent_db
|
||
|
||
# 执行迁移SQL
|
||
source scripts/migration-add-binding-fields.sql;
|
||
|
||
# 验证字段
|
||
SHOW COLUMNS FROM referral_bindings;
|
||
```
|
||
|
||
---
|
||
|
||
### Step 2: 部署代码
|
||
|
||
#### 本地构建
|
||
```bash
|
||
# 在本地项目目录
|
||
cd e:\Gongsi\Mycontent
|
||
|
||
# 构建
|
||
pnpm build
|
||
|
||
# 确认构建产物
|
||
ls -la .next/standalone
|
||
```
|
||
|
||
#### 上传到服务器
|
||
```bash
|
||
# 使用 devlop.py(自动化部署)
|
||
python devlop.py
|
||
|
||
# 或手动上传
|
||
# 1. 上传修改的文件:
|
||
# - app/api/referral/bind/route.ts
|
||
# - app/api/miniprogram/pay/notify/route.ts
|
||
# - scripts/auto-unbind-expired-simple.js
|
||
```
|
||
|
||
---
|
||
|
||
### Step 3: 重启服务
|
||
|
||
```bash
|
||
# 重启 PM2
|
||
pm2 restart soul
|
||
|
||
# 查看日志确认启动正常
|
||
pm2 logs soul --lines 50
|
||
|
||
# 确认进程状态
|
||
pm2 status
|
||
```
|
||
|
||
**预期输出**:
|
||
```
|
||
┌─────┬────────┬─────────┬──────┬─────┬──────────┐
|
||
│ id │ name │ status │ ↺ │ cpu │ memory │
|
||
├─────┼────────┼─────────┼──────┼─────┼──────────┤
|
||
│ 0 │ soul │ online │ 0 │ 0% │ 100.0mb │
|
||
└─────┴────────┴─────────┴──────┴─────┴──────────┘
|
||
```
|
||
|
||
---
|
||
|
||
### Step 4: 配置定时任务
|
||
|
||
#### 宝塔面板配置
|
||
|
||
1. 登录宝塔面板
|
||
2. 进入"计划任务"
|
||
3. 添加 Shell 脚本任务
|
||
|
||
**任务配置**:
|
||
- **任务名称**:自动解绑过期推荐关系
|
||
- **执行周期**:每天 02:00
|
||
- **脚本内容**:
|
||
```bash
|
||
cd /www/wwwroot/soul && node scripts/auto-unbind-expired-simple.js >> /www/wwwroot/soul/logs/auto-unbind.log 2>&1
|
||
```
|
||
|
||
#### 手动测试定时任务
|
||
|
||
```bash
|
||
# 进入项目目录
|
||
cd /www/wwwroot/soul
|
||
|
||
# 创建日志目录
|
||
mkdir -p logs
|
||
|
||
# 手动执行一次
|
||
node scripts/auto-unbind-expired-simple.js
|
||
|
||
# 查看日志
|
||
cat logs/auto-unbind.log
|
||
```
|
||
|
||
**预期输出**(如果有过期记录):
|
||
```
|
||
============================================================
|
||
自动解绑定时任务
|
||
执行时间: 2026/2/5 02:00:00
|
||
============================================================
|
||
|
||
✅ 已连接到数据库: mycontent_db
|
||
|
||
步骤 1: 查询需要解绑的记录...
|
||
------------------------------------------------------------
|
||
找到 3 条需要解绑的记录
|
||
|
||
步骤 2: 解绑明细
|
||
------------------------------------------------------------
|
||
1. 用户 user_abc123
|
||
推荐人: user_xyz789
|
||
绑定时间: 2026/1/5
|
||
过期时间: 2026/2/4 (已过期 1 天)
|
||
购买次数: 0
|
||
累计佣金: ¥0.00
|
||
|
||
...
|
||
|
||
步骤 3: 执行解绑操作...
|
||
------------------------------------------------------------
|
||
✅ 已成功解绑 3 条记录
|
||
|
||
步骤 4: 更新推荐人统计...
|
||
------------------------------------------------------------
|
||
- user_xyz789: -2 个绑定
|
||
- user_def456: -1 个绑定
|
||
✅ 已更新 2 个推荐人的统计数据
|
||
|
||
============================================================
|
||
✅ 任务完成
|
||
- 解绑记录数: 3
|
||
- 受影响推荐人: 2
|
||
============================================================
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 功能测试
|
||
|
||
### 测试用例1:立即切换绑定
|
||
|
||
#### 准备工作
|
||
```bash
|
||
# 创建测试用户 A、B、C
|
||
# A 推荐 B
|
||
# C 也想抢 B
|
||
```
|
||
|
||
#### 测试步骤
|
||
```bash
|
||
# 1. A 推荐 B(新绑定)
|
||
curl -X POST http://localhost:3006/api/referral/bind \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"userId": "test_user_b",
|
||
"referralCode": "SOULA001",
|
||
"source": "miniprogram"
|
||
}'
|
||
|
||
# 预期返回:
|
||
# {
|
||
# "success": true,
|
||
# "message": "绑定成功",
|
||
# "action": "new",
|
||
# "expiryDate": "2026-03-07T...",
|
||
# "referrer": { "id": "test_user_a", "nickname": "用户A" }
|
||
# }
|
||
|
||
# 2. B 点击 C 的链接(立即切换)
|
||
curl -X POST http://localhost:3006/api/referral/bind \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"userId": "test_user_b",
|
||
"referralCode": "SOULC001",
|
||
"source": "miniprogram"
|
||
}'
|
||
|
||
# 预期返回:
|
||
# {
|
||
# "success": true,
|
||
# "message": "已切换推荐人",
|
||
# "action": "switch",
|
||
# "expiryDate": "2026-03-07T...",
|
||
# "referrer": { "id": "test_user_c", "nickname": "用户C" },
|
||
# "oldReferrerId": "test_user_a"
|
||
# }
|
||
|
||
# 3. 验证数据库
|
||
mysql -u root -p mycontent_db -e "
|
||
SELECT referee_id, referrer_id, status, binding_date, expiry_date
|
||
FROM referral_bindings
|
||
WHERE referee_id = 'test_user_b'
|
||
ORDER BY binding_date DESC LIMIT 2;
|
||
"
|
||
|
||
# 预期结果:
|
||
# 记录1: referee=B, referrer=C, status=active (最新)
|
||
# 记录2: referee=B, referrer=A, status=cancelled (旧)
|
||
```
|
||
|
||
---
|
||
|
||
### 测试用例2:购买分佣(累加)
|
||
|
||
#### 测试步骤
|
||
```bash
|
||
# 1. B 购买第1次(1元)
|
||
# 触发支付回调 -> /api/miniprogram/pay/notify
|
||
|
||
# 2. 查询分佣结果
|
||
mysql -u root -p mycontent_db -e "
|
||
SELECT
|
||
rb.referrer_id,
|
||
rb.purchase_count,
|
||
rb.total_commission,
|
||
u.pending_earnings
|
||
FROM referral_bindings rb
|
||
JOIN users u ON rb.referrer_id = u.id
|
||
WHERE rb.referee_id = 'test_user_b' AND rb.status = 'active';
|
||
"
|
||
|
||
# 预期结果:
|
||
# referrer_id: test_user_c
|
||
# purchase_count: 1
|
||
# total_commission: 0.90 (假设90%分成)
|
||
# pending_earnings: 0.90
|
||
|
||
# 3. B 购买第2次(1元)
|
||
# 再次触发支付回调
|
||
|
||
# 4. 再次查询
|
||
# 预期结果:
|
||
# purchase_count: 2
|
||
# total_commission: 1.80
|
||
# pending_earnings: 1.80
|
||
```
|
||
|
||
---
|
||
|
||
### 测试用例3:30天自动解绑
|
||
|
||
#### 模拟测试(修改过期时间)
|
||
```bash
|
||
# 1. 手动修改绑定的过期时间(测试用)
|
||
mysql -u root -p mycontent_db -e "
|
||
UPDATE referral_bindings
|
||
SET expiry_date = '2026-02-04 00:00:00'
|
||
WHERE referee_id = 'test_user_x' AND referrer_id = 'test_user_y';
|
||
"
|
||
|
||
# 2. 执行定时任务
|
||
node scripts/auto-unbind-expired-simple.js
|
||
|
||
# 3. 验证解绑
|
||
mysql -u root -p mycontent_db -e "
|
||
SELECT referee_id, referrer_id, status, expiry_date, purchase_count
|
||
FROM referral_bindings
|
||
WHERE referee_id = 'test_user_x';
|
||
"
|
||
|
||
# 预期结果:
|
||
# status: expired(如果 purchase_count = 0)
|
||
# status: active(如果 purchase_count > 0)
|
||
```
|
||
|
||
---
|
||
|
||
## 🔍 监控与日志
|
||
|
||
### 查看绑定切换日志
|
||
```bash
|
||
# PM2 日志
|
||
pm2 logs soul | grep "Referral Bind"
|
||
|
||
# 查找"立即切换"记录
|
||
pm2 logs soul | grep "立即切换"
|
||
```
|
||
|
||
### 查看分佣日志
|
||
```bash
|
||
# 查看分佣成功记录
|
||
pm2 logs soul | grep "分佣完成"
|
||
|
||
# 查看累加情况
|
||
pm2 logs soul | grep "purchaseCount"
|
||
```
|
||
|
||
### 定时任务日志
|
||
```bash
|
||
# 查看定时任务执行记录
|
||
cat /www/wwwroot/soul/logs/auto-unbind.log
|
||
|
||
# 实时监控
|
||
tail -f /www/wwwroot/soul/logs/auto-unbind.log
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 数据统计
|
||
|
||
### 查看当前绑定状态分布
|
||
```sql
|
||
SELECT
|
||
status,
|
||
COUNT(*) as count,
|
||
SUM(purchase_count) as total_purchases,
|
||
SUM(total_commission) as total_commission
|
||
FROM referral_bindings
|
||
GROUP BY status;
|
||
```
|
||
|
||
### 查看切换频率最高的用户
|
||
```sql
|
||
SELECT
|
||
referee_id,
|
||
COUNT(*) as binding_count,
|
||
GROUP_CONCAT(referrer_id ORDER BY binding_date DESC) as referrer_history
|
||
FROM referral_bindings
|
||
WHERE status IN ('active', 'cancelled')
|
||
GROUP BY referee_id
|
||
HAVING COUNT(*) > 1
|
||
ORDER BY binding_count DESC
|
||
LIMIT 10;
|
||
```
|
||
|
||
### 查看30天内即将过期的绑定
|
||
```sql
|
||
SELECT
|
||
referee_id,
|
||
referrer_id,
|
||
binding_date,
|
||
expiry_date,
|
||
DATEDIFF(expiry_date, NOW()) as days_left,
|
||
purchase_count
|
||
FROM referral_bindings
|
||
WHERE status = 'active'
|
||
AND expiry_date > NOW()
|
||
AND DATEDIFF(expiry_date, NOW()) <= 7
|
||
ORDER BY days_left ASC;
|
||
```
|
||
|
||
---
|
||
|
||
## ⚠️ 回滚方案
|
||
|
||
### 如果需要回滚到旧逻辑
|
||
|
||
#### 1. 恢复数据库
|
||
```bash
|
||
# 停止服务
|
||
pm2 stop soul
|
||
|
||
# 恢复备份
|
||
mysql -u root -p mycontent_db < backup_before_referral_20260205.sql
|
||
|
||
# 重启服务
|
||
pm2 start soul
|
||
```
|
||
|
||
#### 2. 恢复代码
|
||
```bash
|
||
# 方式1:Git回滚
|
||
cd /www/wwwroot/soul
|
||
git reset --hard <上一个commit>
|
||
|
||
# 方式2:恢复备份
|
||
tar -xzf backup_code_20260205.tar.gz
|
||
|
||
# 重启
|
||
pm2 restart soul
|
||
```
|
||
|
||
#### 3. 停用定时任务
|
||
```bash
|
||
# 宝塔面板 -> 计划任务 -> 停用或删除"自动解绑"任务
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 常见问题
|
||
|
||
### Q1: 定时任务没有执行?
|
||
**检查步骤**:
|
||
1. 确认宝塔计划任务状态为"启用"
|
||
2. 查看宝塔计划任务日志
|
||
3. 手动执行测试:`node scripts/auto-unbind-expired-simple.js`
|
||
4. 检查脚本权限:`chmod +x scripts/auto-unbind-expired-simple.js`
|
||
|
||
### Q2: 绑定切换后,旧推荐人还能收到佣金?
|
||
**原因**:可能是购买时的绑定查询逻辑有问题
|
||
|
||
**检查**:
|
||
```sql
|
||
-- 查看 B 当前的绑定
|
||
SELECT * FROM referral_bindings
|
||
WHERE referee_id = 'test_user_b' AND status = 'active';
|
||
|
||
-- 应该只有1条 active 记录(最新的推荐人)
|
||
```
|
||
|
||
### Q3: purchase_count 字段不存在?
|
||
**原因**:数据库迁移未成功
|
||
|
||
**解决**:
|
||
```bash
|
||
# 重新执行迁移
|
||
python3 scripts/migrate_binding_fields.py
|
||
|
||
# 或手动添加
|
||
mysql -u root -p mycontent_db -e "
|
||
ALTER TABLE referral_bindings
|
||
ADD COLUMN purchase_count INT DEFAULT 0;
|
||
"
|
||
```
|
||
|
||
### Q4: 如何验证新逻辑是否生效?
|
||
**验证清单**:
|
||
- [ ] 数据库有 `last_purchase_date`、`purchase_count`、`total_commission` 字段
|
||
- [ ] 点击不同推荐链接会立即切换(无报错)
|
||
- [ ] 购买后 `purchase_count` 会累加
|
||
- [ ] 定时任务能正常执行
|
||
|
||
---
|
||
|
||
## ✅ 部署完成检查表
|
||
|
||
- [ ] 数据库迁移成功
|
||
- [ ] 代码部署完成
|
||
- [ ] PM2 服务正常运行
|
||
- [ ] 定时任务已配置
|
||
- [ ] 测试用例1通过(立即切换)
|
||
- [ ] 测试用例2通过(购买累加)
|
||
- [ ] 日志正常输出
|
||
- [ ] 备份文件已保存
|
||
|
||
---
|
||
|
||
## 📞 问题反馈
|
||
|
||
如有问题,请提供:
|
||
1. 错误日志(PM2日志或定时任务日志)
|
||
2. 数据库状态(相关表的查询结果)
|
||
3. 复现步骤
|
||
|
||
**日志收集命令**:
|
||
```bash
|
||
# PM2日志
|
||
pm2 logs soul --lines 100 > soul_logs.txt
|
||
|
||
# 定时任务日志
|
||
cat /www/wwwroot/soul/logs/auto-unbind.log > auto_unbind.log
|
||
|
||
# 数据库状态
|
||
mysql -u root -p mycontent_db -e "
|
||
SELECT * FROM referral_bindings LIMIT 10;
|
||
SHOW COLUMNS FROM referral_bindings;
|
||
" > db_status.txt
|
||
```
|