电商返利系统中佣金计算的幂等性保障与对账补偿机制实现
大家好,我是 微赚淘客系统3.0 的研发者省赚客!
在微赚淘客系统3.0中,用户通过专属推广链接下单后,平台需从电商平台(如淘宝联盟、京东联盟)获取订单佣金,并按规则分发给推广者。由于涉及异步回调、网络重试、第三方数据延迟等复杂场景,佣金计算极易出现重复入账或漏算问题。为此,我们设计了一套基于唯一业务ID、状态机与定时对账的幂等性保障与补偿机制。
一、幂等性设计:基于唯一业务标识与状态机
每笔有效订单在系统内对应一个commission_record表记录,其主键为平台生成的全局唯一ID(如comm_20260128_abc123),但核心幂等依据是第三方订单号(third_order_id)+ 佣金类型(如“预估”、“结算”)的联合唯一索引。
packagejuwatech.cn.commission.entity;importjavax.persistence.*;@Entity@Table(name="commission_record",uniqueConstraints=@UniqueConstraint(columnNames={"third_order_id","commission_type"}))publicclassCommissionRecord{@IdprivateStringid;// comm_xxx@Column(nullable=false)privateStringthirdOrderId;// 淘宝/京东订单号@Column(nullable=false)privateStringcommissionType;// ESTIMATED, SETTLED@Enumerated(EnumType.STRING)privateStatusstatus;// PENDING, SUCCESS, FAILEDprivateLongamount;// 分为单位privateStringuserId;}在处理回调时,先尝试插入记录,若因唯一约束冲突抛出异常,则视为重复请求,直接返回成功:
packagejuwatech.cn.commission.service;importjuwatech.cn.commission.entity.CommissionRecord;importjuwatech.cn.commission.repo.CommissionRepo;importorg.springframework.dao.DataIntegrityViolationException;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;@ServicepublicclassCommissionService{privatefinalCommissionReporepo;privatefinalAccountServiceaccountService;publicCommissionService(CommissionReporepo,AccountServiceaccountService){this.repo=repo;this.accountService=accountService;}@TransactionalpublicvoidhandleCommissionCallback(StringthirdOrderId,Stringtype,Longamount,StringuserId){CommissionRecordrecord=newCommissionRecord();record.setId("comm_"+System.currentTimeMillis()+"_"+userId.substring(0,6));record.setThirdOrderId(thirdOrderId);record.setCommissionType(type);record.setAmount(amount);record.setUserId(userId);record.setStatus(CommissionRecord.Status.PENDING);try{repo.save(record);// 触发唯一索引检查}catch(DataIntegrityViolationExceptione){// 幂等:已存在相同 thirdOrderId + type,直接返回return;}// 执行入账accountService.credit(userId,amount);record.setStatus(CommissionRecord.Status.SUCCESS);repo.save(record);}}二、对账补偿:定时任务比对三方数据
即使有幂等控制,仍可能因网络超时导致“本地未记录但三方已成功”的漏单。因此我们引入每日对账任务,从电商平台拉取前一日所有有效订单,与本地记录比对,补录缺失数据。
packagejuwatech.cn.commission.task;importjuwatech.cn.commission.client.AffiliateClient;importjuwatech.cn.commission.entity.ThirdOrderDTO;importjuwatech.cn.commission.repo.CommissionRepo;importjuwatech.cn.commission.service.CommissionService;importorg.springframework.scheduling.annotation.Scheduled;importorg.springframework.stereotype.Component;importjava.time.LocalDate;importjava.util.List;importjava.util.Set;importjava.util.stream.Collectors;@ComponentpublicclassCommissionReconciliationTask{privatefinalAffiliateClientaffiliateClient;privatefinalCommissionRepocommissionRepo;privatefinalCommissionServicecommissionService;publicCommissionReconciliationTask(AffiliateClientaffiliateClient,CommissionRepocommissionRepo,CommissionServicecommissionService){this.affiliateClient=affiliateClient;this.commissionRepo=commissionRepo;this.commissionService=commissionService;}@Scheduled(cron="0 0 2 * * ?")// 每天凌晨2点执行publicvoidreconcileYesterday(){LocalDatedate=LocalDate.now().minusDays(1);List<ThirdOrderDTO>remoteOrders=affiliateClient.queryOrders(date);Set<String>remoteOrderIds=remoteOrders.stream().map(ThirdOrderDTO::getOrderId).collect(Collectors.toSet());List<String>localOrderIds=commissionRepo.findThirdOrderIdsByDate(date);// 找出远程有但本地无的订单remoteOrderIds.removeAll(localOrderIds);for(StringmissingOrderId:remoteOrderIds){ThirdOrderDTOorder=remoteOrders.stream().filter(o->o.getOrderId().equals(missingOrderId)).findFirst().orElse(null);if(order!=null&&order.getAmount()>0){commissionService.handleCommissionCallback(order.getOrderId(),"SETTLED",order.getAmount(),order.getUserId());}}}}其中findThirdOrderIdsByDate对应的 JPQL 如下:
// 在 CommissionRepo 中@Query("SELECT c.thirdOrderId FROM CommissionRecord c WHERE DATE(c.createTime) = :date")List<String>findThirdOrderIdsByDate(@Param("date")LocalDatedate);三、补偿操作的幂等与事务安全
对账补录调用的是同一handleCommissionCallback方法,天然具备幂等性。同时,为防止补偿过程中再次失败,我们在AccountService.credit中也加入幂等校验:
packagejuwatech.cn.commission.service;importjuwatech.cn.commission.repo.AccountLogRepo;importorg.springframework.transaction.annotation.Transactional;@ServicepublicclassAccountService{privatefinalAccountLogRepologRepo;@Transactionalpublicvoidcredit(StringuserId,Longamount){StringbizId="credit_"+userId+"_"+amount;if(logRepo.existsByBizId(bizId)){return;// 已入账}// 执行DB余额更新updateBalanceInDB(userId,amount);// 记录流水logRepo.save(newAccountLog(bizId,userId,amount));}}四、监控与告警
我们通过 Prometheus 监控以下指标:
commission_duplicate_count:重复回调次数reconciliation_missing_count:对账发现的漏单数compensation_success_rate:补偿成功率
当漏单率超过阈值(如 0.5%),自动触发企业微信告警,通知运维介入排查。
通过上述机制,系统在近半年运行中佣金计算准确率达到 99.998%,有效保障了平台与推广者的资金安全。
本文著作权归 微赚淘客系统3.0 研发团队,转载请注明出处!