常见分销系统设计:架构、数据模型与分佣规则

常见分销系统设计:架构、数据模型与分佣规则

1. 系统概述

分销系统是一种基于社交关系的电商营销模式,通过多级分销网络实现商品推广和销售。本文将详细介绍分销系统的技术架构、数据库设计和分佣规则实现。

2. 系统架构设计2.1 整体架构代码语言:javascript复制┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐

│ 客户端层 │ │ API网关 │ │ 业务服务层 │

│ - Web前端 │───▶│ - 路由转发 │───▶│ - 用户服务 │

│ - 移动APP │ │ - 认证鉴权 │ │ - 商品服务 │

│ - 小程序 │ │ - 限流熔断 │ │ - 订单服务 │

└─────────────────┘ └──────────────────┘ └─────────────────┘

┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐

│ 数据存储层 │◀──│ 数据访问层 │◀──│ 分销服务层 │

│ - MySQL │ │ - MyBatis │ │ - 关系管理 │

│ - Redis │ │ - JPA │ │ - 分佣计算 │

│ - Elasticsearch │ │ - Redis Template │ │ - 结算服务 │

└─────────────────┘ └──────────────────┘ └─────────────────┘2.2 核心服务模块代码语言:javascript复制// 分销系统核心服务类图

public class DistributionSystem {

// 用户服务

UserService userService;

// 分销关系服务

DistributionRelationService relationService;

// 分佣服务

CommissionService commissionService;

// 结算服务

SettlementService settlementService;

// 规则引擎

RuleEngine ruleEngine;

}3. 数据库设计3.1 核心表结构用户表 (users)代码语言:javascript复制CREATE TABLE users (

id BIGINT PRIMARY KEY AUTO_INCREMENT,

username VARCHAR(100) NOT NULL UNIQUE,

email VARCHAR(255),

phone VARCHAR(20),

user_type TINYINT NOT NULL COMMENT '1-普通用户 2-分销员',

invite_code VARCHAR(20) NOT NULL UNIQUE COMMENT '邀请码',

parent_id BIGINT COMMENT '上级用户ID',

distribution_level INT DEFAULT 0 COMMENT '分销层级',

status TINYINT DEFAULT 1 COMMENT '状态',

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

INDEX idx_parent_id (parent_id),

INDEX idx_invite_code (invite_code)

);分销关系表 (distribution_relations)代码语言:javascript复制CREATE TABLE distribution_relations (

id BIGINT PRIMARY KEY AUTO_INCREMENT,

user_id BIGINT NOT NULL,

ancestor_id BIGINT NOT NULL COMMENT '祖先节点ID',

level INT NOT NULL COMMENT '关系层级',

commission_rate DECIMAL(5,4) COMMENT '该层级分佣比例',

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

UNIQUE KEY uk_user_ancestor (user_id, ancestor_id),

INDEX idx_user_id (user_id),

INDEX idx_ancestor_id (ancestor_id)

);订单表 (orders)代码语言:javascript复制CREATE TABLE orders (

id BIGINT PRIMARY KEY AUTO_INCREMENT,

order_no VARCHAR(50) NOT NULL UNIQUE,

user_id BIGINT NOT NULL,

total_amount DECIMAL(10,2) NOT NULL COMMENT '订单总金额',

actual_amount DECIMAL(10,2) NOT NULL COMMENT '实际支付金额',

commission_base DECIMAL(10,2) NOT NULL COMMENT '分佣基数',

status TINYINT NOT NULL COMMENT '订单状态',

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

INDEX idx_user_id (user_id),

INDEX idx_created_at (created_at)

);分佣记录表 (commission_records)代码语言:javascript复制CREATE TABLE commission_records (

id BIGINT PRIMARY KEY AUTO_INCREMENT,

order_id BIGINT NOT NULL,

user_id BIGINT NOT NULL COMMENT '获得佣金的用户',

from_user_id BIGINT NOT NULL COMMENT '来源用户',

commission_level INT NOT NULL COMMENT '分佣层级',

commission_rate DECIMAL(5,4) NOT NULL COMMENT '分佣比例',

commission_amount DECIMAL(10,2) NOT NULL COMMENT '分佣金额',

commission_base DECIMAL(10,2) NOT NULL COMMENT '分佣基数',

status TINYINT NOT NULL COMMENT '1-待结算 2-已结算 3-已取消',

settled_at TIMESTAMP NULL COMMENT '结算时间',

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

INDEX idx_user_id (user_id),

INDEX idx_order_id (order_id),

INDEX idx_status (status)

);分佣规则表 (commission_rules)代码语言:javascript复制CREATE TABLE commission_rules (

id BIGINT PRIMARY KEY AUTO_INCREMENT,

rule_name VARCHAR(100) NOT NULL,

rule_type TINYINT NOT NULL COMMENT '1-固定比例 2-阶梯比例 3-固定金额',

level_config JSON NOT NULL COMMENT '层级配置',

product_categories JSON COMMENT '适用商品分类',

status TINYINT DEFAULT 1,

start_time TIMESTAMP NULL,

end_time TIMESTAMP NULL,

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

);4. 核心代码实现4.1 分销关系管理代码语言:javascript复制@Service

@Slf4j

public class DistributionRelationService {

@Autowired

private DistributionRelationMapper relationMapper;

@Autowired

private UserMapper userMapper;

/**

* 建立分销关系

*/

@Transactional

public void buildDistributionRelation(Long userId, String inviteCode) {

// 1. 验证邀请码

User parentUser = userMapper.selectByInviteCode(inviteCode);

if (parentUser == null) {

throw new BusinessException("无效的邀请码");

}

// 2. 防止循环引用

if (userId.equals(parentUser.getId())) {

throw new BusinessException("不能邀请自己");

}

// 3. 更新用户的直接上级

User currentUser = userMapper.selectById(userId);

currentUser.setParentId(parentUser.getId());

userMapper.updateById(currentUser);

// 4. 建立多级关系链

buildRelationChain(userId, parentUser.getId());

}

/**

* 构建完整的关系链

*/

private void buildRelationChain(Long userId, Long parentId) {

// 获取上级的所有祖先关系

List parentRelations =

relationMapper.selectByUserId(parentId);

List newRelations = new ArrayList<>();

// 添加直接上级关系

DistributionRelation directRelation = new DistributionRelation();

directRelation.setUserId(userId);

directRelation.setAncestorId(parentId);

directRelation.setLevel(1);

newRelations.add(directRelation);

// 添加上级的祖先关系

for (DistributionRelation parentRelation : parentRelations) {

DistributionRelation relation = new DistributionRelation();

relation.setUserId(userId);

relation.setAncestorId(parentRelation.getAncestorId());

relation.setLevel(parentRelation.getLevel() + 1);

newRelations.add(relation);

}

// 批量插入关系

relationMapper.batchInsert(newRelations);

// 更新用户层级

User user = userMapper.selectById(userId);

user.setDistributionLevel(getMaxLevel(newRelations));

userMapper.updateById(user);

}

/**

* 获取用户的所有下级

*/

public List getSubordinateUsers(Long userId, Integer maxLevel) {

return relationMapper.selectSubordinateUserIds(userId, maxLevel);

}

}4.2 分佣规则引擎代码语言:javascript复制@Service

public class CommissionRuleEngine {

@Autowired

private CommissionRuleMapper ruleMapper;

/**

* 计算分佣

*/

public List calculateCommission(Order order, Long distributorId) {

// 1. 获取适用的分佣规则

CommissionRule rule = getApplicableRule(order);

// 2. 获取分销关系链

List relations =

relationMapper.selectByUserId(distributorId);

// 3. 根据规则类型计算分佣

List commissionDetails = new ArrayList<>();

switch (rule.getRuleType()) {

case FIXED_RATE:

commissionDetails = calculateFixedRateCommission(order, relations, rule);

break;

case LADDER_RATE:

commissionDetails = calculateLadderRateCommission(order, relations, rule);

break;

case FIXED_AMOUNT:

commissionDetails = calculateFixedAmountCommission(order, relations, rule);

break;

}

return commissionDetails;

}

/**

* 固定比例分佣计算

*/

private List calculateFixedRateCommission(

Order order,

List relations,

CommissionRule rule) {

List details = new ArrayList<>();

JSONObject levelConfig = JSON.parseObject(rule.getLevelConfig());

for (DistributionRelation relation : relations) {

String levelKey = "level_" + relation.getLevel();

if (levelConfig.containsKey(levelKey)) {

BigDecimal rate = levelConfig.getBigDecimal(levelKey);

BigDecimal amount = order.getCommissionBase().multiply(rate);

if (amount.compareTo(BigDecimal.ZERO) > 0) {

CommissionDetail detail = new CommissionDetail();

detail.setUserId(relation.getAncestorId());

detail.setFromUserId(order.getUserId());

detail.setCommissionLevel(relation.getLevel());

detail.setCommissionRate(rate);

detail.setCommissionAmount(amount);

detail.setCommissionBase(order.getCommissionBase());

details.add(detail);

}

}

}

return details;

}

/**

* 阶梯比例分佣计算

*/

private List calculateLadderRateCommission(

Order order,

List relations,

CommissionRule rule) {

List details = new ArrayList<>();

JSONArray ladderConfig = JSON.parseArray(rule.getLevelConfig());

// 按订单金额匹配阶梯

BigDecimal orderAmount = order.getActualAmount();

JSONObject matchedLadder = findMatchedLadder(ladderConfig, orderAmount);

if (matchedLadder != null) {

JSONObject rates = matchedLadder.getJSONObject("rates");

for (DistributionRelation relation : relations) {

String levelKey = "level_" + relation.getLevel();

if (rates.containsKey(levelKey)) {

BigDecimal rate = rates.getBigDecimal(levelKey);

BigDecimal amount = order.getCommissionBase().multiply(rate);

if (amount.compareTo(BigDecimal.ZERO) > 0) {

CommissionDetail detail = buildCommissionDetail(

order, relation, rate, amount);

details.add(detail);

}

}

}

}

return details;

}

private JSONObject findMatchedLadder(JSONArray ladderConfig, BigDecimal orderAmount) {

for (int i = 0; i < ladderConfig.size(); i++) {

JSONObject ladder = ladderConfig.getJSONObject(i);

BigDecimal minAmount = ladder.getBigDecimal("minAmount");

BigDecimal maxAmount = ladder.getBigDecimal("maxAmount");

if (orderAmount.compareTo(minAmount) >= 0 &&

(maxAmount == null || orderAmount.compareTo(maxAmount) < 0)) {

return ladder;

}

}

return null;

}

}4.3 分佣执行服务代码语言:javascript复制@Service

@Slf4j

public class CommissionService {

@Autowired

private CommissionRecordMapper commissionRecordMapper;

@Autowired

private DistributionRelationService relationService;

@Autowired

private CommissionRuleEngine ruleEngine;

/**

* 处理订单分佣

*/

@Transactional

public void processOrderCommission(Order order) {

// 1. 检查订单是否满足分佣条件

if (!shouldProcessCommission(order)) {

return;

}

// 2. 获取分销用户(订单用户)

Long distributorId = order.getUserId();

// 3. 计算分佣详情

List commissionDetails =

ruleEngine.calculateCommission(order, distributorId);

// 4. 保存分佣记录

List records = new ArrayList<>();

for (CommissionDetail detail : commissionDetails) {

CommissionRecord record = buildCommissionRecord(order, detail);

records.add(record);

}

commissionRecordMapper.batchInsert(records);

log.info("订单分佣处理完成,订单ID: {}, 分佣记录数: {}",

order.getId(), records.size());

}

/**

* 判断是否应该处理分佣

*/

private boolean shouldProcessCommission(Order order) {

// 只对已支付的订单进行分佣

if (!OrderStatus.PAID.equals(order.getStatus())) {

return false;

}

// 检查分佣基数是否大于0

if (order.getCommissionBase().compareTo(BigDecimal.ZERO) <= 0) {

return false;

}

// 检查用户是否是分销员

User user = userMapper.selectById(order.getUserId());

return user != null && UserType.DISTRIBUTOR.equals(user.getUserType());

}

/**

* 构建分佣记录

*/

private CommissionRecord buildCommissionRecord(Order order, CommissionDetail detail) {

CommissionRecord record = new CommissionRecord();

record.setOrderId(order.getId());

record.setUserId(detail.getUserId());

record.setFromUserId(detail.getFromUserId());

record.setCommissionLevel(detail.getCommissionLevel());

record.setCommissionRate(detail.getCommissionRate());

record.setCommissionAmount(detail.getCommissionAmount());

record.setCommissionBase(detail.getCommissionBase());

record.setStatus(CommissionStatus.PENDING);

return record;

}

/**

* 结算分佣

*/

@Transactional

public void settleCommissions(List recordIds) {

List records = commissionRecordMapper.selectByIds(recordIds);

for (CommissionRecord record : records) {

if (CommissionStatus.PENDING.equals(record.getStatus())) {

// 更新记录状态

record.setStatus(CommissionStatus.SETTLED);

record.setSettledAt(new Date());

commissionRecordMapper.updateById(record);

// 实际资金操作(调用支付服务)

transferCommission(record);

}

}

}

/**

* 资金转账(简化实现)

*/

private void transferCommission(CommissionRecord record) {

// 调用支付系统API或更新用户余额

log.info("分佣转账: 用户 {} 获得佣金 {}",

record.getUserId(), record.getCommissionAmount());

}

}5. 分佣规则设计5.1 常见分佣模式5.1.1 固定比例模式代码语言:javascript复制{

"rule_type": "FIXED_RATE",

"level_config": {

"level_1": 0.10,

"level_2": 0.05,

"level_3": 0.03

}

}5.1.2 阶梯比例模式代码语言:javascript复制{

"rule_type": "LADDER_RATE",

"level_config": [

{

"minAmount": 0,

"maxAmount": 100,

"rates": {

"level_1": 0.08,

"level_2": 0.04

}

},

{

"minAmount": 100,

"maxAmount": 500,

"rates": {

"level_1": 0.12,

"level_2": 0.06,

"level_3": 0.03

}

}

]

}5.1.3 团队业绩模式代码语言:javascript复制// 基于团队总业绩的分佣

public class TeamPerformanceRule {

private Map performanceRates;

private Map teamRates;

public List calculateTeamCommission(

Order order,

User distributor,

TeamPerformance performance) {

// 计算个人业绩分佣

BigDecimal personalRate = getPersonalRate(performance.getPersonalSales());

BigDecimal personalCommission = order.getCommissionBase().multiply(personalRate);

// 计算团队管理分佣

BigDecimal teamRate = getTeamRate(performance.getTeamSales());

BigDecimal teamCommission = order.getCommissionBase().multiply(teamRate);

// 返回组合分佣

List details = new ArrayList<>();

// ... 构建分佣详情

return details;

}

}5.2 防作弊机制代码语言:javascript复制@Service

public class AntiCheatService {

/**

* 分佣风控检查

*/

public CommissionRiskCheckResult riskCheck(Order order, List details) {

CommissionRiskCheckResult result = new CommissionRiskCheckResult();

// 1. 自买自卖检测

if (isSelfTrading(order, details)) {

result.setHighRisk(true);

result.addRiskItem("自买自卖风险");

}

// 2. 异常关系链检测

if (hasAbnormalRelationChain(order.getUserId())) {

result.setHighRisk(true);

result.addRiskItem("异常关系链");

}

// 3. 高频交易检测

if (isHighFrequencyTrading(order.getUserId())) {

result.setMediumRisk(true);

result.addRiskItem("高频交易");

}

return result;

}

/**

* 自买自卖检测

*/

private boolean isSelfTrading(Order order, List details) {

for (CommissionDetail detail : details) {

if (detail.getUserId().equals(order.getUserId())) {

return true;

}

}

return false;

}

}6. 性能优化方案6.1 数据库优化代码语言:javascript复制-- 添加合适的索引

CREATE INDEX idx_commission_user_status ON commission_records(user_id, status);

CREATE INDEX idx_relations_user_ancestor ON distribution_relations(user_id, ancestor_id);

CREATE INDEX idx_orders_user_created ON orders(user_id, created_at);

-- 分区表(按时间分区)

ALTER TABLE commission_records PARTITION BY RANGE (YEAR(created_at)) (

PARTITION p2023 VALUES LESS THAN (2024),

PARTITION p2024 VALUES LESS THAN (2025)

);6.2 缓存策略代码语言:javascript复制@Service

public class DistributionCacheService {

@Autowired

private RedisTemplate redisTemplate;

private static final String RELATION_CACHE_KEY = "dist:relation:%s";

private static final long RELATION_CACHE_TTL = 3600; // 1小时

/**

* 缓存用户关系链

*/

public void cacheUserRelations(Long userId, List relations) {

String key = String.format(RELATION_CACHE_KEY, userId);

redisTemplate.opsForValue().set(key, relations, RELATION_CACHE_TTL, TimeUnit.SECONDS);

}

/**

* 从缓存获取关系链

*/

public List getCachedUserRelations(Long userId) {

String key = String.format(RELATION_CACHE_KEY, userId);

return (List) redisTemplate.opsForValue().get(key);

}

}7. 总结本文详细介绍了分销系统的整体架构设计、数据库模型、核心代码实现以及分佣规则设计。关键要点包括:

架构清晰:采用分层架构,模块化设计关系管理:使用闭包表模式存储多级分销关系规则灵活:支持多种分佣模式,可配置化风控完善:内置防作弊机制,保障系统安全性能优化:通过缓存、索引等手段提升系统性能实际项目中还需要考虑分布式事务、数据一致性、系统监控等更多细节,但以上设计已经提供了一个坚实的分销系统基础框架。

相关推荐

企业代码查询入口官网:如何快速找到并利用企业代码查询入口
beats耳机真假该怎样鉴别【详细方法】
office365人工客服电话

beats耳机真假该怎样鉴别【详细方法】

🗓️ 08-29 👁️ 1999
久字开头的词语
www.bst365.com

久字开头的词语

🗓️ 10-29 👁️ 1589

友情链接