Files
soul-yongping/开发文档/8、部署/推广设置功能-完整修复清单.md
2026-02-09 15:09:29 +08:00

10 KiB
Raw Blame History

推广设置功能 - 完整修复清单

修复概述

为了确保后台「推广设置」页面的配置能正确应用到整个分销流程,我们修复了 3 个关键 bug,并创建了新的管理页面。

已完成的修改

1. 创建管理页面入口

文件: app/admin/layout.tsx 修改内容: 在侧边栏菜单中增加「推广设置」入口

{ icon: CreditCard, label: "推广设置", href: "/admin/referral-settings" },

位置: 「交易中心」和「系统设置」之间


2. 创建推广设置页面

文件: app/admin/referral-settings/page.tsx (新建) 功能:

  • 配置「好友优惠」(userDiscount) - 百分比
  • 配置「推广者分成」(distributorShare) - 百分比,带滑块
  • 配置「绑定有效期」(bindingDays) - 天数
  • 配置「最低提现金额」(minWithdrawAmount) - 元
  • 配置「自动提现开关」(enableAutoWithdraw) - 布尔值 (预留)

关键特性:

  • 保存时强制类型转换(确保所有数字字段是 Number 类型)
  • 加载时有默认值保护
  • 保存成功后有详细提示
const safeConfig = {
  distributorShare: Number(config.distributorShare) || 0,
  minWithdrawAmount: Number(config.minWithdrawAmount) || 0,
  bindingDays: Number(config.bindingDays) || 0,
  userDiscount: Number(config.userDiscount) || 0,
  enableAutoWithdraw: Boolean(config.enableAutoWithdraw),
}

3. 修复绑定 API 硬编码问题 ⚠️

文件: app/api/referral/bind/route.ts Bug: 使用硬编码 BINDING_DAYS = 30,不读取配置

修复:

// 修复前
const BINDING_DAYS = 30
const expiryDate = new Date()
expiryDate.setDate(expiryDate.getDate() + BINDING_DAYS)

// 修复后
const DEFAULT_BINDING_DAYS = 30
let bindingDays = DEFAULT_BINDING_DAYS
try {
  const config = await getConfig('referral_config')
  if (config?.bindingDays) {
    bindingDays = Number(config.bindingDays)
  }
} catch (e) {
  console.warn('[Referral Bind] 读取配置失败,使用默认值', DEFAULT_BINDING_DAYS)
}

const expiryDate = new Date()
expiryDate.setDate(expiryDate.getDate() + bindingDays)

影响:

  • 新用户绑定关系的过期时间会使用后台配置的天数
  • 支持动态调整绑定期(如改为 60 天)

4. 修复提现 API 缺少门槛检查 ⚠️

文件: app/api/withdraw/route.ts Bug: 没有检查最低提现门槛,只检查了 amount > 0

修复:

// 导入 getConfig
import { query, getConfig } from '@/lib/db'

// 在 POST 函数中添加检查
let minWithdrawAmount = 10 // 默认值
try {
  const config = await getConfig('referral_config')
  if (config?.minWithdrawAmount) {
    minWithdrawAmount = Number(config.minWithdrawAmount)
  }
} catch (e) {
  console.warn('[Withdraw] 读取配置失败,使用默认值 10 元')
}

// 检查最低提现门槛
if (amount < minWithdrawAmount) {
  return NextResponse.json({ 
    success: false, 
    message: `最低提现金额为 ¥${minWithdrawAmount},当前 ¥${amount}` 
  }, { status: 400 })
}

影响:

  • 用户提现时会校验后台配置的最低门槛
  • 防止低于门槛的提现请求

5. 后端 API 验证

已确认以下 API 正确读取 referral_config

5.1 支付回调 - 佣金计算

文件: app/api/miniprogram/pay/notify/route.ts

let distributorShare = DEFAULT_DISTRIBUTOR_SHARE
const config = await getConfig('referral_config')
if (config?.distributorShare) {
  distributorShare = config.distributorShare / 100 // 90 → 0.9
}
const commission = Math.round(amount * distributorShare * 100) / 100

已验证正确

5.2 推广数据 API

文件: app/api/referral/data/route.ts

let distributorShare = DISTRIBUTOR_SHARE
const config = await getConfig('referral_config')
if (config?.distributorShare) {
  distributorShare = config.distributorShare / 100 // 用于展示
}

已验证正确


配置字段说明

数据库表: system_config

  • config_key: referral_config
  • config_value: JSON 字符串

JSON 结构:

{
  "distributorShare": 90,       // 推广者分成百分比(存 90计算时除以 100
  "minWithdrawAmount": 10,      // 最低提现金额(元)
  "bindingDays": 30,            // 绑定有效期(天)
  "userDiscount": 5,            // 好友优惠百分比(预留字段)
  "enableAutoWithdraw": false   // 自动提现开关(预留字段)
}

注意:

  • distributorShare 在数据库存的是百分比数字(如 90使用时需除以 1000.9
  • 所有字段必须是 数字类型,不能是字符串 "90"

完整业务流程

1. 用户通过推广链接注册

API: /api/referral/bind 读取配置: bindingDays 行为: 创建绑定关系,过期时间 = 当前时间 + bindingDays 天

2. 绑定用户下单支付

API: /api/miniprogram/pay/notify 读取配置: distributorShare 行为: 计算佣金 = 订单金额 × (distributorShare / 100),写入 referral_bindings

3. 推广者查看收益

API: /api/referral/data 读取配置: distributorShare 行为: 展示推广规则卡片,显示当前分成比例

4. 推广者申请提现

API: /api/withdraw 读取配置: minWithdrawAmount 行为:

  • 检查提现金额 >= minWithdrawAmount
  • 创建提现记录

5. 管理员审核提现

API: /api/admin/withdrawals 读取配置: 不需要 行为: 更新提现状态为 completedrejected


测试验证步骤

验证 1: 绑定天数动态生效

  1. 后台设置「绑定有效期」为 60 天,保存
  2. 小程序新用户通过推广链接注册
  3. 数据库查询:
SELECT expiry_date FROM referral_bindings WHERE referee_id = '新用户ID' ORDER BY created_at DESC LIMIT 1;
  1. 预期: expiry_date = 当前时间 + 60 天

验证 2: 佣金比例动态生效

  1. 后台设置「推广者分成」为 85%,保存
  2. 已绑定用户购买 100 元订单
  3. 数据库查询:
SELECT commission FROM referral_bindings WHERE status = 'converted' ORDER BY created_at DESC LIMIT 1;
  1. 预期: commission = 85.00

验证 3: 提现门槛动态生效

  1. 后台设置「最低提现金额」为 50 元,保存
  2. 用户尝试提现 30 元
  3. 预期: 返回错误「最低提现金额为 ¥50当前 ¥30」

部署清单

1. 代码部署

# 本地构建
pnpm build

# 上传到服务器
python devlop.py

# 重启 PM2
pm2 restart soul

2. 数据库检查

确保 system_config 表存在 referral_config 配置:

SELECT * FROM system_config WHERE config_key = 'referral_config';

如果不存在,插入默认配置:

INSERT INTO system_config (config_key, config_value, description) VALUES (
  'referral_config',
  '{"distributorShare":90,"minWithdrawAmount":10,"bindingDays":30,"userDiscount":5,"enableAutoWithdraw":false}',
  '分销 / 推广规则配置'
);

3. 清理缓存

  • 重启 Node.js 服务
  • 清除前端缓存(刷新浏览器 Ctrl+Shift+R
  • 删除微信小程序缓存(开发者工具 -> 清除缓存)

潜在风险

风险 1: 配置读取失败

场景: 数据库连接异常或配置格式错误 保护措施: 所有读取配置的地方都有默认值 fallback

try {
  const config = await getConfig('referral_config')
  if (config?.distributorShare) {
    distributorShare = config.distributorShare / 100
  }
} catch (e) {
  // 使用默认配置 DEFAULT_DISTRIBUTOR_SHARE
}

风险 2: 历史订单佣金

场景: 修改配置后,历史订单的佣金会变吗? 回答: 不会。已结算的佣金存在 referral_bindings 表的 commission 字段,不会因配置修改而变化。只影响 新订单

风险 3: 类型错误

场景: 前端输入框可能返回字符串 "90" 而不是数字 90 保护措施: 管理页面保存时强制类型转换

const safeConfig = {
  distributorShare: Number(config.distributorShare) || 0,
  // ...
}

性能优化建议

当前实现

每次绑定/支付/提现都会查询一次 system_config

优化方案 (可选)

增加 Redis 缓存:

// 伪代码
const cachedConfig = await redis.get('referral_config')
if (cachedConfig) {
  return JSON.parse(cachedConfig)
}

const config = await getConfig('referral_config')
await redis.set('referral_config', JSON.stringify(config), 'EX', 60) // TTL 60s
return config

收益: 减少数据库查询QPS 可提升 10-20 倍 成本: 需要部署 Redis配置变更有最多 60 秒延迟


遗留问题

userDiscount 字段未应用

状态: 已定义, 未应用 说明: userDiscount (好友优惠) 目前只存在配置中,但订单价格计算逻辑中没有实际使用。 影响: 修改这个值 不会 影响实际订单价格 建议: 如需启用,需在订单创建 API 中读取此配置并应用折扣

enableAutoWithdraw 字段未应用

状态: 已定义, 未实现 说明: 自动提现功能需结合定时任务cron job和微信商家转账 API 影响: 修改这个开关 不会 触发任何行为 建议: 后续实现定时任务模块时读取此配置


FAQ

Q1: 修改配置后需要重启服务吗?

A: 不需要。每次请求都会动态读取数据库配置。

Q2: 小程序展示的规则和后台设置不一致?

A: 可能原因:

  1. 小程序缓存未清除 - 重新编译上传小程序
  2. API 未正确读取配置 - 检查 PM2 日志
  3. 前端硬编码了文案 - 检查小程序代码

Q3: 测试环境如何验证?

A: 使用测试数据库,修改配置后用测试账号走完整流程(绑定→下单→提现)

Q4: 如何回滚配置?

A: 执行 SQL

UPDATE system_config
SET config_value = '{"distributorShare":90,"minWithdrawAmount":10,"bindingDays":30,"userDiscount":5,"enableAutoWithdraw":false}'
WHERE config_key = 'referral_config';

总结

3 个 bug 已修复

  1. 绑定 API 读取配置的 bindingDays
  2. 提现 API 检查 minWithdrawAmount
  3. 管理页面强制类型转换

5 个 API 已验证正确

  1. /api/referral/bind - 绑定关系
  2. /api/miniprogram/pay/notify - 佣金计算
  3. /api/referral/data - 推广数据
  4. /api/withdraw - 提现申请
  5. /api/admin/withdrawals - 提现审核

整个分销流程已打通,后台配置会实时生效!