Files
shz-dataDashboard/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/MathUtil.java
2026-07-13 18:09:40 +08:00

243 lines
7.7 KiB
Java

package com.ruoyi.cms.util;
import com.ruoyi.cms.constant.CommonConstant;
import com.ruoyi.cms.constant.enums.QueryTimeTypeEnum;
import com.ruoyi.cms.domain.vo.HeatmapVo;
import com.ruoyi.cms.domain.vo.QueryResultVo;
import com.ruoyi.cms.domain.vo.TreadVo;
import org.apache.commons.collections4.CollectionUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 数学工具
*
* @author 马宝龙
* @date 2026/1/21
*/
public class MathUtil {
private MathUtil() {
}
/**
* 除以 10000
*
* @param n 原数
* @return 结果
*/
public static BigDecimal divideTenThousand(Long n) {
if (Objects.isNull(n)) {
return null;
}
BigDecimal bigDecimal = new BigDecimal(n.toString());
return divideTenThousand(bigDecimal);
}
/**
* 除以 10000
*
* @param bigDecimal 原数
* @return 结果
*/
public static BigDecimal divideTenThousand(BigDecimal bigDecimal) {
if (Objects.isNull(bigDecimal)) {
return null;
}
String plainString = bigDecimal.divide(new BigDecimal("10000"), 2, RoundingMode.HALF_UP)
.stripTrailingZeros().toPlainString();
return new BigDecimal(plainString);
}
/**
* 计算比率
*
* @param divisor 除数(分母)
* @param dividend 被除数(分子)
* @return 比率
*/
public static BigDecimal divide(BigDecimal divisor, BigDecimal dividend) {
if (Objects.isNull(divisor) || Objects.isNull(dividend)) {
return null;
}
String plainString = dividend.divide(divisor, 2, RoundingMode.HALF_UP)
.stripTrailingZeros().toPlainString();
return new BigDecimal(plainString);
}
/**
* 计算比率
*
* @param divisor 除数(分母)
* @param dividend 被除数(分子)
* @return 比率
*/
public static BigDecimal calculatePercentage(Long divisor, Long dividend) {
BigDecimal divisorBigDecimal = Optional.ofNullable(divisor)
.map(item -> new BigDecimal(item.toString())).orElse(null);
BigDecimal dividendBigDecimal = Optional.ofNullable(dividend)
.map(item -> new BigDecimal(item.toString())).orElse(null);
return calculatePercentage(divisorBigDecimal, dividendBigDecimal);
}
/**
* 计算比率
*
* @param divisor 除数(分母)
* @param dividend 被除数(分子)
* @return 比率
*/
public static BigDecimal calculatePercentage(BigDecimal divisor, BigDecimal dividend) {
if (null == dividend || BigDecimal.ZERO.compareTo(dividend) == 0) {
return BigDecimal.ZERO;
} else {
if (null == divisor || BigDecimal.ZERO.compareTo(divisor) == 0) {
return new BigDecimal("100");
} else {
String plainString = dividend.multiply(new BigDecimal("100"))
.divide(divisor, 2, RoundingMode.HALF_UP)
.stripTrailingZeros().toPlainString();
return new BigDecimal(plainString);
}
}
}
/**
* 计算占比
*
* @param list 数据
*/
public static void calculatePercentage(List<QueryResultVo> list) {
if (CollectionUtils.isEmpty(list)) {
return;
}
long totalCount = 0;
for (QueryResultVo dto : list) {
totalCount = totalCount + Optional.ofNullable(dto.getCount()).orElse(0L);
}
for (QueryResultVo dto : list) {
dto.setCountPercentage(calculatePercentage(totalCount, dto.getCount()));
}
}
/**
* 计算环比
*
* @param lastCount 上期数据
* @param currentCount 本期数据
* @return 环比
*/
public static BigDecimal calculatePercentageGrowthRate(Long lastCount, Long currentCount) {
lastCount = Optional.ofNullable(lastCount).orElse(0L);
currentCount = Optional.ofNullable(currentCount).orElse(0L);
return calculatePercentage(lastCount, (currentCount - lastCount));
}
/**
* 计算环比
*
* @param lastBigDecimal 上期数据
* @param currentBigDecimal 本期数据
* @return 环比
*/
public static BigDecimal calculatePercentageGrowthRate(BigDecimal lastBigDecimal, BigDecimal currentBigDecimal) {
lastBigDecimal = Optional.ofNullable(lastBigDecimal).orElse(BigDecimal.ZERO);
currentBigDecimal = Optional.ofNullable(currentBigDecimal).orElse(BigDecimal.ZERO);
return calculatePercentage(lastBigDecimal, (currentBigDecimal.subtract(lastBigDecimal)));
}
/**
* 计算同比和环比
*
* @param queryTimeType 查询时间类型
* @param list 趋势数据列表
*/
public static void calculatePercentageGrowthRate(QueryTimeTypeEnum queryTimeType,
List<TreadVo> list) {
if (CollectionUtils.isEmpty(list)) {
return;
}
// 按月份正序排序
list.sort(Comparator.comparingInt(TreadVo::getSort));
for (int i = 0; i < list.size(); i++) {
TreadVo item = list.get(i);
long currentCount = Optional.ofNullable(item.getCurrentCount())
.orElse(0L);
long lastYearCount = Optional.ofNullable(item.getLastYearCount())
.orElse(0L);
// 年度不计算同比
if (queryTimeType != QueryTimeTypeEnum.YEAR) {
// 计算同比
item.setYearOnYearGrowthDiff(currentCount - lastYearCount);
if (0 == currentCount) {
item.setYearOnYearGrowthRate(BigDecimal.ZERO);
} else if (0 == lastYearCount) {
item.setYearOnYearGrowthRate(new BigDecimal("100.00"));
} else {
item.setYearOnYearGrowthRate(calculatePercentage(lastYearCount, (currentCount - lastYearCount)));
}
}
// 计算环比
long lastMonthCount;
if (0 == i) {
// 第一个取上期的最后一个数据
lastMonthCount = Optional.ofNullable(list.get(list.size() - 1).getLastYearCount())
.orElse(0L);
} else {
lastMonthCount = Optional.ofNullable(list.get(i - 1).getLastYearCount())
.orElse(0L);
}
item.setLastMonthCount(lastMonthCount);
item.setMonthOnMonthGrowthDiff(currentCount - lastMonthCount);
if (0 == currentCount) {
item.setMonthOnMonthGrowthRate(BigDecimal.ZERO);
} else if (0 == lastMonthCount) {
item.setMonthOnMonthGrowthRate(new BigDecimal("100.00"));
} else {
item.setMonthOnMonthGrowthRate(calculatePercentage(lastMonthCount, (currentCount - lastMonthCount)));
}
}
}
/**
* 计算占比 - 热力图
*
* @param list 热力图数据
*/
public static void calculatePercentageHeatmap(List<HeatmapVo> list) {
if (CollectionUtils.isEmpty(list)) {
return;
}
// 计算占比
long totalCount = 0;
for (HeatmapVo vo : list) {
totalCount = totalCount + Optional.ofNullable(vo.getCount()).orElse(0L);
}
for (HeatmapVo vo : list) {
vo.setPercentage(calculatePercentage(totalCount, vo.getCount()));
vo.setAreaName(Optional.ofNullable(CommonConstant.AREA_MAP.get(vo.getAreaCode())).orElse("未知"));
}
}
}