关于芋道源码分佣系统的详细说明
一、分佣机制概览
1.1 双模式设计
项目分佣机制支持两种模式:
① 基于用户绑定关系的多级分佣
- 原理:通过用户上下级推荐关系进行分佣
- 层级:支持二级分销(一级推广人、二级推广人)
- 资金归属:直接入账至推广用户的个人分佣账户
- 提现能力:支持用户自主提现
② 平台级统一分佣
- 原理:按商品维度配置固定比例或金额
- 计算方式:每笔订单向平台结算分佣
- 资金归属:计入平台专属账户
- 提现能力:不支持提现,仅用于内部财务结算与对账
二、分佣逻辑详解
2.1 分佣触发时机
订单支付成功(TradeBrokerageOrderHandler.afterPayOrder)
↓
查找一级推广人(通过 bindUserId)
↓
计算一级分佣(比例或固定金额)
↓
查找二级推广人(一级推广人的 bindUserId)
↓
计算二级分佣(比例或固定金额)
↓
写入佣金记录(trade_brokerage_record)
↓
更新用户佣金账户(trade_brokerage_user)
2.2 分佣计算基准
关键代码位置:TradeOrderConvert.java 第 267 行
BrokerageAddReqBO bo = new BrokerageAddReqBO()
.setBasePrice(item.getPayPrice()) // ⭐ 基于实付金额计算
重点:
- ✅ 分佣是基于
payPrice(用户实际支付金额)计算的 - ❌ 不是基于商品原价
totalPrice
2.3 分佣计算方式
系统支持两种分佣计算方式(优先级从高到低):
方式一:固定佣金(优先)
- 在商品 SKU 中设置
firstBrokeragePrice、secondBrokeragePrice - 需要商品 SPU 的
subCommissionType = true - 直接使用固定金额,不按比例计算
方式二:比例佣金
- 使用全局配置的
brokerageFirstPercent(一级分佣比例) - 使用全局配置的
brokerageSecondPercent(二级分佣比例) - 佣金金额 = 实付金额 × 分佣比例
计算逻辑(BrokerageRecordServiceImpl.calculatePrice 方法):
int calculatePrice(Integer basePrice, Integer percent, Integer fixedPrice) {
// 1. 优先使用固定佣金
if (fixedPrice != null && fixedPrice > 0) {
return fixedPrice;
}
// 2. 根据比例计算佣金
if (basePrice != null && basePrice > 0 && percent != null && percent > 0) {
return MoneyUtils.calculateRatePriceFloor(basePrice, Double.valueOf(percent));
}
return 0;
}
三、分佣配置详解
3.1 基础配置项(TradeConfigBaseVO)
| 配置项 | 字段名 | 说明 | 示例值 |
|---|---|---|---|
| 是否启用分佣 | brokerageEnabled | 分佣功能总开关 | true |
| 分佣模式 | brokerageEnabledCondition | 1=人人分销,2=指定分销 | 1 |
| 绑定关系模式 | brokerageBindMode | 1=首次绑定,2=注册绑定,3=覆盖绑定 | 1 |
| 一级返佣比例 | brokerageFirstPercent | 0-100% | 5 |
| 二级返佣比例 | brokerageSecondPercent | 0-100% | 3 |
| 佣金冻结天数 | brokerageFrozenDays | 防止恶意退款 | 7 |
| 最低提现金额 | brokerageWithdrawMinPrice | 单位:分 | 1000 |
| 提现手续费比例 | brokerageWithdrawFeePercent | 百分比 | 1 |
| 分销海报图 | brokeragePosterUrls | 推广素材 | ["https://..."] |
| 提现方式 | brokerageWithdrawTypes | 支持多选 | [1,2,5] |
3.2 提现方式枚举(BrokerageWithdrawTypeEnum)
| 编号 | 名称 | 说明 |
|---|---|---|
| 1 | 钱包 | 转入平台钱包(自动) |
| 2 | 银行卡 | 手动打款 |
| 3 | 微信收款码 | 手动打款 |
| 4 | 支付宝收款码 | 手动打款 |
| 5 | 微信零钱 | 自动打款(微信转账API) |
| 6 | 支付宝余额 | 自动打款(支付宝转账API) |
四、资金流转详解
4.1 分佣资金来源
核心结论:分佣资金来源于商品实际成交价,从用户支付的金额中按比例扣除,不属于额外增收。
案例 1:100元商品,无优惠
商品原价:100 元
优惠金额:0 元
实付金额:100 元
一级分佣(5%):100 × 5% = 5 元
二级分佣(3%):100 × 3% = 3 元
商家实际收入:100 - 5 - 3 = 92 元
案例 2:100元商品,用券后80元
商品原价:100 元
优惠券减免:20 元
实付金额:80 元
一级分佣(5%):80 × 5% = 4 元
二级分佣(3%):80 × 3% = 2.4 元
商家实际收入:80 - 4 - 2.4 = 73.6 元
4.2 订单价格组成
价格计算公式(TradePriceCalculateRespBO.Price):
最终支付金额 (payPrice) =
商品原价总额 (totalPrice)
- 活动优惠金额 (discountPrice)
- 优惠券减免金额 (couponPrice)
- 积分抵扣金额 (pointPrice)
- VIP会员减免金额 (vipPrice)
+ 运费金额 (deliveryPrice)
4.3 佣金到账流程
订单支付成功
↓
计算分佣金额
↓
写入 trade_brokerage_record(佣金记录表)
↓
更新 trade_brokerage_user 表的字段:
- brokeragePrice(可用佣金):立即可提现
- frozenPrice(冻结佣金):需等待 N 天解冻
重要说明:
- ❌ 佣金不是直接充值到用户钱包
- ✅ 佣金存储在
trade_brokerage_user表的专属字段 - ✅ 提现时才会打款到实际账户(钱包/银行卡/支付宝等)
4.4 用户提现流程
用户发起提现申请
↓
扣减 brokeragePrice(可用佣金)
↓
创建 trade_brokerage_withdraw(提现申请记录)
↓
后台审核通过
↓
根据提现方式打款:
- 钱包:转入支付钱包(自动)
- 银行卡/微信/支付宝:通过 PayTransferApi 打款(自动/手动)
五、支付手续费与分佣的关系
5.1 手续费计算基数
关键代码位置:PayOrderServiceImpl.java 第 361-362 行
.channelFeeRate(channel.getFeeRate()) // 手续费率(如 0.6%)
.channelFeePrice(MoneyUtils.calculateRatePrice(order.getPrice(), channel.getFeeRate()))
// 手续费金额 = 订单支付金额 × 手续费率
重点结论:
- ✅ 手续费是基于用户实际支付金额(100元)计算
- ❌ 不是基于商家收入(87元)计算
5.2 完整资金流转案例
场景:商品售价 100 元
第一步:平台收到支付款
微信/支付宝收款:100 元
渠道手续费(0.6%):100 × 0.6% = 0.6 元
平台实际到账:99.4 元
第二步:平台系统内部分账
总金额:100 元(逻辑金额)
分佣扣除:
├─ 一级推广人分佣(5%):100 × 5% = 5 元
├─ 二级推广人分佣(3%):100 × 3% = 3 元
└─ 平台分佣(5%):100 × 5% = 5 元
分佣小计:13 元
第三步:商家实际到账
商家收入 = 用户支付 - 渠道手续费 - 分佣总额
= 100 - 0.6 - 13
= 86.4 元
5.3 资金分配明细表
| 金额项 | 计算基数 | 比例/费率 | 金额 | 收款方 |
|---|---|---|---|---|
| 用户支付 | - | - | 100.00 元 | 平台代收 |
| 渠道手续费 | 100 元 | 0.6% | 0.60 元 | 微信/支付宝 |
| 一级分佣 | 100 元 | 5% | 5.00 元 | 推广用户A |
| 二级分佣 | 100 元 | 3% | 3.00 元 | 推广用户B |
| 平台分佣 | 100 元 | 5% | 5.00 元 | 平台账户 |
| 商家到账 | - | - | 86.40 元 | 商家 |
核算:0.6 + 5 + 3 + 5 + 86.4 = 100 ✅
5.4 渠道手续费 vs 平台分佣
| 项目 | 说明 | 收款方 | 计算基数 | 扣款时机 |
|---|---|---|---|---|
| 渠道手续费 | 微信/支付宝等支付通道收取 | 支付渠道商 | 用户支付金额(100元) | 支付时立即扣除 |
| 平台分佣 | 平台系统内部分配 | 推广用户/平台 | 用户支付金额(100元) | 订单支付成功后记账 |
六、商家结算逻辑
6.1 商家实际到账计算公式
商家实际到账 = 用户支付金额 × (1 - 渠道手续费率 - 分佣总比例)
示例:
= 100 × (1 - 0.6% - 13%)
= 100 × 86.4%
= 86.4 元
6.2 商家收款模式
模式一:平台代收代付(推荐)
用户支付 → 平台收款账户 → 定期结算给商家
特点:
- 平台统一收款
- 扣除分佣和手续费后
- 按周期(T+7、T+15)结算给商家
- 通过银行转账或平台钱包
模式二:直连商户(不常见)
用户支付 → 商家微信商户号 → 商家直接收款
特点:
- 商家自己收款
- 平台通过账单向商家收取分佣
- 需要商家开通微信/支付宝商户号
6.3 商家定价建议
目标:成本 50 元,期望利润 50 元
成本:50 元
期望利润:50 元
渠道手续费:0.6%
分佣比例:13%(5% + 3% + 5%)
定价计算:
售价 = (成本 + 利润) / (1 - 手续费率 - 分佣率)
= (50 + 50) / (1 - 0.006 - 0.13)
= 100 / 0.864
≈ 115.74 元
验证:
用户支付:115.74 元
渠道手续费:115.74 × 0.6% = 0.69 元
分佣:115.74 × 13% = 15.05 元
商家实收:115.74 - 0.69 - 15.05 = 100 元
扣除成本:100 - 50 = 50 元利润 ✅
七、平台分佣扩展方案
7.1 方案一:独立统计(推荐)
核心思路
- 平台分佣不走提现流程
- 直接计入财务账户用于统计
实现要点
1. 新增平台佣金统计表
CREATE TABLE `trade_platform_brokerage_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`biz_id` varchar(64) NOT NULL COMMENT '业务编号(订单项ID)',
`biz_type` tinyint NOT NULL COMMENT '业务类型(1=订单)',
`price` int NOT NULL COMMENT '佣金金额(分)',
`title` varchar(255) NOT NULL COMMENT '标题',
`description` varchar(500) COMMENT '说明',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) COMMENT='平台佣金记录表';
2. 配置项扩展
// TradeConfigBaseVO.java 中增加
@Schema(description = "平台分佣比例", example = "5")
@Range(min = 0, max = 100, message = "平台分佣比例必须在 0 - 100 之间")
private Integer platformBrokeragePercent;
@Schema(description = "是否启用平台分佣", example = "true")
private Boolean platformBrokerageEnabled;
3. 分佣计算逻辑
// 1. 平台分佣(不涉及提现)
if (BooleanUtil.isTrue(config.getPlatformBrokerageEnabled())) {
addPlatformBrokerage(list, config.getPlatformBrokeragePercent(), bizType);
}
// 2. 用户分佣(现有逻辑)
if (BooleanUtil.isTrue(config.getBrokerageEnabled())) {
// 一级、二级分佣...
}
应用场景
- 财务统计报表
- 商品利润分析
- 平台收入趋势分析
- 不需要提现,直接计入平台收入
7.2 方案二:虚拟用户模式
核心思路
- 创建虚拟平台用户
- 复用现有分佣和提现逻辑
实现要点
1. 创建虚拟平台用户
-- 在 member_user 表插入虚拟平台用户
INSERT INTO member_user (id, mobile, password, nickname, status)
VALUES (1, '00000000000', 'xxx', '平台账户', 0);
-- 在 trade_brokerage_user 表绑定
INSERT INTO trade_brokerage_user (id, brokerage_enabled, brokerage_price, frozen_price)
VALUES (1, 1, 0, 0);
2. 平台分佣计入虚拟用户
private void addPlatformBrokerage(List<BrokerageAddReqBO> list, Integer percent,
BrokerageRecordBizTypeEnum bizType) {
Long platformUserId = 1L; // 平台虚拟用户ID
BrokerageUserDO platformUser = brokerageUserService.getBrokerageUser(platformUserId);
// 复用现有的分佣逻辑
addBrokerage(platformUser, list, 0, percent, bizType, 0);
}
优缺点
- ✅ 完全复用现有代码
- ✅ 提现流程统一管理
- ⚠️ 虚拟用户可能引起数据混淆
- ⚠️ 需要前端支持平台账户登录
7.3 方案对比
| 维度 | 方案一:独立统计 | 方案二:虚拟用户 |
|---|---|---|
| 开发复杂度 | 中等(新建表和逻辑) | 低(复用现有逻辑) |
| 数据清晰度 | ⭐⭐⭐⭐⭐ 用户/平台分离 | ⭐⭐⭐ 需区分虚拟用户 |
| 提现需求 | ❌ 不支持(仅统计) | ✅ 支持 |
| 财务报表 | ⭐⭐⭐⭐⭐ 独立报表 | ⭐⭐⭐ 需筛选虚拟用户 |
| 扩展性 | ⭐⭐⭐⭐ 可灵活扩展 | ⭐⭐⭐ 受限于用户模型 |
八、数据库表结构
8.1 分佣用户表(trade_brokerage_user)
CREATE TABLE `trade_brokerage_user` (
`id` bigint NOT NULL COMMENT '用户编号(对应 MemberUserDO.id)',
`bind_user_id` bigint COMMENT '推广员编号',
`bind_user_time` datetime COMMENT '推广员绑定时间',
`brokerage_enabled` bit NOT NULL COMMENT '是否有分销资格',
`brokerage_time` datetime COMMENT '成为分销员时间',
`brokerage_price` int NOT NULL DEFAULT 0 COMMENT '可用佣金(单位:分)',
`frozen_price` int NOT NULL DEFAULT 0 COMMENT '冻结佣金(单位:分)',
PRIMARY KEY (`id`)
) COMMENT='分销用户表';
8.2 佣金记录表(trade_brokerage_record)
CREATE TABLE `trade_brokerage_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '用户编号',
`biz_id` varchar(64) NOT NULL COMMENT '业务编号',
`biz_type` tinyint NOT NULL COMMENT '业务类型',
`title` varchar(255) NOT NULL COMMENT '标题',
`price` int NOT NULL COMMENT '金额(单位:分)',
`total_price` int NOT NULL COMMENT '当前总佣金',
`description` varchar(500) COMMENT '说明',
`status` tinyint NOT NULL COMMENT '状态(0=待结算,1=已结算,2=已取消)',
`frozen_days` int NOT NULL COMMENT '冻结时间(天)',
`unfreeze_time` datetime COMMENT '解冻时间',
`source_user_level` int COMMENT '来源用户等级(1=一级,2=二级)',
`source_user_id` bigint COMMENT '来源用户编号',
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) COMMENT='佣金记录表';
8.3 提现申请表(trade_brokerage_withdraw)
CREATE TABLE `trade_brokerage_withdraw` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '用户编号',
`price` int NOT NULL COMMENT '提现金额(单位:分)',
`fee_price` int NOT NULL COMMENT '提现手续费(单位:分)',
`type` tinyint NOT NULL COMMENT '提现方式',
`status` tinyint NOT NULL COMMENT '状态',
`user_account` varchar(64) COMMENT '用户账号',
`user_name` varchar(32) COMMENT '用户姓名',
`audit_reason` varchar(255) COMMENT '审核原因',
`audit_time` datetime COMMENT '审核时间',
`pay_transfer_id` bigint COMMENT '打款订单编号',
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) COMMENT='佣金提现申请表';
九、常见问题解答
Q1: 分佣的钱从哪来?
A: 从用户实际支付的金额中按比例扣除,不是额外增收。例如用户支付 100 元,按 13% 分佣,则 13 元分给推广用户和平台,87 元归商家。
Q2: 使用优惠券后分佣如何计算?
A: 基于用户实际支付金额计算。例如商品 100 元,用券后 80 元,分佣按 80 元计算,不是按 100 元。
Q3: 支付手续费是基于哪个金额计算的?
A: 基于用户实际支付金额(100元),不是基于商家收入(87元)。
Q4: 佣金是直接打入用户钱包吗?
A: 不是。佣金存储在 trade_brokerage_user 表的 brokeragePrice 字段,用户发起提现后才会打款到实际账户。
Q5: 佣金冻结的作用是什么?
A: 防止恶意退款。订单完成后佣金先冻结 N 天(如 7 天),确认无售后问题后才解冻,用户才能提现。
Q6: 平台分佣和用户分佣有什么区别?
A:
- 用户分佣:推广用户可提现,资金需要实际支付
- 平台分佣:计入平台收入,用于财务统计,不需要提现
Q7: 商家如何定价才能保证利润?
A: 使用公式:售价 = (成本 + 利润) / (1 - 手续费率 - 分佣率)
Q8: 分佣比例可以按商品设置吗?
A: 可以。商品 SPU 设置 subCommissionType = true 后,每个 SKU 可以设置独立的 firstBrokeragePrice 和 secondBrokeragePrice,优先使用固定佣金而不是比例。
十、关键代码位置索引
| 功能模块 | 文件路径 | 关键方法/字段 |
|---|---|---|
| 订单分佣处理 | TradeBrokerageOrderHandler.java | afterPayOrder() |
| 佣金计算逻辑 | BrokerageRecordServiceImpl.java | addBrokerage(), calculatePrice() |
| 分佣配置 | TradeConfigBaseVO.java | 分佣相关字段 |
| 分佣用户实体 | BrokerageUserDO.java | brokeragePrice, frozenPrice |
| 提现服务 | BrokerageWithdrawServiceImpl.java | createBrokerageWithdraw() |
| 支付订单 | PayOrderServiceImpl.java | 手续费计算 |
| 价格计算 | TradePriceCalculateRespBO.java | 订单价格结构 |