2026-09-01-cf-commission-redesign.md 15 KB

CF 值分佣体系重构设计文档

日期:2026-09-01 状态:待评审 分支:cfc 主分支

1. 背景与目标

1.1 现状问题

当前系统存在两套并行的返利/分佣体系:

体系 服务 作用域 用途 状态
新 CF 值分佣 CommissionDistService commission_dist_log / user_platform_balance 个人 商品订单 P点分润 仅商品订单
旧佣金体系 CommissionService.settle/settleTwoLevel commission_records / family_earnings 个人+家庭 套餐/测评/会员/订阅订单 L1/L2 佣金 @Deprecated,仍被调用

问题:

  1. 旧佣金体系 settleTwoLevel 仍被 5 个订单服务调用(套餐/测评/会员/订阅),与需求"全部走 CF 值体系"冲突。
  2. 团队规模统计 PromotionTierService.updateTeamSize() 全库无调用者,等级评估链路断裂。
  3. promotion_tier_config 表列(min_team_size_1st/2nd + commission_rate_l1/l2)与实体 PromotionTierConfigmin_team_size + profit_share_percent + enabled)不一致,等级配置实际未接通。
  4. 用户无法查询自己的团队规模和返佣比例。
  5. CF 值不能成员间转让;优惠券不绑定家庭。

1.2 目标

  1. 全部订单统一走 CF 值分佣,废弃旧佣金体系写入。
  2. 返佣比例由团队规模(不限层级)决定,规模越大比例越高。
  3. 用户可查自己的团队规模与返佣比例。
  4. 家庭成员间可转让 CF 值;CF 值兑换的优惠券绑定家庭共享。

1.3 关键决策(已与需求方确认)

# 决策点 结论
D1 返佣比例模型 纯团队规模阶梯cf_rate_tier 动态可配置)
D2 CF 值归属维度 返给个人钱包user_platform_balance),不进家庭池
D3 会员升级特例 仅 membership/subscription 订单不返当前人,只返推荐人
D4 优惠券绑定 用 CF 兑换的券放进家庭共享池(券绑 family_id,全家可用)
D5 同家庭互推 跳过本人,上溯到第一个非同家庭的引荐人
D6 分润结算主体 个人维度(A/B 引荐人不同则各自独立结算)
D7 团队规模口径 推荐人全层级下线总人数(promotion_tier.total_team_size

2. 数据模型设计

2.1 新建表

cf_rate_tier — 分佣阶梯配置

CREATE TABLE IF NOT EXISTS cf_rate_tier (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    tier_name VARCHAR(50) NOT NULL COMMENT '档位名称: 铜牌/银牌/金牌/铂金/钻石',
    min_team_size INT NOT NULL DEFAULT 0 COMMENT '团队规模下限(含)',
    rate_percent INT NOT NULL DEFAULT 0 COMMENT '返佣比例(%),如10表示10%',
    sort_order INT NOT NULL DEFAULT 0 COMMENT '排序,越大越高',
    enabled TINYINT DEFAULT 1 COMMENT '1启用/0停用',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_min_size (min_team_size)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='CF值返佣阶梯配置';

种子数据(默认 5 档,管理端可动态增删改):

tier_name min_team_size rate_percent
铜牌 0 5
银牌 3 10
金牌 10 15
铂金 30 20
钻石 100 25

个人 CF 钱包 — 复用 user_platform_balance(不新建)

经核对,user_platform_balance 表已具备 family_id 列及完整字段(available/frozen/withdrawn/exchanged/total_earned),PlatformPointsService 即为个人 CF 钱包服务(earn/spend/freeze/unfreeze/withdraw)。直接复用,无需新建 user_cf_wallet 表。

转让时个人钱包变动复用 PlatformPointsService.spend/earn,通过 cf_transfer_record 记录流转明细。

cf_transfer_record — CF 流转记录

CREATE TABLE IF NOT EXISTS cf_transfer_record (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    from_user_id BIGINT COMMENT '转出用户ID(null=平台/家庭池)',
    to_user_id BIGINT COMMENT '转入用户ID',
    family_id BIGINT COMMENT '所属家庭ID',
    amount INT NOT NULL COMMENT 'CF值数量',
    type VARCHAR(16) NOT NULL COMMENT 'transfer成员转让/allocate管理员分配/refund退款回补',
    ref_type VARCHAR(50) COMMENT '关联业务类型',
    ref_id BIGINT COMMENT '关联业务ID',
    remark VARCHAR(255),
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_from (from_user_id),
    INDEX idx_to (to_user_id),
    INDEX idx_family (family_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='CF值流转记录';

2.2 迁移现有表

操作 说明
promotion_tier 删除 team_size_1st/2nd/3rd;新增 rate_percent INT DEFAULT 0 团队规模只留 total_team_size 一个口径
promotion_tier_config 废弃(保留历史数据,业务不再读取) 替代者为 cf_rate_tier
coupon 新增 family_id BIGINT NULL 券绑定家庭(NULL=不绑定)
user_platform_balance 复用,不改表 已有 family_id 列及完整字段,直接作为个人 CF 钱包

2.3 保留不变

  • referral_tree:已支持全层级(path 物化路径)。
  • user_platform_balance / platform_balance_log:个人 CF 值余额与流水(复用为个人钱包)。
  • commission_dist_log:商品订单分润流水(迁移后并入通用分润流水体系)。
  • family_platform_balance / family_platform_exchange_record仅作优惠券兑换用途(用个人 CF 兑换后券挂家庭),不再承接返佣。

3. 分佣核心逻辑

3.1 统一服务 CfCommissionService(新建)

@Service
public class CfCommissionService {
    /** 通用分佣入口(商品/套餐/测评订单:当前人+推荐人双返) */
    void settle(Long orderId, String orderType, Long buyerUserId, Long buyerFamilyId,
                Integer orderAmountCent, Long productId);

    /** 会员/订阅专用(只返推荐人,不返当前人) */
    void settleReferrerOnly(Long orderId, String orderType, Long buyerUserId, Long buyerFamilyId,
                Integer orderAmountCent);
}

3.2 推荐链查询与同家庭上溯

List<ReferralNode> getEffectiveReferrers(Long buyerUserId, Long buyerFamilyId) {
    // 1. 查 referral_tree WHERE child_id=buyerUserId ORDER BY level ASC
    // 2. 对每个 referrer R:
    //    a. R.familyId == buyerFamilyId → 跳过(同家庭不返),查 R 的引荐人继续上溯
    //    b. R.familyId != buyerFamilyId → 加入结果集
    // 3. 上溯终止:无更上级 / 已找到非同家庭引荐人
}

决策 D5:同家庭互推时跳过本人,上溯到第一个非同家庭的引荐人。避免"自家消费返自己家人"的无效内耗与刷单。

3.3 返佣比例计算(纯团队规模阶梯)

int getRatePercent(Long referrerUserId) {
    // 1. 查 promotion_tier.total_team_size(全层级下线人数)
    // 2. SELECT rate_percent FROM cf_rate_tier
    //    WHERE enabled=1 AND min_team_size <= total_team_size
    //    ORDER BY min_team_size DESC LIMIT 1
    // 3. 无匹配 → 0
}

3.4 分润公式

分润CF = floor(orderAmountCent / 100) × 分润基数比例 × rate_percent / 100

其中:
- 商品订单:分润基数比例 = 商品 P点 ppointConfig(如 sourcePpoint 已含此义)
- 非商品订单(套餐/测评/会员/订阅):分润基数比例 = sys_config.commission_service_rate(默认 10%)

简化统一为:

商品:cf = floor(sourcePpoint) × rate_percent / 100   (sourcePpoint 来自 PpointConfigService)
非商品:cf = floor(orderAmountCent / 100) × serviceRateBps / 10000 × rate_percent / 100

3.5 返现路径

  • 当前人返 CF(商品/套餐/测评):platformPointsService.earn(buyerUserId, cf, "order_consume", orderId, ...) → 个人钱包
  • 推荐人分润platformPointsService.earn(referrerUserId, cf, "referral_dist", orderId, ...) → 个人钱包
  • 流水cf_transfer_record 记录 from=平台 to=受益人(type=allocate)

3.6 幂等

(ref_type='referral_dist'|'order_consume', ref_id=orderId) 为幂等键,platform_balance_log 存在即跳过。platformPointsService.earn 当前实现无幂等检查,实现时需补(仿 FamilyPlatformPointsService.earn(ref_type, ref_id) 去重)。

4. 订单结算迁移点

订单类型 文件:行 原调用 新调用 当前人返 CF 推荐人分润
测评 AssessmentOrderService.java:76 commissionService.settle(...) cfCommissionService.settle(...)
套餐 PackagePaymentService.java:220 commissionService.settle(...) cfCommissionService.settle(...)
套餐 PaymentService.java:240 commissionService.settle(...) cfCommissionService.settle(...)
商品 ProductOrderService.java:645 commissionDistService.distribute(...) cfCommissionService.settle(...)
会员 MembershipService.java:784 commissionService.settleTwoLevel(...) cfCommissionService.settleReferrerOnly(...)
订阅 MemberSubscriptionService.java:174 commissionService.settleTwoLevel(...) cfCommissionService.settleReferrerOnly(...)
订阅 MemberSubscriptionService.java:255 commissionService.settleTwoLevel(...) cfCommissionService.settleReferrerOnly(...)
活动 ActivityOrderService

4.1 商品订单处理

商品订单当前 ProductOrderService:633platformPointsService.earn(buyerId, cfValue, "product_order", ...) 返个人钱包,:827 confirmReceivefamilyPlatformPointsService.earn(familyId, ...) 返家庭池。

本次设计决定:

  • :633 个人 CF 返现:保留(属于分佣双返中的"当前人返")。
  • :827 confirmReceive 家庭池返 CF:保留不动(属于"确认收货额外积分",非本次推荐分佣范畴)。若后续需统一口径,另行提任务。

待产品确认项:确认收货返家庭池 CF 是否仍需保留(与"返佣只进个人钱包"是否冲突)。

5. 等级评估触发链路修复

5.1 团队规模写入

推荐绑定:CommissionService.bindReferral(userId, referralCode)
  → 建立 referral_tree 关系(path 物化路径)
  → promotionTierService.updateTeamSize(referrerId, 全链路上溯, +1)
     (对 referrer 及其所有祖先 promotion_tier.total_team_size +1)

5.2 等级/比例刷新

PromotionTierService.refreshRate(referrerUserId)
  → 查 total_team_size → JOIN cf_rate_tier → rate_percent
  → 写回 promotion_tier.rate_percent + tier(档位名)

触发时机:

  1. bindReferral 后(实时)
  2. 每日定时任务 PromotionTierCheckScheduledTask(现有,改调 refreshRate 批量刷新)

5.3 废弃旧逻辑

  • CommissionService.settle/settleTwoLevel → 标 @Deprecated + 保留(历史读取),删除全部调用点。
  • PromotionTierEvalService.evaluateTier(基于 promotion_tier_config)→ 改读 cf_rate_tier
  • PromotionTierConfig 实体 → 不再使用。

6. 查询接口

6.1 用户查询

接口 说明 返回
POST /api/commission/cf/rate 我的团队规模 + 当前返佣比例 { totalTeamSize, ratePercent, tierName }
POST /api/commission/cf/summary 我的 CF 钱包汇总 { available, frozen, totalEarned, totalSpent }
POST /api/commission/cf/list CF 分润/消费流水(分页) Page<PlatformBalanceLog>
POST /api/commission/cf/wallet 个人 CF 余额 PlatformPointsService.getBalance

6.2 CF 转让

接口 说明 约束
POST /api/cf/transfer/send 成员间转让 CF 双方同家庭;转出余额足;type=transfer
POST /api/cf/transfer/list 转让记录(分页) 按 family_id 或 userId 查询

6.3 家庭券库

接口 说明
POST /api/coupon/family/list 查询家庭共享券(user_coupon.family_id=familyId
复用 POST /api/family-platform/exchange-coupon(兑换后券挂 family_id)

6.4 管理端

接口 说明
POST /api/admin/cf-rate-tier/list 阶梯配置列表
POST /api/admin/cf-rate-tier/save 新增/更新档位
POST /api/admin/cf-rate-tier/delete 删除档位

7. 前端改造

7.1 推广中心(pages/promotion/*

  • team.vue:展示全层级团队总人数 + 当前返佣比例 + 档位名(调 /api/commission/cf/rate
  • commission.vue:改用 CF 分润流水(调 /api/commission/cf/list
  • index.vue:头部汇总改用个人 CF 钱包(调 /api/commission/cf/summary

7.2 家庭积分页

  • 新增"成员间转让"入口:调 /api/cf/transfer/send
  • 显示个人 CF 余额与转让记录

7.3 优惠券

  • 兑换后的券进入家庭共享券库;下单可选择家庭券

8. 错误处理与边界

场景 处理
同家庭互推 跳过本人,上溯到非同家庭引荐人(D5)
无有效推荐人 不产生分润,结束
余额不足 spend 抛异常,前端提示"CF值不足"
重复结算 earn 补幂等(ref_type+ref_id 去重)
团队规模未更新 定时任务兜底刷新
孤儿用户(无 family) 分润照常走个人钱包,family_id 冗余为空

9. 测试计划

  • 单元:getRatePercent 阶梯匹配(边界 0/2/3/9/10);同家庭上溯逻辑;幂等防重。
  • 集成:各订单支付成功 → 分润到账;会员订单只返推荐人;转让同家庭校验。
  • E2E:推广中心展示团队规模与比例;成员间转让;家庭券库兑换与使用。

10. 实施顺序(阶段)

  1. 数据库迁移:cf_rate_tier 建表 + 种子;promotion_tier 精简加列;couponfamily_iduser_platform_balancefamily_id
  2. CfCommissionService 核心 + PlatformPointsService.earn 幂等补丁。
  3. 等级评估:updateTeamSize 调用链 + refreshRate + 定时任务改造。
  4. 各订单服务迁移点切换(7 处)。
  5. settle/settleTwoLevel 调用点摘除 + @Deprecated
  6. 查询接口 + CF 转让接口 + 家庭券库接口。
  7. 管理端阶梯配置接口。
  8. 前端改造。
  9. 测试 + 部署。

11. 明确不做(YAGNI)

  • 不重建家庭级分润系统(家庭公共账户 family_earnings 不在本次范围)。
  • 不改 invite_milestone / referral_leaderboard / onboarding 现有逻辑。
  • 不迁移历史 commission_records 数据到 CF 体系(历史只读)。
  • 不做积分商城实物兑换(现有 PointsExchangeProduct 不动)。