当前位置:网站首页 >  攻略

构建可扩展的佣金分成系统:从零到一的技术实现指南

时间:2026年06月15日 17:16:03 来源:易频IT社区

一、系统架构设计

佣金分成系统的核心是准确追踪每一笔订单的来源,并按照预设规则实时或定期计算分佣金额。本方案采用事件驱动架构,确保高并发下的数据一致性。

核心数据表结构

以下是MySQL数据库的核心表结构定义,直接执行以下SQL语句创建表:

```sql -- 用户关系表,记录推广关系 CREATE TABLE `user_relation` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `inviter_id` bigint(20) NOT NULL COMMENT '推广人ID', `invitee_id` bigint(20) NOT NULL COMMENT '被邀请人ID', `relation_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '关系类型:1-直接邀请', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_invitee` (`invitee_id`), KEY `idx_inviter` (`inviter_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户邀请关系表'; -- 佣金规则表 CREATE TABLE `commission_rule` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `rule_name` varchar(100) NOT NULL COMMENT '规则名称', `product_type` varchar(50) NOT NULL COMMENT '商品类型', `commission_type` tinyint(4) NOT NULL COMMENT '佣金类型:1-百分比,2-固定金额', `commission_value` decimal(10,2) NOT NULL COMMENT '佣金值', `level_limit` tinyint(4) NOT NULL DEFAULT 1 COMMENT '分成层级限制', `is_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用', `effective_time` datetime NOT NULL COMMENT '生效时间', `expire_time` datetime DEFAULT NULL COMMENT '失效时间', PRIMARY KEY (`id`), KEY `idx_product_time` (`product_type`,`effective_time`,`expire_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='佣金规则表'; -- 订单佣金记录表 CREATE TABLE `order_commission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_no` varchar(64) NOT NULL COMMENT '订单号', `order_amount` decimal(10,2) NOT NULL COMMENT '订单金额', `user_id` bigint(20) NOT NULL COMMENT '下单用户ID', `commission_amount` decimal(10,2) NOT NULL COMMENT '总佣金金额', `commission_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '佣金状态:0-待计算,1-已计算,2-已结算', `calc_time` datetime DEFAULT NULL COMMENT '计算时间', `settle_time` datetime DEFAULT NULL COMMENT '结算时间', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order` (`order_no`), KEY `idx_user_status` (`user_id`,`commission_status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单佣金记录表'; -- 佣金分成明细表 CREATE TABLE `commission_detail` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_commission_id` bigint(20) NOT NULL COMMENT '订单佣金ID', `user_id` bigint(20) NOT NULL COMMENT '受益人ID', `level` tinyint(4) NOT NULL COMMENT '层级:1-一级,2-二级', `commission_rule_id` bigint(20) NOT NULL COMMENT '佣金规则ID', `commission_amount` decimal(10,2) NOT NULL COMMENT '分佣金额', `commission_rate` decimal(5,4) NOT NULL COMMENT '分佣比例', `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '状态:0-待结算,1-已结算', PRIMARY KEY (`id`), KEY `idx_order_commission` (`order_commission_id`), KEY `idx_user` (`user_id`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='佣金分成明细表'; ```

二、环境搭建与依赖配置

2.1 项目初始化

使用Spring Boot 2.7+创建项目,在pom.xml中添加以下依赖:

```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java 8.0.33 org.springframework.kafka spring-kafka org.redisson redisson-spring-boot-starter 3.23.2 ```

2.2 配置文件

在application.yml中配置数据库和消息队列:

```yaml spring: datasource: url: jdbc:mysql://localhost:3306/commission_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true kafka: bootstrap-servers: localhost:9092 consumer: group-id: commission-group auto-offset-reset: earliest key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring.json.trusted.packages: "" redis: address: redis://localhost:6379 ```

三、核心功能实现

3.1 用户关系绑定

构建可扩展的佣金分成系统:从零到一的技术实现指南

在用户注册时,通过邀请码建立推广关系。创建UserRelationService.java:

```java @Service @Slf4j public class UserRelationService { @Autowired private UserRelationRepository userRelationRepository; @Autowired private RedisTemplate redisTemplate; @Transactional public void bindInviteRelation(Long inviteeId, String inviteCode) { // 1. 从Redis获取邀请人ID String inviterIdStr = redisTemplate.opsForValue() .get("invite:code:" + inviteCode); if (StringUtils.isEmpty(inviterIdStr)) { throw new BusinessException("邀请码无效"); } Long inviterId = Long.parseLong(inviterIdStr); // 2. 检查是否已存在绑定关系 Optional existing = userRelationRepository .findByInviteeId(inviteeId); if (existing.isPresent()) { throw new BusinessException("该用户已绑定邀请关系"); } // 3. 创建关系记录 UserRelation relation = new UserRelation(); relation.setInviterId(inviterId); relation.setInviteeId(inviteeId); relation.setRelationType(1); userRelationRepository.save(relation); // 4. 清除缓存 redisTemplate.delete("user:relation:chain:" + inviteeId); } public List getInviterChain(Long userId) { String cacheKey = "user:relation:chain:" + userId; String cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { return JSON.parseArray(cached, Long.class); } List chain = new ArrayList<>(); Long currentId = userId; // 最多查询5级 for (int i = 0; i < 5; i++) { Optional relation = userRelationRepository .findByInviteeId(currentId); if (!relation.isPresent()) { break; } Long inviterId = relation.get().getInviterId(); chain.add(inviterId); currentId = inviterId; } // 缓存24小时 redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(chain), 24, TimeUnit.HOURS); return chain; } } ```

3.2 佣金规则配置

创建佣金规则管理接口,CommissionRuleController.java:

```java @RestController @RequestMapping("/api/commission/rule") public class CommissionRuleController { @Autowired private CommissionRuleRepository ruleRepository; @PostMapping("/create") public ApiResponse createRule(@RequestBody CommissionRuleDTO dto) { CommissionRule rule = new CommissionRule(); rule.setRuleName(dto.getRuleName()); rule.setProductType(dto.getProductType()); rule.setCommissionType(dto.getCommissionType()); rule.setCommissionValue(dto.getCommissionValue()); rule.setLevelLimit(dto.getLevelLimit()); rule.setEffectiveTime(dto.getEffectiveTime()); rule.setExpireTime(dto.getExpireTime()); rule.setIsEnabled(true); ruleRepository.save(rule); // 清除规则缓存 redisTemplate.delete("commission:rule:" + dto.getProductType()); return ApiResponse.success("规则创建成功"); } @GetMapping("/effective") public ApiResponse getEffectiveRules(String productType) { String cacheKey = "commission:rule:" + productType; String cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { return ApiResponse.success(JSON.parseArray(cached, CommissionRule.class)); } LocalDateTime now = LocalDateTime.now(); List rules = ruleRepository .findEffectiveRules(productType, now); // 缓存10分钟 redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(rules), 10, TimeUnit.MINUTES); return ApiResponse.success(rules); } } ```

3.3 订单佣金计算

创建佣金计算服务,OrderCommissionService.java:

```java @Service @Slf4j public class OrderCommissionService { @Autowired private UserRelationService userRelationService; @Autowired private CommissionRuleRepository ruleRepository; @Autowired private OrderCommissionRepository orderCommissionRepository; @Autowired private CommissionDetailRepository detailRepository; @KafkaListener(topics = "order.created") @Transactional public void calculateCommission(String orderMessage) { OrderDTO order = JSON.parseObject(orderMessage, OrderDTO.class); // 1. 创建订单佣金记录 OrderCommission commission = new OrderCommission(); commission.setOrderNo(order.getOrderNo()); commission.setOrderAmount(order.getAmount()); commission.setUserId(order.getUserId()); commission.setCommissionStatus(0); orderCommissionRepository.save(commission); // 2. 获取用户的推广链 List inviterChain = userRelationService .getInviterChain(order.getUserId()); if (inviterChain.isEmpty()) { commission.setCommissionStatus(1); commission.setCalcTime(new Date()); orderCommissionRepository.save(commission); return; } // 3. 获取适用的佣金规则 LocalDateTime now = LocalDateTime.now(); List rules = ruleRepository .findEffectiveRules(order.getProductType(), now); if (rules.isEmpty()) { log.warn("未找到商品类型{}的佣金规则", order.getProductType()); return; } CommissionRule rule = rules.get(0); // 4. 计算各级佣金 BigDecimal totalCommission = BigDecimal.ZERO; int levelLimit = Math.min(rule.getLevelLimit(), inviterChain.size()); for (int i = 0; i < levelLimit; i++) { Long inviterId = inviterChain.get(i); int currentLevel = i + 1; // 计算当前层级佣金比例 BigDecimal levelRate = calculateLevelRate(rule, currentLevel); BigDecimal levelAmount = order.getAmount() .multiply(levelRate) .setScale(2, RoundingMode.HALF_UP); // 创建佣金明细 CommissionDetail detail = new CommissionDetail(); detail.setOrderCommissionId(commission.getId()); detail.setUserId(inviterId); detail.setLevel(currentLevel); detail.setCommissionRuleId(rule.getId()); detail.setCommissionAmount(levelAmount); detail.setCommissionRate(levelRate); detail.setStatus(0); detailRepository.save(detail); totalCommission = totalCommission.add(levelAmount); } // 5. 更新订单佣金记录 commission.setCommissionAmount(totalCommission); commission.setCommissionStatus(1); commission.setCalcTime(new Date()); orderCommissionRepository.save(commission); log.info("订单{}佣金计算完成,总佣金:{}", order.getOrderNo(), totalCommission); } private BigDecimal calculateLevelRate(CommissionRule rule, int level) { // 示例:一级30%,二级10%,三级5% Map rateMap = new HashMap<>(); rateMap.put(1, new BigDecimal("0.30")); rateMap.put(2, new BigDecimal("0.10")); rateMap.put(3, new BigDecimal("0.05")); return rateMap.getOrDefault(level, BigDecimal.ZERO); } } ```

四、佣金结算与对账

4.1 结算任务配置

创建定时结算任务,SettlementService.java:

```java @Service @Slf4j public class SettlementService { @Autowired private CommissionDetailRepository detailRepository; @Scheduled(cron = "0 0 1 ?") // 每天凌晨1点执行 @Transactional public void dailySettlement() { LocalDate settleDate = LocalDate.now().minusDays(1); Date startTime = Date.from(settleDate.atStartOfDay() .atZone(ZoneId.systemDefault()).toInstant()); Date endTime = Date.from(settleDate.plusDays(1).atStartOfDay() .atZone(ZoneId.systemDefault()).toInstant()); // 1. 查询待结算的佣金明细 List details = detailRepository .findPendingSettlement(startTime, endTime); if (details.isEmpty()) { log.info("{}无待结算佣金", settleDate); return; } // 2. 按用户分组 Map> userDetails = details.stream() .collect(Collectors.groupingBy(CommissionDetail::getUserId)); // 3. 批量结算 for (Map.Entry> entry : userDetails.entrySet()) { Long userId = entry.getKey(); List userCommDetails = entry.getValue(); BigDecimal totalAmount = userCommDetails.stream() .map(CommissionDetail::getCommissionAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); // 4. 调用支付接口 boolean paymentSuccess = callPaymentApi(userId, totalAmount, userCommDetails); if (paymentSuccess) { // 5. 更新状态 userCommDetails.forEach(detail -> { detail.setStatus(1); detailRepository.save(detail); }); // 6. 更新订单佣金记录状态 updateOrderCommissionStatus(userCommDetails); } } log.info("{}佣金结算完成,共处理{}条记录", settleDate, details.size()); } private boolean callPaymentApi(Long userId, BigDecimal amount, List details) { // 实际项目中调用支付系统接口 // 这里简化为直接返回成功 return true; } } ```

4.2 对账报表生成

创建对账服务,ReconciliationService.java:

```java @Service public class ReconciliationService { public ReconciliationReport generateDailyReport(LocalDate date) { ReconciliationReport report = new ReconciliationReport(); report.setReportDate(date); // 1. 查询当日所有佣金明细 Date startTime = Date.from(date.atStartOfDay() .atZone(ZoneId.systemDefault()).toInstant()); Date endTime = Date.from(date.plusDays(1).atStartOfDay() .atZone(ZoneId.systemDefault()).toInstant()); List details = detailRepository .findByCreatedAtBetween(startTime, endTime); // 2. 统计汇总数据 BigDecimal totalCommission = details.stream() .map(CommissionDetail::getCommissionAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); long userCount = details.stream() .map(CommissionDetail::getUserId) .distinct() .count(); // 3. 按层级统计 Map levelStats = details.stream() .collect(Collectors.groupingBy( CommissionDetail::getLevel, Collectors.reducing( BigDecimal.ZERO, CommissionDetail::getCommissionAmount, BigDecimal::add ) )); // 4. 按商品类型统计 Map productStats = new HashMap<>(); // 这里需要关联查询商品类型 report.setTotalCommission(totalCommission); report.setUserCount(userCount); report.setLevelStatistics(levelStats); report.setProductStatistics(productStats); report.setDetailCount(details.size()); return report; } } ```

相关推荐

最新

热门

推荐

精选

标签

易频IT社区是综合性互联网IT技术门户网站,专注分享网络技术、服务器运维、网络安全、编程开发、系统架构、云计算、大数据等行业干货,实时更新IT行业资讯、零基础教程、实战案例,为IT从业者、技术爱好者提供专业的学习交流平台。

Copyright © 2021-2026 易频IT社区. All Rights Reserved. 备案号:闽ICP备2023013482号 网站地图