12 KiB
12 KiB
新分销逻辑 - 部署步骤
📋 部署前检查
确认新逻辑
- ✅ 点击谁的链接,立即绑定谁(无条件切换)
- ✅ 购买时,佣金给当前推荐人
- ✅ 30天内无购买 → 自动解绑
- ✅ 方案A:购买后不重置30天
备份数据
# 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 脚本(推荐)
# 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
# 连接数据库
mysql -u root -p mycontent_db
# 执行迁移SQL
source scripts/migration-add-binding-fields.sql;
# 验证字段
SHOW COLUMNS FROM referral_bindings;
Step 2: 部署代码
本地构建
# 在本地项目目录
cd e:\Gongsi\Mycontent
# 构建
pnpm build
# 确认构建产物
ls -la .next/standalone
上传到服务器
# 使用 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: 重启服务
# 重启 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: 配置定时任务
宝塔面板配置
- 登录宝塔面板
- 进入"计划任务"
- 添加 Shell 脚本任务
任务配置:
- 任务名称:自动解绑过期推荐关系
- 执行周期:每天 02:00
- 脚本内容:
cd /www/wwwroot/soul && node scripts/auto-unbind-expired-simple.js >> /www/wwwroot/soul/logs/auto-unbind.log 2>&1
手动测试定时任务
# 进入项目目录
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:立即切换绑定
准备工作
# 创建测试用户 A、B、C
# A 推荐 B
# C 也想抢 B
测试步骤
# 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:购买分佣(累加)
测试步骤
# 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天自动解绑
模拟测试(修改过期时间)
# 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)
🔍 监控与日志
查看绑定切换日志
# PM2 日志
pm2 logs soul | grep "Referral Bind"
# 查找"立即切换"记录
pm2 logs soul | grep "立即切换"
查看分佣日志
# 查看分佣成功记录
pm2 logs soul | grep "分佣完成"
# 查看累加情况
pm2 logs soul | grep "purchaseCount"
定时任务日志
# 查看定时任务执行记录
cat /www/wwwroot/soul/logs/auto-unbind.log
# 实时监控
tail -f /www/wwwroot/soul/logs/auto-unbind.log
📊 数据统计
查看当前绑定状态分布
SELECT
status,
COUNT(*) as count,
SUM(purchase_count) as total_purchases,
SUM(total_commission) as total_commission
FROM referral_bindings
GROUP BY status;
查看切换频率最高的用户
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天内即将过期的绑定
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. 恢复数据库
# 停止服务
pm2 stop soul
# 恢复备份
mysql -u root -p mycontent_db < backup_before_referral_20260205.sql
# 重启服务
pm2 start soul
2. 恢复代码
# 方式1:Git回滚
cd /www/wwwroot/soul
git reset --hard <上一个commit>
# 方式2:恢复备份
tar -xzf backup_code_20260205.tar.gz
# 重启
pm2 restart soul
3. 停用定时任务
# 宝塔面板 -> 计划任务 -> 停用或删除"自动解绑"任务
📝 常见问题
Q1: 定时任务没有执行?
检查步骤:
- 确认宝塔计划任务状态为"启用"
- 查看宝塔计划任务日志
- 手动执行测试:
node scripts/auto-unbind-expired-simple.js - 检查脚本权限:
chmod +x scripts/auto-unbind-expired-simple.js
Q2: 绑定切换后,旧推荐人还能收到佣金?
原因:可能是购买时的绑定查询逻辑有问题
检查:
-- 查看 B 当前的绑定
SELECT * FROM referral_bindings
WHERE referee_id = 'test_user_b' AND status = 'active';
-- 应该只有1条 active 记录(最新的推荐人)
Q3: purchase_count 字段不存在?
原因:数据库迁移未成功
解决:
# 重新执行迁移
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通过(购买累加)
- 日志正常输出
- 备份文件已保存
📞 问题反馈
如有问题,请提供:
- 错误日志(PM2日志或定时任务日志)
- 数据库状态(相关表的查询结果)
- 复现步骤
日志收集命令:
# 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