distribution-model-v2.md 20 KB

分销与积分体系重构设计 v2

从两级分销 → 一级分销(统一40%)+ 间接邀请积分 + 能量师月度分红(资金池竞争制) 状态:v1.0 参数全部锁定,待编码实现


一、设计目标

  1. 简化层级:取消二级佣金(A的下级B再推荐C,A不再拿C的佣金)
  2. 扩大分销主体:所有付费用户(C端/家庭套餐)均可发展下级,系统运营官(tarunner)有每日邀请上限
  3. 能量师团队制:能量师组建团队,享受月度资金池分红(个人池5%+团队池5%,按占比竞争分配)
  4. 合规性:一级分销 + 团队分红(非直推计酬),符合《禁止传销条例》

二、新角色与分销权限

角色 年费 可发展下级 一级佣金 团队分红 数据库标记 每日邀请上限
普通用户 ¥0 vipType=NULL
C端会员 ¥131 直接邀请40% vipType='annual' 默认受限,按需放开
家庭套餐 ¥686 直接邀请40% vipType='family_plan' 默认受限,按需放开
超级会员 ¥1,314 直接邀请40% vipType='super_annual'(暂缓) 默认受限,按需放开
能量师 ¥1,314+ 直接邀请40% vipType='practitioner' 默认开放
系统运营官 直接邀请40% role='tarunner' 按配置

核心规则:

  • 只有付费用户可以发展下级(免费用户不行)
  • 直接邀请佣金 = 购买金额 × 40%,即时结算
  • 间接邀请不结算佣金,仅奖励 10 积分/次
  • 同一用户只能被邀请一次(上级锁定,不可变更)
  • tarunner 默认日限模式,通过 sys_config.allow_tarunner 按需放开上限

三、触发事件与结算规则

3.1 直接邀请佣金(一级)

购买事件 直接邀请人获得 间接邀请人获得 团队分红
购买 C端年费 ¥131 40% 佣金(¥52.40) 10 积分
购买 家庭套餐 ¥686 40% 佣金(¥274.40) 10 积分
购买 超级会员 ¥1,314 40% 佣金(¥525.60) 10 积分
购买 能量师 ¥1,314+ 40% 佣金(¥525.60) 10 积分 ✅(能量师的直接上级拿佣金,能量师自己的团队拿分红)

注意:直接邀请不赠送积分,仅结算佣金。间接邀请不结算佣金,仅奖励 10 积分。团队分红不是多级佣金,而是能量师作为团队长的月度资金池竞争分配(按占池比例)。
更多分红细则见 §4.3。

种子价用户分佣说明:以种子价(¥1,314)购买的能量师同样享受 40% 直接邀请佣金,与标准价购买用户权益一致。

四、佣金与积分结构

4.1 直接邀请佣金(一级分销)

用户 B 被推广者 A 直接邀请注册
│
▼
用户 B 完成任意付费购买
│
▼
推广者 A 获得佣金 = 购买金额 × 40%
推荐人角色 佣金比例 说明
所有付费用户(C端/家庭/能量师) 40% 统一比例,推广任意付费产品均按 40% 结算

直接邀请不赠送积分,仅结算佣金。

4.2 间接邀请积分

推广者 A → 用户 B(直接邀请)
            │
            └→ 用户 C(B 邀请,即 A 的间接邀请)
               │
               ▼ C 完成付费购买
               │
               ▼ A 获得 10 积分(固定,非按比例)
指标 默认值 说明
间接邀请积分 10 每笔间接付费下单奖励,固定值

10 积分为示例值,通过 sys_config.indirect_invite.points 可动态调整。

4.3 能量师月度分红(资金池竞争制)

平台拿出月总收入的 10% 作为分红总池,拆分为个人分红池(5%)和团队分红池(5%),按参与者的销售占比竞争分配。

核心公式

平台月总收入 = 当月所有付费订单实付金额总和(扣除退款)

个人分红池 = 平台月总收入 × 5%
团队分红池 = 平台月总收入 × 5%
总分红池   = 平台月总收入 × 10%

个人分红

个人当月直接销售额    = 该能量师直接下级的所有付费订单总和
所有活跃能量师直接销售总额 = Σ(每个人直接销售额)
个人分红 = (个人直接销售额 / 所有活跃能量师直接销售总额) × 个人分红池

团队分红

个人当月团队销售额 = 该能量师团队的总付费订单(不限层级,逐层递归,
                     遇到当月无直接销售的成员则截断该分支,不计入)
所有能量师团队销售总额 = Σ(每个人团队销售额)
团队分红 = (个人团队销售额 / 所有能量师团队销售总额) × 团队分红池

单人月分红总额

月分红总额 = 个人分红 + 团队分红

示例

平台月总收入:¥100,000
个人分红池:¥5,000  团队分红池:¥5,000

参与分红的活跃能量师共 3 人:

        个人直销额    占个人池       个人分红        团队销售额    占团队池       团队分红
A        ¥20,000       20%          ¥1,000           ¥40,000       20%          ¥1,000
B        ¥50,000       50%          ¥2,500           ¥100,000      50%          ¥2,500
C        ¥30,000       30%          ¥1,500           ¥60,000       30%          ¥1,500
        ────────     ──────       ────────          ────────     ──────       ────────
总计     ¥100,000      100%         ¥5,000 ✅         ¥200,000      100%         ¥5,000 ✅

月月独立判定

当月无直接销售 → 当月不参与任何分红(个人池 + 团队池均不参与)
下月有销售     → 自动恢复,无需申请
无"累积月数"概念,每月独立评判
指标 默认值 说明
个人分红池比例 5% 平台月总收入×5% 作为个人分红竞争池
团队分红池比例 5% 平台月总收入×5% 作为团队分红竞争池
结算周期 每月 每月 1 日结算上月数据
最低发放门槛 ¥100 分红 < ¥100 滚入下月
团队层级 不限层级 递归至当月无销售成员截断该分支
分红数据基数 订单实付金额 扣除退款后的实付金额

4.4 积分抵扣兑换规则(用户侧)

普通用户(非能量师)积分可作为替代分析次数的兑换单位:

兑换项 消耗积分 说明
单人能量盘分析 100 100 积分兑换 1 次分析(等同原免费 1 次配额)
其他商品 按兑换项配置 通过 points_rewards 表维护,后台可增删改

100 积分解耦于原"3 人 × 3 次/天"配额,属独立兑换通道。

4.5 完整示例

场景:推广者 A 发展了 2 个直接下级 B、C

B 通过 A 的推广码购买 C端会员 ¥131
→ A 佣金 = ¥131 × 40% = ¥52.40(即时结算)
→ A 的个人直销额增加 ¥131(计入月度个人分红池竞争)

C 通过 B 的推广码购买 家庭套餐 ¥686(A 的间接邀请)
→ A 佣金 = 0(只有直接邀请才有佣金)
→ A 获得间接积分 = 10 积分

月末结算(A 为能量师):
- 个人当月直接销售额 = ¥131 + ...(A 所有直接下级本月购买)
- 团队当月销售额 = A + B + C + ...(递归,遇无销售截断)
- 个人分红 = (个人直接销售额 / 全部分红池) × 平台总收入 × 5%
- 团队分红 = (团队销售额 / 全部团队池) × 平台总收入 × 5%

五、与旧体系对比

维度 旧体系(两级分销) 新体系(一级+团队分红)
分销层级 两级(一级30% + 二级10%) 一级(直接推荐佣金)
可发展下级 仅能量师 所有付费用户
能量师激励 二级佣金10% 团队分红5%(基于团队业绩)
下级推广动力 强(下级也能拿二级佣金) 弱(下级只能拿一级佣金,和普通付费用户一样)
平台留存 60% 约 65%-80%(取决于配置)
合规定性 两级 = 安全边界 一级 + 团队分红 = 更安全
结算复杂度 中(需追踪两级关系) 低(仅需追踪直接上级)

六、后端设计

6.1 佣金计算逻辑

// CommissionService.java - 核心逻辑

// 1. 一级佣金(简化:只结算给直接上级)
public void settleFirstLevelCommission(Long buyerId, Long productId, int amountFen) {
    User buyer = userRepo.findById(buyerId).orElseThrow();
    Long referrerId = buyer.getReferrerId(); // 直接上级
    
    if (referrerId == null) return; // 无上级,不结算
    
    User referrer = userRepo.findById(referrerId).orElse(null);
    if (referrer == null || !isPaidUser(referrer)) return; // 上级未付费,不结算
    
    // 根据上级角色确定佣金比例
    BigDecimal rate = getFirstLevelRate(referrer.getVipType());
    BigDecimal commission = new BigDecimal(amountFen).multiply(rate);
    
    // 创建佣金记录
    Commission c = new Commission();
    c.setUserId(referrerId);
    c.setAmountFen(commission.intValue());
    c.setLevel(1); // 一级
    c.setSourceUserId(buyerId);
    c.setProductType(productId);
    c.setStatus("available");
    commissionRepo.save(c);
}

// 2. 团队分红(月度批量结算,资金池竞争制)
@Scheduled(cron = "0 0 1 * * ?") // 每月1日 00:00
public void settleMonthlyBonus() {
    // 第一步:计算平台当月总收入(实付金额,扣除退款)
    int totalRevenue = orderRepo.sumMonthlyPaidAmount(LocalDate.now().withDayOfMonth(1));
    int personalPool = totalRevenue * 5 / 100;   // 5% 个人分红池
    int teamPool = totalRevenue * 5 / 100;       // 5% 团队分红池

    // 第二步:查询所有能量师,判定当月资格
    List<User> allPractitioners = userRepo.findByVipType("practitioner");
    
    // 统计每个能量师当月直接销售额
    Map<Long, Integer> personalSales = new HashMap<>();
    for (User p : allPractitioners) {
        int sales = orderRepo.sumDirectMonthlySales(p.getId(), yearMonth);
        personalSales.put(p.getId(), sales);
    }
    
    // 第三步:筛选活跃能量师(当月有直接销售)
    List<Long> activeIds = personalSales.entrySet().stream()
        .filter(e -> e.getValue() > 0)
        .map(e -> e.getKey())
        .collect(Collectors.toList());
    
    // 第四步:计算活跃能量师的团队销售额(递归,遇无销售截断)
    Map<Long, Integer> teamSales = new HashMap<>();
    for (Long id : activeIds) {
        teamSales.put(id, calcTeamSalesRecursive(id, yearMonth));
    }
    
    // 第五步:计算总分母
    int totalPersonalSales = personalSales.entrySet().stream()
        .filter(e -> activeIds.contains(e.getKey()))
        .mapToInt(e -> e.getValue()).sum();
    int totalTeamSales = teamSales.values().stream().mapToInt(Integer::intValue).sum();
    
    // 第六步:对每个活跃能量师计算占比和分红
    for (Long id : activeIds) {
        int pSales = personalSales.get(id);
        int tSales = teamSales.getOrDefault(id, 0);
        
        // 个人分红 = (个人销售额 / 总个人销售额) × 个人分红池
        int personalBonus = totalPersonalSales > 0 
            ? (int)((long)pSales * personalPool / totalPersonalSales) : 0;
        // 团队分红 = (团队销售额 / 总团队销售额) × 团队分红池
        int teamBonus = totalTeamSales > 0 
            ? (int)((long)tSales * teamPool / totalTeamSales) : 0;
        
        if (personalBonus + teamBonus < 10000) continue; // < ¥100 滚入下月
        
        // 写入分红记录
        insertBonusRecord(id, yearMonth, totalRevenue,
            personalPool, teamPool,
            pSales, totalPersonalSales, personalBonus,
            tSales, totalTeamSales, teamBonus);
    }
}

// 递归计算团队销售额:有销售则继续向下,遇无销售截断
private int calcTeamSalesRecursive(Long userId, String yearMonth) {
    int selfSales = orderRepo.sumDirectMonthlySales(userId, yearMonth);
    List<User> subordinates = userRepo.findByReferrerId(userId);
    
    int teamSales = selfSales;
    for (User sub : subordinates) {
        int subSales = orderRepo.sumDirectMonthlySales(sub.getId(), yearMonth);
        if (subSales > 0) {
            teamSales += calcTeamSalesRecursive(sub.getId(), yearMonth);
        }
        // 无销售截断,不继续向下
    }
    return teamSales;
}

6.2 数据库变更

commissions 表字段调整

-- level 字段含义调整:
-- 0 = 团队分红(月度)
-- 1 = 一级佣金
-- 2 = (不再使用,预留)

ALTER TABLE commissions 
  MODIFY COLUMN level INT COMMENT '0=团队分红 1=一级佣金 2=预留';

新增 monthly_bonus_records 月度分红结算表

CREATE TABLE `monthly_bonus_records` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `energy_master_id` BIGINT NOT NULL COMMENT '能量师用户ID',
  `year_month` VARCHAR(7) NOT NULL COMMENT '结算月份(如 2026-06)',

  -- 平台数据
  `platform_revenue_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '平台当月总收入(分)',
  `personal_pool_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '个人分红池总金额(分)',
  `team_pool_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '团队分红池总金额(分)',

  -- 个人销售数据
  `personal_sales_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '个人直接销售金额(分)',
  `total_personal_sales_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '所有活跃能量师直接销售总额(分)',
  `personal_share_pct` INT NOT NULL DEFAULT 0 COMMENT '个人占池百分比(万分比,如 2000=20%)',

  -- 团队销售数据
  `team_sales_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '个人团队销售金额(分)',
  `total_team_sales_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '所有活跃能量师团队销售总额(分)',
  `team_share_pct` INT NOT NULL DEFAULT 0 COMMENT '团队占池百分比(万分比)',

  -- 分红结果
  `personal_bonus_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '个人分红金额(分)',
  `team_bonus_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '团队分红金额(分)',
  `total_bonus_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '总分红金额(分)',

  -- 资格状态
  `qualified` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '当月是否合格(0=不合格 1=合格)',

  -- 结算状态
  `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending/approved/paid/cancelled',
  `settled_at` DATETIME COMMENT '结算处理时间',
  `paid_at` DATETIME COMMENT '实际发放时间',
  `remark` VARCHAR(200) COMMENT '备注',

  UNIQUE KEY `uk_master_month` (`energy_master_id`, `year_month`),
  INDEX `idx_year_month` (`year_month`),
  INDEX `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能量师月度分红结算表';

6.3 sys_config 参数

INSERT INTO sys_config (`key`, `value`, `desc`, `value_type`) VALUES
-- 一级分销佣金(统一比例)
('commission.level1.rate_unified', '4000', '一级佣金统一万分比(4000=40%)', 'percent'),
-- 间接邀请积分
('commission.indirect_invite.points', '10', '间接邀请(二级)每次付费奖励积分(固定值)', 'number'),
-- 分红方案(资金池制)
('bonus.personal.pool_rate', '500', '个人分红池占总收入万分比(500=5%)', 'percent'),
('bonus.team.pool_rate', '500', '团队分红池占总收入万分比(500=5%)', 'percent'),
('bonus.settlement_day', '1', '月度结算日(每月第几天)', 'number'),
('bonus.min_payout', '10000', '最低发放金额(分,¥100),不足滚入下月', 'amount'),
-- 团队规模上限
('commission.team.max_members', '5', '能量师直接下级人数上限(0=不限制)', 'number'),
-- tarunner(系统运营官)每日邀请上限
('tarunner.invite_daily_limit', '50', 'tarunner 每日邀请人数上限(0=不限制)', 'number'),
('system.allow_tarunner', 'false', '是否允许 tarunner 申请/入驻(默认关闭,按需放开)', 'boolean'),
-- 积分系统
('points.checkin.daily', '1', '每日签到积分', 'number'),
('points.consultation.complete', '1', '完成一次咨询积分', 'number'),
('points.redeem.analysis_cost', '100', '积分兑换单人能量盘分析所需积分(等同1次免费配额)', 'number'),
('points.redeem.min', '10', '最低兑换积分门槛', 'number'),
('points.year_expire_enabled', 'true', '是否开启年度过期', 'boolean'),
('points.year_remind_day', '1101', '过期提醒日期(MMDD格式)', 'number');

6.4 推荐关系表

现有 referrerId 单字段即可支撑一级分销,无需额外表。


七、前端改动

7.1 豁免的关系

付费用户(C端/家庭/超级/能量师)均可发展下级,推广码面板不再仅限于能量师。

7.2 能量师额外展示

能量师个人中心增加「我的团队」板块:

┌─────────────────────────┐
│ 我的团队                │
├─────────────────────────┤
│ 直接下级:12人          │
│ 本月团队销售额:¥8,680  │
│ 本月团队分红:¥434.00   │
│ [查看团队列表 →]        │
└─────────────────────────┘

7.3 佣金类型标签

佣金记录增加类型区分:

  • 一级佣金(蓝色标签)
  • 团队分红(金色标签)

八、与现有系统兼容

现有功能 影响 处理方式
commissions level 字段含义变化 迁移脚本更新注释
OrderService.paySuccess() 二级佣金逻辑删除 改为只调用一级佣金结算
UserService.hasPermission() 推广权限从 practitioner 扩展为所有付费用户 修改判断条件
个人中心推广码 能量师专属 → 所有付费用户可见 UI 改为按角色展示
提现系统 不影响 佣金来源变了,提现逻辑不变
团队分红(资金池制) 新功能(替换旧5%单轨) 新增 monthly_bonus_records 表 + 递归团队计算

九、已确认决策快照(v1.0)

决策项 结论 落地位置
佣金比例(一级) 统一40%(所有付费用户) sys_config commission.level1.rate_unified=4000
间接邀请 10积分(无佣金) sys_config commission.indirect_invite.points=10
种子用户 不限创始人码,仅限时间+名额,同样享受40%分佣 phase1-user-stories US-6.x
个人分红池 平台月总收入×5%,按个人直接销售占比竞争分配 sys_config bonus.personal.pool_rate=500
团队分红池 平台月总收入×5%,按团队销售额占比竞争分配 sys_config bonus.team.pool_rate=500
分红淘汰规则 月月独立:当月无销售当月不参与,下月自动恢复 monthly_bonus_records.qualified
最低发放金额 ¥100,不足滚入下月 sys_config bonus.min_payout=10000
团队层级 不限层级,遇当月无销售成员截断该分支 calcTeamSalesRecursive()
团队规模上限 直接下级 5人,之后按负载放开 代码硬上限 + 配置化预留
tarunner 邀请策略 选项A:默认日限,按需放开 sys_config tarunner.invite_daily_limit + system.allow_tarunner
超级会员上线节奏 暂缓,v1.0 不包含 文档标记「暂缓」,不投入开发
升级路径 全部含在当前版本,不分拆 v1.1 实现清单归一到 v1.0
分红数据基数 订单实付金额(扣除退款) orderRepo.sumMonthlyPaidAmount()

十、v1.0 交付后仍开放的议题

  1. tarunner 日限放开阈值(预估流量达到 X 时提升)
  2. 团队 5 人上限解除后的扩容触发条件
  3. 超级会员上线时的权益定价与佣金比例确认
  4. 年度汇算清缴结果回填与用户通知流程(税务)