使用Gemini2.5 pro 优化我的定时任务(二)
前言
公司最近有一个九维分红的需求,用户体系一共是从普通用户、一维会员、二维会员、三维会员、四维会员、五维会员、六维会员、七维会员、八维会员、九维会员。
用户直推上一个统计周期总佣金/(所有一度会员上一个统计周期直推总佣金+所有二度会员上一个统计周期直推总佣金+所有三度会员上一个统计周期直推总佣金+所有四度会员上一个统计周期直推总佣金+所有五度会员上一个统计周期直推总佣金+所有六度会员上一个统计周期直推总佣金+所有七度会员上一个统计周期直推总佣金+所有八度会员上一个统计周期直推总佣金+所有九度会员上一个统计周期直推总佣金+)】*(平台总佣金*9%)*10000=XXX活跃积分
分红奖励:每月25日结算上个月佣金(统计周期与结算时间自定义)
分红条件:以下两个条件同时满足
1、必须是(一度会员.....九度会员)
2、必须有自购订单(统计周期)
其中, 平台总佣金 = 用户直推上一个统计周期总佣金 + 普通会员总佣金 + 没有自购订单的一~~九度会员的总佣金;
第一版代码
java
import com.alibaba.nacos.api.config.listener.Listener;
import com.alipay.api.domain.SealTabsVO;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Max;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 数字联盟九维分红定时任务
*/
@Component
public class NineDimsDiddCpsJob {
@Resource
private JtkSharingConfigService jtkSharingConfigService;
@Resource
private JtkOrdersService jtkOrdersService;
@Resource
private DtkTbOrderService dtkTbOrderService;
@Resource
private DtkPddOrderService pddOrderService;
private final String WEEK = "WEEK";
private final String MONTH = "MONTH";
/** 活跃积分固定值 */
private final BigDecimal FIXED_ACTIVE = new BigDecimal("10000");
/** FIRST_LEVEL 一维会员 */
private final Long FIRST_LEVEL = 2L;
/** SECOND_LEVEL 二维会员 */
private final Long SECOND_LEVEL = 3L;
/** THIRD_LEVEL 三维会员 */
private final Long THIRD_LEVEL = 4L;
/** FOURTH_LEVEL 四维会员 */
private final Long FOURTH_LEVEL = 5L;
/** FIFTH_LEVEL 五维会员 */
private final Long FIFTH_LEVEL = 6L;
/** SIXTH_LEVEL 六维会员 */
private final Long SIXTH_LEVEL = 7L;
/** SEVENTH_LEVEL 七维会员 */
private final Long SEVENTH_LEVEL = 8L;
/** EIGHTH_LEVEL 八维会员 */
private final Long EIGHTH_LEVEL = 9L;
/** NINTH_LEVEL 九维会员 */
private final Long NINTH_LEVEL = 10L;
@XxlJob("NineDimsDiddCpsJob")
public void execute() {
XxlJobHelper.log("数字联盟九维分红开始执行!");
// 获取配置信息,确定是按照周分配还是月分配
JtkSharingConfigDO jtkSharingConfigData = jtkSharingConfigService.getJtkSharingConfigData();
// 获取配置为空则返回,不进行分配
if (jtkSharingConfigData.getId() == 0) {
XxlJobHelper.log("数字联盟九维分红配置为空!不进行后续分配!");
return;
}
String statisticalPeriod = jtkSharingConfigData.getStatisticalPeriod();
XxlJobHelper.log("数字联盟九维分红配置信息为:" + jtkSharingConfigData);
LocalDateTime startTime;
LocalDateTime endTime;
if (WEEK.equals(statisticalPeriod)) {
// 获取上周周一起始时间到周日的时间
startTime = LocalDateTimeUtils.getLastMondayStart(LocalDateTime.now());
endTime = LocalDateTimeUtils.getLastSundayEnd(LocalDateTime.now());
} else {
// 获取上月起始时间到月末的时间
startTime = LocalDateTimeUtils.beginOfMonth(LocalDateTime.now().minusMonths(1));
endTime = LocalDateTimeUtils.endOfMonth(LocalDateTime.now().minusMonths(1));
}
// 通过时间间隔查询订单数据
List<JtkOrdersDO> jtkOrdersDOS = jtkOrdersService.getJtkOrdersByTimeInterval(startTime, endTime);
List<DtkTbOrderDO> dtkTbOrderDOS = dtkTbOrderService.getDtkOrdersByTimeInterval(startTime, endTime);
List<DtkPddOrderDO> pddOrderDOS = pddOrderService.getPddOrdersByTimeInterval(startTime, endTime);
if (jtkOrdersDOS.isEmpty() && dtkTbOrderDOS.isEmpty() && pddOrderDOS.isEmpty()) {
XxlJobHelper.log("数字联盟所有订单订单数据为空!不进行后续分配!");
}
/** step #1 计算用户直推上一个统计周期总佣金 */
BigDecimal dprTotalCommission = BigDecimal.ZERO;
BigDecimal jtkOrderTotalCommission = BigDecimal.ZERO;
BigDecimal dtkTBTotalCommission = BigDecimal.ZERO;
BigDecimal pddTotalCommission = BigDecimal.ZERO;
boolean isJtkOrderEmpty = jtkOrdersDOS.isEmpty();
boolean isDtkTbOrderEmpty = dtkTbOrderDOS.isEmpty();
boolean isPddOrderEmpty = pddOrderDOS.isEmpty();
/** step ##1.1 计算聚推客总佣金 */
if (!isJtkOrderEmpty) {
// 获取用户直推上一个统计周期总佣金
jtkOrderTotalCommission = jtkOrdersDOS.stream()
// 将流转换为Stream<BigDecimal>, 存储佣金值的流
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
XxlJobHelper.log("数字联盟聚推客订单分配总佣金为:" + jtkOrderTotalCommission);
/** step ##1.2 计算聚推客淘宝订单总佣金 */
if (!isDtkTbOrderEmpty) {
dtkTBTotalCommission = dtkTbOrderDOS.stream()
// 将流转换为Stream<BigDecimal>, 存储佣金值的流
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
XxlJobHelper.log("数字联盟淘宝订单分配总佣金为:" + dtkTBTotalCommission);
/** step ##1.3 获取拼多多订单总佣金 */
if (!isPddOrderEmpty) {
pddTotalCommission = pddOrderDOS.stream()
// 将流转换为Stream<BigDecimal>, 存储佣金值的流
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
XxlJobHelper.log("数字联盟拼多多订单分配总佣金为:" + pddTotalCommission);
/** step ##1.4 获取平台总佣金 */
dprTotalCommission = jtkOrderTotalCommission.add(dtkTBTotalCommission).add(pddTotalCommission);
XxlJobHelper.log("数字联盟待分配订单总佣金为:" + dprTotalCommission);
/** step #2 获取各维度用户直推总佣金 */
/**
* 序数词(表示顺序)
* first(第 1),缩写:1st
* second(第 2),缩写:2nd
* third(第 3),缩写:3rd
* fourth(第 4),缩写:4th
* fifth(第 5),缩写:5th
* sixth(第 6),缩写:6th
* seventh(第 7),缩写:7th
* eighth(第 8),缩写:8th
* ninth(第 9),缩写:9th
*/
/** step ##2.1 获取第一维度用户直推总佣金 */
BigDecimal dprFirstTotalCommission = BigDecimal.ZERO;
/** step ##2.2 获取第二维度用户直推总佣金 */
BigDecimal dprSecondTotalCommission = BigDecimal.ZERO;
/** step ##2.3 获取第三维度用户直推总佣金 */
BigDecimal dprThirdTotalCommission = BigDecimal.ZERO;
/** step ##2.4 获取第四维度用户直推总佣金 */
BigDecimal dprFourthTotalCommission = BigDecimal.ZERO;
/** step ##2.5 获取第五维度用户直推总佣金 */
BigDecimal dprFifthTotalCommission = BigDecimal.ZERO;
/** step ##2.6 获取第六维度用户直推总佣金 */
BigDecimal dprSixthTotalCommission = BigDecimal.ZERO;
/** step ##2.7 获取第七维度用户直推总佣金 */
BigDecimal dprSeventhTotalCommission = BigDecimal.ZERO;
/** step ##2.8 获取第八维度用户直推总佣金 */
BigDecimal dprEighthTotalCommission = BigDecimal.ZERO;
/** step ##2.9 获取第九维度用户直推总佣金 */
BigDecimal dprNinthTotalCommission = BigDecimal.ZERO;
if (!isJtkOrderEmpty) {
// 获取用户直推上一个统计周期总佣金
dprFirstTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(FIRST_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprSecondTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(SECOND_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprThirdTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(THIRD_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprFourthTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(FOURTH_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprFifthTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(FIFTH_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprSixthTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(SIXTH_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprSeventhTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(SEVENTH_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprEighthTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(EIGHTH_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
dprNinthTotalCommission = jtkOrdersDOS.stream()
.filter(jtkOrdersDO -> jtkOrdersDO.getDprLevel().equals(NINTH_LEVEL))
.map(JtkOrdersDO::getJtkShareFee)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
if (!isDtkTbOrderEmpty) {
dprFirstTotalCommission = dprFirstTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(FIRST_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprSecondTotalCommission = dprSecondTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(SECOND_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprThirdTotalCommission = dprThirdTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(THIRD_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprFourthTotalCommission = dprFourthTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(FOURTH_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprFifthTotalCommission = dprFifthTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(FIFTH_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprSixthTotalCommission = dprSixthTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(SIXTH_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprSeventhTotalCommission = dprSeventhTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(SEVENTH_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprEighthTotalCommission = dprEighthTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(EIGHTH_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprNinthTotalCommission = dprNinthTotalCommission.add(dtkTbOrderDOS.stream()
.filter(dtkTbOrderDO -> dtkTbOrderDO.getDprLevel().equals(NINTH_LEVEL))
.map(DtkTbOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
}
if (!isPddOrderEmpty) {
dprFirstTotalCommission = dprFirstTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(FIRST_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprSecondTotalCommission = dprSecondTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(SECOND_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprThirdTotalCommission = dprThirdTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(THIRD_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprFourthTotalCommission = dprFourthTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(FOURTH_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprFifthTotalCommission = dprFifthTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(FIFTH_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprSixthTotalCommission = dprSixthTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(SIXTH_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprSeventhTotalCommission = dprSeventhTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(SEVENTH_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprEighthTotalCommission = dprEighthTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(EIGHTH_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dprNinthTotalCommission = dprNinthTotalCommission.add(pddOrderDOS.stream()
.filter(pddOrderDO -> pddOrderDO.getDprLevel().equals(NINTH_LEVEL))
.map(DtkPddOrderDO::getPromotionAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
}
XxlJobHelper.log("数字联盟聚推客订单分配第一维度总佣金为:" + dprFirstTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第二维度总佣金为:" + dprSecondTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第三维度总佣金为:" + dprThirdTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第四维度总佣金为:" + dprFourthTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第五维度总佣金为:" + dprFifthTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第六维度总佣金为:" + dprSixthTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第七维度总佣金为:" + dprSeventhTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第八维度总佣金为:" + dprEighthTotalCommission);
XxlJobHelper.log("数字联盟聚推客订单分配第九维度总佣金为:" + dprNinthTotalCommission);
/** step #3 获取各维度用户信息 */
/** step ##3.1 一维用户的所有信息 */
Set<Long> firstDprUids = extractSpecialDprUids(jtkOrdersDOS, FIRST_LEVEL);
firstDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, FIRST_LEVEL));
firstDprUids.addAll(extractSpecialDprUids(pddOrderDOS, FIRST_LEVEL));
XxlJobHelper.log("获取到一维用户总数为:" + firstDprUids.size());
/** step ##3.2 二维用户的所有信息 */
Set<Long> secondDprUids = extractSpecialDprUids(jtkOrdersDOS, SECOND_LEVEL);
secondDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, SECOND_LEVEL));
secondDprUids.addAll(extractSpecialDprUids(pddOrderDOS, SECOND_LEVEL));
XxlJobHelper.log("获取到二维用户总数为:" + secondDprUids.size());
/** step ##3.3 三维用户的所有信息 */
Set<Long> thirdDprUids = extractSpecialDprUids(jtkOrdersDOS, THIRD_LEVEL);
thirdDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, THIRD_LEVEL));
thirdDprUids.addAll(extractSpecialDprUids(pddOrderDOS, THIRD_LEVEL));
XxlJobHelper.log("获取到三维用户总数为:" + thirdDprUids.size());
/** step ##3.4 四维用户的所有信息 */
Set<Long> fourthDprUids = extractSpecialDprUids(jtkOrdersDOS, FOURTH_LEVEL);
fourthDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, FOURTH_LEVEL));
fourthDprUids.addAll(extractSpecialDprUids(pddOrderDOS, FOURTH_LEVEL));
XxlJobHelper.log("获取到四维用户总数为:" + fourthDprUids.size());
/** step ##3.5 五维用户的所有信息 */
Set<Long> fifthDprUids = extractSpecialDprUids(jtkOrdersDOS, FIFTH_LEVEL);
fifthDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, FIFTH_LEVEL));
fifthDprUids.addAll(extractSpecialDprUids(pddOrderDOS, FIFTH_LEVEL));
XxlJobHelper.log("获取到五维用户总数为:" + fifthDprUids.size());
/** step ##3.6 六维用户的所有信息 */
Set<Long> sixthDprUids = extractSpecialDprUids(jtkOrdersDOS, SIXTH_LEVEL);
sixthDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, SIXTH_LEVEL));
sixthDprUids.addAll(extractSpecialDprUids(pddOrderDOS, SIXTH_LEVEL));
XxlJobHelper.log("获取到六维用户总数为:" + sixthDprUids.size());
/** step ##3.7 七维用户的所有信息 */
Set<Long> seventhDprUids = extractSpecialDprUids(jtkOrdersDOS, SEVENTH_LEVEL);
seventhDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, SEVENTH_LEVEL));
seventhDprUids.addAll(extractSpecialDprUids(pddOrderDOS, SEVENTH_LEVEL));
XxlJobHelper.log("获取到七维用户总数为:" + seventhDprUids.size());
/** step ##3.8 八维用户所有的信息 */
Set<Long> eighthDprUids = extractSpecialDprUids(jtkOrdersDOS, EIGHTH_LEVEL);
eighthDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, EIGHTH_LEVEL));
eighthDprUids.addAll(extractSpecialDprUids(pddOrderDOS, EIGHTH_LEVEL));
XxlJobHelper.log("获取到八维用户总数为:" + eighthDprUids.size());
/** step ##3.9 九维用户所有的信息 */
Set<Long> ninthDprUids = extractSpecialDprUids(jtkOrdersDOS, NINTH_LEVEL);
ninthDprUids.addAll(extractSpecialDprUids(dtkTbOrderDOS, NINTH_LEVEL));
ninthDprUids.addAll(extractSpecialDprUids(pddOrderDOS, NINTH_LEVEL));
XxlJobHelper.log("获取到九维用户总数为:" + ninthDprUids.size());
/** step #4 计算各维度分配金额 */
// 计算1-9维用户所有的直推金额
BigDecimal dprAllLevelTotalCommission = dprFirstTotalCommission.add(dprSecondTotalCommission).add(dprThirdTotalCommission).add(dprFourthTotalCommission)
.add(dprFifthTotalCommission).add(dprSixthTotalCommission).add(dprSeventhTotalCommission)
.add(dprEighthTotalCommission).add(dprNinthTotalCommission);
/** step ##4.1 计算一维到九维用户分配金额 */
BigDecimal firstLevelTotalAllocate = dprTotalCommission.divide(dprAllLevelTotalCommission, 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(FIRST_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.2 计算二维到九维用户分配金额 */
BigDecimal secondLevelTotalAllocate = dprTotalCommission.divide(dprAllLevelTotalCommission.subtract(dprFirstTotalCommission), 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(SECOND_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.3 获取三维到九维用户分配金额 */
BigDecimal thirdLevelTotalAllocate = dprTotalCommission.divide(dprAllLevelTotalCommission.subtract(dprFirstTotalCommission.add(dprSecondTotalCommission)), 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(THIRD_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.4 获取四级到九维用户分配金额 */
BigDecimal fourthLevelTotalAllocate = dprTotalCommission.divide(dprAllLevelTotalCommission.subtract(dprFirstTotalCommission.add(dprSecondTotalCommission).add(dprThirdTotalCommission)), 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(FOURTH_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.5 获取五级到九维用户分配金额 */
BigDecimal fifthLevelTotalAllocate = dprTotalCommission.divide(dprAllLevelTotalCommission.subtract(dprFirstTotalCommission.add(dprSecondTotalCommission).add(dprThirdTotalCommission).add(dprFourthTotalCommission)), 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(FIFTH_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.6 获取六级到九维用户分配金额 */
BigDecimal sixthLevelTotalAllocate = dprTotalCommission.divide(dprAllLevelTotalCommission.subtract(dprFirstTotalCommission.add(dprSecondTotalCommission).add(dprThirdTotalCommission).add(dprFourthTotalCommission)).add(dprFifthTotalCommission), 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(SIXTH_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.7 获取七级到九维用户分配金额 */
BigDecimal seventhLevelTotalAllocate = dprTotalCommission.divide(dprSeventhTotalCommission.add(dprEighthTotalCommission).add(dprNinthTotalCommission), 8, RoundingMode.DOWN)
.multiply(jtkSharingConfigData.getDividendProp().get(SEVENTH_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.8 获取八维到九维用户分配金额 */
BigDecimal eightLevelTotalAllocate = dprTotalCommission.divide(dprEighthTotalCommission.add(dprNinthTotalCommission))
.multiply(jtkSharingConfigData.getDividendProp().get(EIGHTH_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##4.9 获取九维用户分配金额 */
BigDecimal ninthLevelTotalAllocate = dprTotalCommission.divide(dprNinthTotalCommission)
.multiply(jtkSharingConfigData.getDividendProp().get(NINTH_LEVEL))
.multiply(FIXED_ACTIVE);
/** step ##5 获取用户平均分配金额 */
/** step ##5.1 获取一维用户分配金额平均值 */
BigDecimal firstLevelAllocateAvg = firstLevelTotalAllocate.divide(BigDecimal.valueOf(firstDprUids.size()), 8, RoundingMode.DOWN);
/** step ###5.3 获取二维用户分配金额平均值 */
BigDecimal secondLevelAllocateAvg = secondLevelTotalAllocate.divide(BigDecimal.valueOf(secondDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.4 获取三维用户分配金额平均值 */
BigDecimal thirdLevelAllocateAvg = thirdLevelTotalAllocate.divide(BigDecimal.valueOf(thirdDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.5 获取四级用户分配金额平均值 */
BigDecimal fourthLevelAllocateAvg = fourthLevelTotalAllocate.divide(BigDecimal.valueOf(fourthDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.6 获取五级用户分配金额平均值 */
BigDecimal fifthLevelAllocateAvg = fifthLevelTotalAllocate.divide(BigDecimal.valueOf(fifthDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.7 获取六级用户分配金额平均值 */
BigDecimal sixthLevelAllocateAvg = sixthLevelTotalAllocate.divide(BigDecimal.valueOf(sixthDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.8 获取七级用户分配金额平均值 */
BigDecimal seventhLevelAllocateAvg = seventhLevelTotalAllocate.divide(BigDecimal.valueOf(seventhDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.9 获取八级用户分配金额平均值 */
BigDecimal eightLevelAllocateAvg = eightLevelTotalAllocate.divide(BigDecimal.valueOf(eighthDprUids.size()), 8, RoundingMode.DOWN);
/** step ##5.10 获取九级用户分配金额平均值 */
BigDecimal ninthLevelAllocateAvg = ninthLevelTotalAllocate.divide(BigDecimal.valueOf(ninthDprUids.size()), 8, RoundingMode.DOWN);
/** step ##6 执行各维度用户分配逻辑 */
}
private static Set<Long> extractSpecialDprUids(List<? extends OrderCommonData> orders, Long levelId) {
// 1. 健壮性检查
if (orders == null || orders.isEmpty()) {
return Set.of();
}
// 2. [预处理] 高效提取所有非空sid
Set<String> existingSids = orders.stream()
.map(OrderCommonData::getSid) // 使用接口方法
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (existingSids.isEmpty()) {
return Set.of();
}
// 3. [主处理] 使用Stream流处理,所有逻辑都通过接口方法调用
return orders.stream()
.filter(order -> Objects.equals(order.getDprLevel(), levelId))
.filter(order -> order.getDprUid() != null)
.filter(order -> existingSids.contains(String.valueOf(order.getDprUid())))
.map(OrderCommonData::getDprUid)
.collect(Collectors.toSet());
}
}
Gemini建议
代码功能上是完整的,但存在一些可以显著优化的方面,主要集中在:
- 代码冗余和重复:大量逻辑(特别是佣金计算和用户ID提取)在9个维度之间重复,导致代码冗长且难以维护。
- 性能效率:对订单列表进行了多次全量遍历(stream()),在订单数量巨大时会造成不必要的性能开销。
- 可读性和可维护性:大量的独立变量(dprFirstTotalCommission, firstDprUids 等)使得逻辑跟踪困难。如果未来需要增加或修改维度(例如增加"十维"),需要修改的地方非常多。
- 健壮性:代码中存在多处除法运算(divide()),但没有处理除数为零的边界情况,可能导致 ArithmeticException 异常。同时,从配置Map中获取分红比例时,也没有检查key是否存在。
详细分析与优化建议
1. 问题:代码冗余与性能低下 (核心问题)
现状分析: 代码在 step #2 和 step #3 中,为了计算每个维度的总佣金和用户ID集合,对三个订单列表(jtkOrdersDOS, dtkTbOrderDOS, pddOrderDOS)进行了 9 * 3 = 27 次stream操作。这是一个非常大的性能瓶颈和代码冗余点。
优化策略 : 采用 "一次遍历,全面聚合" 的思想。
- 数据归一化:由于三种订单 DO 都实现了 OrderCommonData 接口,我们可以将它们合并成一个 List。
- 单次聚合 :只遍历这个合并后的列表一次,使用Java Stream的 Collectors.groupingBy,按 dprLevel(维度ID)进行分组。在分组的同时,使用下游收集器(Collectors.teeing 或自定义收集器)一次性完成两件事:
- 计算该维度的总佣金。
- 收集该维度的所有dprUid。
这样,我们只需要遍历一次订单数据,就能得到所有维度所需的核心信息。
2. 问题:可读性与可维护性差
现状分析:
- 定义了从 FIRST_LEVEL 到 NINTH_LEVEL 9个 Long 型常量。
- 定义了 dprFirstTotalCommission 到 dprNinthTotalCommission 9个 BigDecimal 变量。
- 定义了 firstDprUids 到 ninthDprUids 9个 Set 变量。
- 后续的计算逻辑(step #4, step #5)也是逐个对这些变量进行硬编码操作。
优化策略: 使用数据结构来管理维度数据,而不是独立的变量。
- 使用 Map :
- Map<Long, BigDecimal> commissionByLevel:键是维度ID,值是该维度的总佣金。
- Map<Long, Set> uidsByLevel:键是维度ID,值是该维度的用户ID集合。
- 这样,后续的计算可以通过遍历Map来完成,代码更简洁、更具扩展性。例如,增加一个维度只需要在配置和常量定义中增加,计算逻辑无需改动。
3. 问题:健壮性不足
现状分析:
- firstLevelTotalAllocate.divide(BigDecimal.valueOf(firstDprUids.size()), ...):如果 firstDprUids 为空,size() 为0,将抛出 ArithmeticException。
- dprTotalCommission.divide(dprAllLevelTotalCommission, ...):如果总佣金为0,也会抛出异常。
- jtkSharingConfigData.getDividendProp().get(FIRST_LEVEL):如果配置的Map中缺少某个维度的比例,会返回 null,导致后续 multiply 操作时抛出 NullPointerException。
优化策略: 在所有除法和Map取值操作前,进行严格的非空和非零检查。
4. 问题:复杂的计算逻辑
现状分析: 在 step #4 中,计算分红池的公式非常复杂,例如: dprAllLevelTotalCommission.subtract(dprFirstTotalCommission.add(dprSecondTotalCommission)) 这种链式减法难以阅读,且容易出错。
优化策略 : 可以预先计算一个 "累积佣金" 的Map。
- Map<Long, BigDecimal> cumulativeCommissionByLevel: 键是维度ID,值是 从该维度到最高维度 的总佣金之和。
- 这样,计算分母时,可以直接从这个Map中获取,使公式更清晰。
优化版代码亮点总结
- 高效聚合:使用 Stream.of().flatMap() 合并数据源,再通过 Collectors.groupingBy 和 Collectors.teeing 一次性完成所有维度的数据聚合,将几十次遍历优化为一次。
- 结构清晰:用 Map<Long, LevelAggregate> 等数据结构替代了大量离散的变量,代码逻辑更集中,易于理解。
- 高可维护性:维度信息由 List LEVELS 管理。未来增加"十维"时,只需修改此列表和相关配置即可,计算逻辑无需改动,遵循了"开闭原则"。
- 健壮性增强:在所有除法和Map取值操作前都增加了校验,避免了 ArithmeticException 和 NullPointerException,并打印了明确的日志。
- 逻辑简化:通过预计算累积佣金 (cumulativeCommission),简化了分红池的计算公式,使其更易读。
- 代码复用:将核心计算逻辑(计算分红池、计算人均)封装成独立的私有方法,主流程 execute 方法更加清晰。
- 辅助数据类:使用内部类 LevelAggregate 来承载聚合结果,使得数据传递和处理更加规范。
优化代码
java
import com.alipay.api.domain.SealTabsVO;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Max;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 数字联盟九维分红定时任务
*/
@Component
public class NineDimsDiddCpsJob {
@Resource
private JtkSharingConfigService jtkSharingConfigService;
@Resource
private JtkOrdersService jtkOrdersService;
@Resource
private DtkTbOrderService dtkTbOrderService;
@Resource
private DtkPddOrderService pddOrderService;
private final String WEEK = "WEEK";
private final String MONTH = "MONTH";
/** 活跃积分固定值 */
private final BigDecimal FIXED_ACTIVE = new BigDecimal("10000");
/** 九维等级Id 0-9对应一度会员-九度会员 */
private static final List<Long> LEVELS = List.of(2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L);
@XxlJob("NineDimsDiddCpsJob")
public void execute() {
XxlJobHelper.log("数字联盟九维分红开始执行!");
/** step #1 获取配置信息,确定是按照周分配还是月分配 */
JtkSharingConfigDO jtkSharingConfigData = jtkSharingConfigService.getJtkSharingConfigData();
if (jtkSharingConfigData.getId() == 0) {
XxlJobHelper.log("数字联盟九维分红配置为空!不进行后续分配!");
return;
}
XxlJobHelper.log("数字联盟九维分红配置信息为:" + jtkSharingConfigData);
LocalDateTime startTime;
LocalDateTime endTime;
String statisticalPeriod = jtkSharingConfigData.getStatisticalPeriod();
/** step #2 获取查询订单的时间间隔 */
if (WEEK.equals(statisticalPeriod)) {
// 获取上周周一起始时间到周日的时间
startTime = LocalDateTimeUtils.getLastMondayStart(LocalDateTime.now());
endTime = LocalDateTimeUtils.getLastSundayEnd(LocalDateTime.now());
} else {
// 获取上月起始时间到月末的时间
startTime = LocalDateTimeUtils.beginOfMonth(LocalDateTime.now().minusMonths(1));
endTime = LocalDateTimeUtils.endOfMonth(LocalDateTime.now().minusMonths(1));
}
/** step #3 获取订单数据 */
List<JtkOrdersDO> jtkOrdersDOS = jtkOrdersService.getJtkOrdersByTimeInterval(startTime, endTime);
XxlJobHelper.log("数字联盟聚推客订单数据总数为:" + jtkOrdersDOS.size());
List<DtkTbOrderDO> dtkTbOrderDOS = dtkTbOrderService.getDtkOrdersByTimeInterval(startTime, endTime);
XxlJobHelper.log("数字联盟淘宝订单数据总数为:" + dtkTbOrderDOS.size());
List<DtkPddOrderDO> pddOrderDOS = pddOrderService.getPddOrdersByTimeInterval(startTime, endTime);
XxlJobHelper.log("数字联盟拼多多订单数据总数为:" + pddOrderDOS.size());
/** step #4 数据归一化:合并所有订单源到一个列表 */
List<OrderCommonData> allOrders = Stream.of(jtkOrdersDOS, dtkTbOrderDOS, pddOrderDOS)
.filter(list -> !list.isEmpty())
.flatMap(List::stream)
.collect(Collectors.toList());
if (allOrders.isEmpty()) {
XxlJobHelper.log("时间范围 [{} - {}] 内所有订单数据为空! 任务终止!", startTime, endTime);
return;
}
/** step #5 一次遍历, 聚合所有维度的数据 */
Map<Long, LevelAggregate> aggregatesByLevel = allOrders.stream()
.collect(Collectors.groupingBy(
OrderCommonData::getDprLevel,
Collectors.teeing(
// 下游收集器1: 计算总佣金
Collectors.mapping(OrderCommonData::getPromotionAmount, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)),
// 下游收集器2: 收集有效用户ID
Collectors.mapping(OrderCommonData::getDprUid, Collectors.toSet()),
// 合并两个结果
LevelAggregate::new
)
));
/** step 5.1 提取订单全量uid, 获取所有用户的自购订单 */
Set<String> allSids = allOrders.stream()
.map(OrderCommonData::getSid)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
aggregatesByLevel.forEach((leveId, aggregate) -> {
Set<Long> filteredUids = aggregate.getUids().stream()
.filter(uid -> uid != null && allSids.contains(String.valueOf(uid)))
.collect(Collectors.toSet());
aggregate.setUids(filteredUids);
XxlJobHelper.log("维度 {}: 总佣金 = {}, 有效用户数 = {}", leveId, aggregate.getCommission(), filteredUids.size());
});
/** step #6 计算总待分配佣金 */
BigDecimal totalCommissionToDistribute = aggregatesByLevel.values().stream()
.map(LevelAggregate::getCommission)
.reduce(BigDecimal.ZERO, BigDecimal::add);
XxlJobHelper.log("待分配总佣金为: {}" + totalCommissionToDistribute);
if (totalCommissionToDistribute.compareTo(BigDecimal.ZERO) <= 0) {
XxlJobHelper.log("待分配总佣金为0, 任务终止!");
return;
}
}
/**
* 计算每个维度的分红总额 (分红池)
*/
private Map<Long, BigDecimal> calculateAllocationPools(Map<Long, LevelAggregate> aggregatesByLevel, BigDecimal totalCommission, Map<Long, BigDecimal> dividendProps) {
Map<Long, BigDecimal> pools = new HashMap<>();
// 预计算所有级别的累计佣金,用于后续分母计算
// 降序排序便于后续计算
Map<Long, BigDecimal> cumulativeComminssion = new TreeMap<Long, BigDecimal>(Comparator.reverseOrder());
BigDecimal currentSum = BigDecimal.ZERO;
for (long level = LEVELS.get(LEVELS.size() - 1); level >= LEVELS.get(0); level--) {
// 仅处理在LEVELS中定义的级别
if (!LEVELS.contains(level)) continue;
LevelAggregate levelAggregate = aggregatesByLevel.get(level);
if (levelAggregate != null) {
currentSum = currentSum.add(levelAggregate.getCommission());
}
cumulativeComminssion.put(level, currentSum);
}
for (Long level : LEVELS) {
BigDecimal dividendProp = dividendProps.get(level);
// 分母: 从当前级别到最高级的总佣金
BigDecimal denominator = cumulativeComminssion.get(level);
// 健壮性检查
if (dividendProp == null || dividendProp.compareTo(BigDecimal.ZERO) <= 0) {
XxlJobHelper.log("警告: 维度 {} 的分红比例未配置或为0,跳过计算。", level);
pools.put(level, BigDecimal.ZERO);
continue;
}
if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) {
XxlJobHelper.log("警告: 维度 {} 及以上无分红佣金,分母为0,跳过计算。", level);
pools.put(level, BigDecimal.ZERO);
continue;
}
// 分红池 = 用户直推总佣金 / (当前级别到最高级别的总佣金) * 平台总佣金 * 分红比例 * 活跃积分
BigDecimal poolAmount = currentSum
.divide(denominator, 8, RoundingMode.DOWN)
.multiply(dividendProp).divide(BigDecimal.valueOf(100))
.multiply(FIXED_ACTIVE);
pools.put(level, poolAmount);
XxlJobHelper.log("维度 {} 分红池计算: {} / {} * {} * {} = {}", level, totalCommission, denominator, dividendProp, FIXED_ACTIVE, poolAmount);
XxlJobHelper.log("维度 {}: 分红池 = {}", level, poolAmount);
}
return pools;
}
/**
* 计算每个维度的人均分红金额
*/
private Map<Long, BigDecimal> calculateAverageDividends(Map<Long, BigDecimal> pools, Map<Long, LevelAggregate> aggregates) {
Map<Long, BigDecimal> averages = new HashMap<>();
for (Long level : LEVELS) {
BigDecimal poolAmount = pools.getOrDefault(level, BigDecimal.ZERO);
int userCount = aggregates.get(level) != null ? aggregates.get(level).getUids().size() : 0;
if (poolAmount.compareTo(BigDecimal.ZERO) <= 0 || userCount == 0) {
averages.put(level, BigDecimal.ZERO);
continue;
}
BigDecimal avgAmount = poolAmount.divide(new BigDecimal(userCount), 9, RoundingMode.DOWN);
averages.put(level, avgAmount);
}
return averages;
}
/**
* 内部数据类,用于在一次收集中聚合维度的多个信息
*/
@Data
@AllArgsConstructor
private static class LevelAggregate {
private BigDecimal commission;
private Set<Long> uids;
}
}
最终版优化代码
java
import com.alibaba.nacos.api.config.listener.Listener;
import com.alipay.api.domain.SealTabsVO;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Max;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 数字联盟九维分红定时任务
*/
@Component
public class NineDimsDiddCpsJob {
@Resource
private JtkSharingConfigService jtkSharingConfigService;
@Resource
private JtkOrdersService jtkOrdersService;
@Resource
private DtkTbOrderService dtkTbOrderService;
@Resource
private DtkPddOrderService pddOrderService;
private final String WEEK = "WEEK";
private final String MONTH = "MONTH";
/** 活跃积分固定值 */
private final BigDecimal FIXED_ACTIVE = new BigDecimal("10000");
/** 九维等级Id 0-9对应一度会员-九度会员 */
private static final List<Long> LEVELS = List.of(2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L);
@XxlJob("NineDimsDiddCpsJob")
public void execute() {
XxlJobHelper.log("数字联盟九维分红开始执行!");
/** step #1 获取配置信息,确定是按照周分配还是月分配 */
JtkSharingConfigDO jtkSharingConfigData = jtkSharingConfigService.getJtkSharingConfigData();
if (jtkSharingConfigData.getId() == 0) {
XxlJobHelper.log("数字联盟九维分红配置为空!不进行后续分配!");
return;
}
XxlJobHelper.log("数字联盟九维分红配置信息为:" + jtkSharingConfigData);
LocalDateTime startTime;
LocalDateTime endTime;
String statisticalPeriod = jtkSharingConfigData.getStatisticalPeriod();
/** step #2 获取查询订单的时间间隔 */
if (WEEK.equals(statisticalPeriod)) {
// 获取上周周一起始时间到周日的时间
startTime = LocalDateTimeUtils.getLastMondayStart(LocalDateTime.now());
endTime = LocalDateTimeUtils.getLastSundayEnd(LocalDateTime.now());
} else {
// 获取上月起始时间到月末的时间
startTime = LocalDateTimeUtils.beginOfMonth(LocalDateTime.now().minusMonths(1));
endTime = LocalDateTimeUtils.endOfMonth(LocalDateTime.now().minusMonths(1));
}
/** step #3 获取订单数据 */
List<JtkOrdersDO> jtkOrdersDOS = jtkOrdersService.getJtkOrdersByTimeInterval(startTime, endTime);
XxlJobHelper.log("数字联盟聚推客订单数据总数为:" + jtkOrdersDOS.size());
List<DtkTbOrderDO> dtkTbOrderDOS = dtkTbOrderService.getDtkOrdersByTimeInterval(startTime, endTime);
XxlJobHelper.log("数字联盟淘宝订单数据总数为:" + dtkTbOrderDOS.size());
List<DtkPddOrderDO> pddOrderDOS = pddOrderService.getPddOrdersByTimeInterval(startTime, endTime);
XxlJobHelper.log("数字联盟拼多多订单数据总数为:" + pddOrderDOS.size());
/** step #4 数据归一化:合并所有订单源到一个列表 */
List<OrderCommonData> allOrders = Stream.of(jtkOrdersDOS, dtkTbOrderDOS, pddOrderDOS)
.filter(list -> !list.isEmpty())
.flatMap(List::stream)
.collect(Collectors.toList());
if (allOrders.isEmpty()) {
XxlJobHelper.log("时间范围 [{} - {}] 内所有订单数据为空! 任务终止!", startTime, endTime);
return;
}
/** step #5 一次遍历, 聚合所有维度的数据 */
Map<Long, LevelAggregate> aggregatesByLevel = allOrders.stream()
.collect(Collectors.groupingBy(
OrderCommonData::getDprLevel,
Collectors.teeing(
// 下游收集器1: 计算总佣金
Collectors.mapping(OrderCommonData::getPromotionAmount, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)),
// 下游收集器2: 收集有效用户ID
Collectors.mapping(OrderCommonData::getDprUid, Collectors.toSet()),
// 合并两个结果
LevelAggregate::new
)
));
/** step 5.1 提取订单全量uid, 获取所有用户的自购订单 */
Set<String> allSids = allOrders.stream()
.map(OrderCommonData::getSid)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
aggregatesByLevel.forEach((leveId, aggregate) -> {
Set<Long> filteredUids = aggregate.getUids().stream()
.filter(uid -> uid != null && allSids.contains(String.valueOf(uid)))
.collect(Collectors.toSet());
aggregate.setUids(filteredUids);
XxlJobHelper.log("维度 {}: 总佣金 = {}, 有效用户数 = {}", leveId, aggregate.getCommission(), filteredUids.size());
});
/** step #6 计算总待分配佣金 */
BigDecimal totalCommissionToDistribute = aggregatesByLevel.values().stream()
.map(LevelAggregate::getCommission)
.reduce(BigDecimal.ZERO, BigDecimal::add);
XxlJobHelper.log("待分配总佣金为: {}" , totalCommissionToDistribute);
if (totalCommissionToDistribute.compareTo(BigDecimal.ZERO) <= 0) {
XxlJobHelper.log("待分配总佣金为0, 任务终止!");
return;
}
/** step #7 计算每个维度的分红总额 (分红池) */
Map<Long, BigDecimal> allocationPools = calculateAllocationPools(aggregatesByLevel, totalCommissionToDistribute, jtkSharingConfigData.getDividendProp());
/** step #8 计算每个维度的平均分红具体值 */
Map<Long, BigDecimal> averageAllocations = calculateAverageDividends(allocationPools, aggregatesByLevel);
/** step #9 执行分红逻辑 */
averageAllocations.forEach((level, avgAmount) -> {
if (avgAmount.compareTo(BigDecimal.ZERO) > 0) {
Set<Long> uids = aggregatesByLevel.get(level).getUids();
XxlJobHelper.log("执行第 {} 级会员分红, 共有 {} 个用户, 平均分红值为 {}", level, uids.size(), avgAmount);
// 这里执行分红逻辑,进行批量处理
}
});
XxlJobHelper.handleSuccess(LocalDate.now()+ " 数字联盟九维分红执行完毕!");
}
/**
* 计算每个维度的分红总额 (分红池)
*/
private Map<Long, BigDecimal> calculateAllocationPools(Map<Long, LevelAggregate> aggregatesByLevel, BigDecimal totalCommission, Map<Long, BigDecimal> dividendProps) {
Map<Long, BigDecimal> pools = new HashMap<>();
// 预计算所有级别的累计佣金,用于后续分母计算
// 降序排序便于后续计算
Map<Long, BigDecimal> cumulativeComminssion = new TreeMap<Long, BigDecimal>(Comparator.reverseOrder());
BigDecimal currentSum = BigDecimal.ZERO;
for (long level = LEVELS.get(LEVELS.size() - 1); level >= LEVELS.get(0); level--) {
// 仅处理在LEVELS中定义的级别
if (!LEVELS.contains(level)) continue;
LevelAggregate levelAggregate = aggregatesByLevel.get(level);
if (levelAggregate != null) {
currentSum = currentSum.add(levelAggregate.getCommission());
}
cumulativeComminssion.put(level, currentSum);
}
for (Long level : LEVELS) {
BigDecimal dividendProp = dividendProps.get(level);
// 分母: 从当前级别到最高级的总佣金
BigDecimal denominator = cumulativeComminssion.get(level);
// 健壮性检查
if (dividendProp == null || dividendProp.compareTo(BigDecimal.ZERO) <= 0) {
XxlJobHelper.log("警告: 维度 {} 的分红比例未配置或为0,跳过计算。", level);
pools.put(level, BigDecimal.ZERO);
continue;
}
if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) {
XxlJobHelper.log("警告: 维度 {} 及以上无分红佣金,分母为0,跳过计算。", level);
pools.put(level, BigDecimal.ZERO);
continue;
}
// 分红池 = 用户直推总佣金 / (当前级别到最高级别的总佣金) * 平台总佣金 * 分红比例 * 活跃积分
BigDecimal poolAmount = currentSum
.divide(denominator, 8, RoundingMode.DOWN)
.multiply(totalCommission)
.multiply(dividendProp).divide(BigDecimal.valueOf(100))
.multiply(FIXED_ACTIVE);
pools.put(level, poolAmount);
XxlJobHelper.log("维度 {} 分红池计算: {} / {} * {} * {} * {} = {}", level, currentSum, denominator, totalCommission, dividendProp.divide(BigDecimal.valueOf(100)), FIXED_ACTIVE, poolAmount);
XxlJobHelper.log("维度 {}: 分红池 = {}", level, poolAmount);
}
return pools;
}
/**
* 计算每个维度的人均分红金额
*/
private Map<Long, BigDecimal> calculateAverageDividends(Map<Long, BigDecimal> pools, Map<Long, LevelAggregate> aggregates) {
Map<Long, BigDecimal> averages = new HashMap<>();
for (Long level : LEVELS) {
BigDecimal poolAmount = pools.getOrDefault(level, BigDecimal.ZERO);
int userCount = aggregates.get(level) != null ? aggregates.get(level).getUids().size() : 0;
if (poolAmount.compareTo(BigDecimal.ZERO) <= 0 || userCount == 0) {
averages.put(level, BigDecimal.ZERO);
continue;
}
BigDecimal avgAmount = poolAmount.divide(new BigDecimal(userCount), 8, RoundingMode.DOWN);
averages.put(level, avgAmount);
}
return averages;
}
/**
* 内部数据类,用于在一次收集中聚合维度的多个信息
*/
@Data
@AllArgsConstructor
private static class LevelAggregate {
private BigDecimal commission;
private Set<Long> uids;
}
}
总结
-
将重复的逻辑抽象成了数据结构(Map, List)。
-
使用了一次遍历聚合(groupingBy + teeing)代替了多次循环。
-
将复杂的计算逻辑封装到了独立的辅助方法中。
同时也要充分吸收Gemini的优化要点,包括如下:
- 高效聚合:使用 Stream.of().flatMap() 合并数据源,再通过 Collectors.groupingBy 和 Collectors.teeing 一次性完成所有维度的数据聚合,将几十次遍历优化为一次。
- 结构清晰:用 Map<Long, LevelAggregate> 等数据结构替代了大量离散的变量,代码逻辑更集中,易于理解。
- 高可维护性:维度信息由 List LEVELS 管理。未来增加"十维"时,只需修改此列表和相关配置即可,计算逻辑无需改动,遵循了"开闭原则"。
- 健壮性增强:在所有除法和Map取值操作前都增加了校验,避免了 ArithmeticException 和 NullPointerException,并打印了明确的日志。
- 逻辑简化:通过预计算累积佣金 (cumulativeCommission),简化了分红池的计算公式,使其更易读。
- 代码复用:将核心计算逻辑(计算分红池、计算人均)封装成独立的私有方法,主流程 execute 方法更加清晰。
- 辅助数据类:使用内部类 LevelAggregate 来承载聚合结果,使得数据传递和处理更加规范。