将现有个人级推广/佣金体系重构为家庭级体系,所有二维码改为微信小程序码(wxacode),推广收益归入家庭公共账户,重新设计推广中心前端页面。
所有二维码强制使用 WechatService.generateWxacode(),移除 ZXing 兜底。改造现有点:
| 端点 | 原有方式 | 改造后 |
|---|---|---|
POST /api/common/qrcode |
ZXing 普通二维码 | wxacode,新注册使用 |
POST /api/invite/qrcode |
wxacode + ZXing 降级 | 仅 wxacode,移除降级 |
POST /api/family/invite/generate |
wxacode + ZXing 降级 | 仅 wxacode,移除降级 |
类型A:注册推广码
| 字段 | 值 |
|---|---|
| scene | ref={referralCode} (referralCode = 6位字母数字) |
| page | pages/invite/join |
| 生成端点 | POST /api/common/qrcode → 入参 { type: "register", referralCode: "xxx" } |
| 说明 | 废弃旧版 { content } 入参,改 type 区分场景;wxacode 失败时返回错误信息(用户可重试),不再降级为 ZXing 普通码 |
扫码流程:
用户扫码 → 打开小程序 → App.vue handleScene()
→ 解析 scene 中的 ref=xxx → navigateTo /pages/invite/join?refCode=xxx
→ 新用户注册时将 refCode 转为 referrerId 写入 user.referrerId
→ 结算时 referrerId → familyId → 归属到家庭公共账户
类型B:家庭邀请码
| 字段 | 值 |
|---|---|
| scene | invite={token} (token = UUID,已有) |
| page | pages/invite/join |
| 生成端点 | POST /api/family/invite/generate(已有,不变) |
扫码流程(已有,不变):
用户扫码 → 打开小程序 → App.vue handleScene()
→ 解析 scene 中的 invite=token → navigateTo /pages/invite/join?token=xxx
→ 验证 token → 加入家庭
移除 ZXing 降级后,wxacode 生成失败的策略:
| 失败原因 | 处理方式 |
|---|---|
| access_token 过期 | 自动重试一次(重新获取 token) |
| 频率超限(45009) | 返回错误码提示用户稍后重试,不降级 |
| 其他 API 错误 | 记录日志,返回错误信息 |
| 微信 API 不可达 | 返回错误,提示用户稍后再试 |
前端收到错误后显示「生成二维码失败,请稍后重试」,不自动降级。
@Value("${wechat.env-version:release}")
private String envVersion;
各环境配置:
| 环境 | 值 |
|------|-----|
| dev | development |
| test | trial |
| prod | release |
现有体系(个人级):
用户 referralCode → 推荐新用户 → 佣金记到 user 个人
新体系(家庭级):
用户 referralCode → 推荐新用户 → 佣金记到 family 公共账户
↓
个人贡献记录保留(用于展示谁推荐了多少)
family_earnings(家庭公共账户总账)CREATE TABLE family_earnings (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
family_id BIGINT NOT NULL UNIQUE COMMENT '家庭ID',
total_earned BIGINT NOT NULL DEFAULT 0 COMMENT '累计推荐收入(分)',
available_amount BIGINT NOT NULL DEFAULT 0 COMMENT '当前可支配余额(分)',
withdrawn_amount BIGINT NOT NULL DEFAULT 0 COMMENT '已提现金额(分)',
distributed_amount BIGINT NOT NULL DEFAULT 0 COMMENT '已颁发给成员的金额(分)',
updated_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_family_id (family_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭公共账户';
family_earnings_member(成员已获分配余额)CREATE TABLE family_earnings_member (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
family_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
allocated_amount BIGINT NOT NULL DEFAULT 0 COMMENT '管理员累计分配(分)',
consumed_amount BIGINT NOT NULL DEFAULT 0 COMMENT '已消费金额(分)',
available_amount BIGINT NOT NULL DEFAULT 0 COMMENT '可消费余额 = allocated - consumed(分)',
can_use_family_balance TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否允许使用家庭公共池',
updated_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_family_user (family_id, user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭成员已分配余额';
family_earnings_record(收支流水)CREATE TABLE family_earnings_record (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
family_id BIGINT NOT NULL,
type VARCHAR(20) NOT NULL COMMENT 'earn收入/withdraw提现/distribute颁发/consume消费',
amount BIGINT NOT NULL COMMENT '金额(分)',
from_user_id BIGINT COMMENT '操作人',
to_user_id BIGINT COMMENT '受益人(颁发/消费时填写)',
note VARCHAR(255) COMMENT '备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_family_id (family_id),
INDEX idx_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭收益收支流水';
commission_records 加列ALTER TABLE commission_records ADD COLUMN family_id BIGINT COMMENT '归属家庭ID';
成员推荐新用户 → 新人消费产生订单
↓
CommissionService.settle()
├── 写 commission_records(个人级别,+family_id)
├── family_earnings.total_earned += 佣金
├── family_earnings.available_amount += 佣金
└── family_earnings_record (type=earn)
管理员发起提现(¥100起)
→ 扣 family_earnings.available_amount
→ 写 family_earnings_record (type=withdraw)
→ 写 withdrawal_requests(新增 family_withdraw 类型,关联 family_id)
→ 平台管理员审核 → 打款到管理员支付宝
管理员选择成员 + 输入金额
→ 扣 family_earnings.available_amount
→ 加 family_earnings_member.allocated_amount
→ 重算 family_earnings_member.available_amount
→ 写 family_earnings_record (type=distribute)
管理员切换某成员的 can_use_family_balance 开关
→ UPDATE family_earnings_member SET can_use_family_balance = ?
成员下单结算时,支付选项含「家庭余额」
选择家庭余额 → 按以下顺序扣款:
① family_earnings_member.available_amount(个人分配额度)
↓ 不够才进下一步
② 如果 can_use_family_balance=true 且 family_earnings.available_amount > 0
从 family_earnings.available_amount 扣差额
↓ 还不够
③ 微信支付补差
扣款成功后:
- 写 family_earnings_record (type=consume)
- 更新 family_earnings_member.consumed_amount
- 如果用了公共池,更新 family_earnings.available_amount
推广中心(pages/promotion/index.vue)
├── 头部:家庭总数据卡片
│ ├── 累计推荐人数
│ ├── 家庭总收益(元)
│ └── 可支配余额(元)
│
├── 成员贡献榜
│ ├── 列表:头像 + 昵称 + 推荐人数 + 角色标签
│ └── 当前用户高亮
│
├── 管理员专属面板(仅 isFamilyAdmin 可见)
│ ├── 提现按钮 → 弹窗输入金额
│ ├── 颁发给成员 → 选择成员 + 输入金额
│ └── 成员权限管理 → 开关 can_use_family_balance
│
├── 操作区
│ ├── 生成推广二维码(wxacode)
│ ├── 分享海报
│ └── 复制邀请码
│
├── Tab 导航
│ ├── 推广明细 → 现有 commission.vue 改为显示家庭级
│ ├── 我的团队 → 现有 team.vue 适配家庭模式
│ └── 排行榜 → 现有 leaderboard.vue
│
└── 提现入口 → 现有 withdraw.vue 增加家庭余额提现
| 方法 | 端点 | 说明 |
|---|---|---|
| POST | /api/earnings/family/summary |
家庭收益总览(总推荐数、总收益、可用余额) |
| POST | /api/earnings/family/members |
各成员贡献列表(推荐人数 + 分配余额) |
| POST | /api/earnings/family/records |
家庭收益流水 |
| POST | /api/earnings/admin/distribute |
管理员颁发给成员 |
| POST | /api/earnings/admin/toggle-member |
开关成员的家庭余额权限 |
| POST | /api/earnings/admin/withdraw |
管理员提现(走现有 withdrawal_requests) |
| 端点 | 改动 |
|---|---|
POST /api/common/qrcode |
改为 wxacode,新增 type=register 支持 |
POST /api/invite/summary |
返回家庭总推荐数 + 个人贡献 |
POST /api/commission/summary |
返回家庭级统计数据 |
POST /api/family/invite/generate |
移除 ZXing 降级,仅用 wxacode |
6 个调用 settle() 的服务需同步改造:
| 服务 | 改动 |
|---|---|
AssessmentOrderService |
结算时取买家 familyId 写入 commission_records 并更新 family_earnings |
PaymentService |
同上 |
PackagePaymentService |
同上 |
ProductOrderService |
同上 |
MemberSubscriptionService |
同上 |
MembershipService |
同上 |
pages/invite/join?refCode=xxx 打开
→ 注册页显示推荐人信息(昵称 + 家庭名)
→ 用户完成注册(微信登录 + 授权)
→ 后端:
1. 通过 referralCode 查 user
2. 取 user.familyId
3. 新用户写入 user.referrerId = 推荐人ID
4. 更新推荐人的 directCount
5. 家庭公共账户绑定关系建立
→ 新用户创建自己的家庭(或加入已有家庭)
handleScene(options) {
if (!options || !options.query) return
var scene = decodeURIComponent(options.query.scene || '')
if (scene.indexOf('ref=') === 0) {
var refCode = scene.substring(3)
uni.navigateTo({ url: '/pages/invite/join?refCode=' + refCode })
} else if (scene.indexOf('invite=') === 0) {
var token = scene.substring(7)
uni.navigateTo({ url: '/pages/invite/join?token=' + token })
}
}
// api.js 新增
export const getFamilyEarningsSummary = () =>
request('/api/earnings/family/summary', 'POST')
export const getFamilyEarningsMembers = () =>
request('/api/earnings/family/members', 'POST')
export const getFamilyEarningsRecords = (params) =>
request('/api/earnings/family/records', 'POST', params)
export const distributeToMember = (userId, amount) =>
request('/api/earnings/admin/distribute', 'POST', { userId, amount })
export const toggleMemberBalance = (userId, enabled) =>
request('/api/earnings/admin/toggle-member', 'POST', { userId, enabled })
export const withdrawFamilyEarnings = (amount) =>
request('/api/earnings/admin/withdraw', 'POST', { amount })
promotion_tier(R0-R4)等级体系referral_tree 物化路径表invite_milestone 里程碑奖励| 阶段 | 内容 | 涉及 |
|---|---|---|
| P1 | 二维码改造:全部切 wxacode + 修复 env_version + 新增 ref= 场景处理 |
WechatService, CommonController, App.vue |
| P2 | 家庭公共账户:建表 + CommissionService 改造 + settle 调用点改造 + 注册绑定 referralCode | 3 张新表 + 6 个 order service |
| P3 | 前端重构:推广中心重设计 + 成员贡献榜 + 管理员面板 + 消费支付集成 | 7 个 frontend pages + 支付流程 |
| P4 | 管理后台:家庭收益管理 + 提现审核 | admin panel |
每阶段可独立上线,P1 无业务影响(二维码表现不变),P2/P3 为核心功能,P4 为运营配套。