mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-16 13:19:57 +08:00
新增 查询经营效率接口
This commit is contained in:
@@ -4,7 +4,9 @@ import com.jsowell.common.core.page.PageResponse;
|
||||
import com.jsowell.pile.dto.MerchantOrderReportDTO;
|
||||
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessEfficiencyQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
@@ -45,4 +47,12 @@ public interface BusinessFinancialService {
|
||||
* @return 经营规模数据(含指标卡片和曲线图)
|
||||
*/
|
||||
BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 查询经营效率
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 经营效率数据(含指标卡片和曲线图)
|
||||
*/
|
||||
BusinessEfficiencyVO getBusinessEfficiency(BusinessEfficiencyQueryDTO dto);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.pile.dto.MerchantOrderReportDTO;
|
||||
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessEfficiencyQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationDateRangeDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationSummaryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.domain.PileConnectorInfo;
|
||||
import com.jsowell.pile.domain.PileStationInfo;
|
||||
import com.jsowell.pile.domain.SettleOrderReport;
|
||||
import com.jsowell.pile.service.BusinessFinancialService;
|
||||
import com.jsowell.pile.service.CarCouponRecordService;
|
||||
@@ -25,7 +27,9 @@ import com.jsowell.pile.service.PileConnectorInfoService;
|
||||
import com.jsowell.pile.service.PileStationInfoService;
|
||||
import com.jsowell.pile.service.SettleOrderReportService;
|
||||
import com.jsowell.pile.util.UserUtils;
|
||||
import com.jsowell.pile.vo.base.ConnectorInfoVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationDiagnosisItemVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO;
|
||||
@@ -74,6 +78,11 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
|
||||
private static final String SCALE_METRIC_SERVICE_AMOUNT = "SERVICE_AMOUNT";
|
||||
private static final String SCALE_METRIC_USE_ELECTRICITY = "USE_ELECTRICITY";
|
||||
|
||||
private static final String EFFICIENCY_METRIC_AVG_SERVICE_FEE_PER_DEGREE = "AVG_SERVICE_FEE_PER_DEGREE";
|
||||
private static final String EFFICIENCY_METRIC_TIME_UTILIZATION = "TIME_UTILIZATION";
|
||||
private static final String EFFICIENCY_METRIC_GUN_AVG_ELECTRICITY = "GUN_AVG_ELECTRICITY";
|
||||
private static final String EFFICIENCY_METRIC_POWER_UTILIZATION = "POWER_UTILIZATION";
|
||||
|
||||
/**
|
||||
* 财务查询通用线程池(支持多方法并行查询)
|
||||
* 核心线程数4:覆盖经营规模(2路)、我的钱包(3路)、经营分析(2路)、枪均效率(2路)等场景
|
||||
@@ -785,4 +794,323 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
|
||||
return BigDecimalUtils.nullToZero(report.getUseElectricity());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询经营效率
|
||||
* 优化:当前周期和上周期汇总并行查询,枪口数量与总额定功率并行查询
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 经营效率数据
|
||||
*/
|
||||
@Override
|
||||
public BusinessEfficiencyVO getBusinessEfficiency(BusinessEfficiencyQueryDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(6);
|
||||
dto.setStartTime(startDate.toString());
|
||||
dto.setEndTime(endDate.toString());
|
||||
}
|
||||
|
||||
BusinessOperationDateRangeDTO range = buildDateRangeFromEfficiency(dto);
|
||||
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
|
||||
|
||||
// 并行查询当前周期和上周期原始报表数据
|
||||
CompletableFuture<List<SettleOrderReport>> currentReportFuture = CompletableFuture.supplyAsync(
|
||||
() -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<List<SettleOrderReport>> previousReportFuture = CompletableFuture.supplyAsync(
|
||||
() -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
|
||||
// 等待报表查询完成
|
||||
List<SettleOrderReport> currentReports = currentReportFuture.join();
|
||||
List<SettleOrderReport> previousReports = previousReportFuture.join();
|
||||
|
||||
BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReports);
|
||||
BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReports);
|
||||
|
||||
Map<String, SettleOrderReport> currentDailyMap = buildDailyMapFromReports(currentReports);
|
||||
Map<String, SettleOrderReport> previousDailyMap = buildDailyMapFromReports(previousReports);
|
||||
|
||||
long dayCount = calculateInclusiveDays(range.getCurrentStart(), range.getCurrentEnd());
|
||||
|
||||
// 查询枪口数量和总额定功率
|
||||
ConnectorStats connectorStats = buildConnectorStats(stationIdList);
|
||||
int connectorCount = connectorStats.getConnectorCount();
|
||||
BigDecimal totalRatedPower = connectorStats.getTotalRatedPower();
|
||||
|
||||
// 构建指标卡片
|
||||
BigDecimal currentAvgServiceFeePerDegree = BigDecimalUtils.safeDivide(
|
||||
currentSummary.getServiceAmount(), currentSummary.getUseElectricity(), 2);
|
||||
BigDecimal previousAvgServiceFeePerDegree = BigDecimalUtils.safeDivide(
|
||||
previousSummary.getServiceAmount(), previousSummary.getUseElectricity(), 2);
|
||||
|
||||
BigDecimal currentTimeUtilization = calculateTimeUtilization(
|
||||
currentSummary.getChargeTime(), connectorCount, dayCount);
|
||||
BigDecimal previousTimeUtilization = calculateTimeUtilization(
|
||||
previousSummary.getChargeTime(), connectorCount, dayCount);
|
||||
|
||||
BigDecimal currentGunAvgElectricity = connectorCount > 0
|
||||
? BigDecimalUtils.safeDivide(currentSummary.getUseElectricity(),
|
||||
BigDecimal.valueOf(connectorCount), 2)
|
||||
: BigDecimal.ZERO;
|
||||
BigDecimal previousGunAvgElectricity = connectorCount > 0
|
||||
? BigDecimalUtils.safeDivide(previousSummary.getUseElectricity(),
|
||||
BigDecimal.valueOf(connectorCount), 2)
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
BigDecimal currentPowerUtilization = calculatePowerUtilization(
|
||||
currentSummary.getUseElectricity(), currentSummary.getChargeTime(),
|
||||
connectorCount, dayCount, totalRatedPower);
|
||||
BigDecimal previousPowerUtilization = calculatePowerUtilization(
|
||||
previousSummary.getUseElectricity(), previousSummary.getChargeTime(),
|
||||
connectorCount, dayCount, totalRatedPower);
|
||||
|
||||
List<BusinessScaleMetricVO> metricList = new ArrayList<>();
|
||||
metricList.add(buildScaleMetric(EFFICIENCY_METRIC_AVG_SERVICE_FEE_PER_DEGREE, "度均服务费", "元",
|
||||
currentAvgServiceFeePerDegree, previousAvgServiceFeePerDegree));
|
||||
metricList.add(buildScaleMetric(EFFICIENCY_METRIC_TIME_UTILIZATION, "时间利用率", "%",
|
||||
currentTimeUtilization, previousTimeUtilization));
|
||||
metricList.add(buildScaleMetric(EFFICIENCY_METRIC_GUN_AVG_ELECTRICITY, "枪均电量", "度",
|
||||
currentGunAvgElectricity, previousGunAvgElectricity));
|
||||
metricList.add(buildScaleMetric(EFFICIENCY_METRIC_POWER_UTILIZATION, "功率利用率", "%",
|
||||
currentPowerUtilization, previousPowerUtilization));
|
||||
|
||||
// 构建选中指标的曲线图数据
|
||||
String selectedMetricCode = StringUtils.isBlank(dto.getSelectedMetricCode())
|
||||
? EFFICIENCY_METRIC_AVG_SERVICE_FEE_PER_DEGREE : dto.getSelectedMetricCode();
|
||||
BusinessScaleChartVO chartData = buildEfficiencyChartFromMaps(
|
||||
selectedMetricCode, range, currentDailyMap, previousDailyMap,
|
||||
connectorCount, totalRatedPower);
|
||||
|
||||
return BusinessEfficiencyVO.builder()
|
||||
.currentStartTime(range.getCurrentStart())
|
||||
.currentEndTime(range.getCurrentEnd())
|
||||
.previousStartTime(range.getPreviousStart())
|
||||
.previousEndTime(range.getPreviousEnd())
|
||||
.metricList(metricList)
|
||||
.chartData(chartData)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建经营效率日期区间
|
||||
*/
|
||||
private BusinessOperationDateRangeDTO buildDateRangeFromEfficiency(BusinessEfficiencyQueryDTO dto) {
|
||||
try {
|
||||
LocalDate currentStart = LocalDate.parse(dto.getStartTime());
|
||||
LocalDate currentEnd = LocalDate.parse(dto.getEndTime());
|
||||
if (currentEnd.isBefore(currentStart)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
long days = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
|
||||
LocalDate previousEnd = currentStart.minusDays(1);
|
||||
LocalDate previousStart = previousEnd.minusDays(days - 1);
|
||||
return BusinessOperationDateRangeDTO.builder()
|
||||
.currentStart(currentStart.toString())
|
||||
.currentEnd(currentEnd.toString())
|
||||
.previousStart(previousStart.toString())
|
||||
.previousEnd(previousEnd.toString())
|
||||
.build();
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建枪口统计信息(枪口数量 + 总额定功率)
|
||||
*/
|
||||
private ConnectorStats buildConnectorStats(List<String> stationIdList) {
|
||||
List<ConnectorInfoVO> connectors;
|
||||
if (stationIdList == null) {
|
||||
// 平台账号:先查询所有站点ID,再批量查询枪口信息
|
||||
List<PileStationInfo> allStations = pileStationInfoService.selectPileStationInfoList(new PileStationInfo());
|
||||
List<String> allStationIds = allStations.stream()
|
||||
.map(s -> String.valueOf(s.getId()))
|
||||
.collect(Collectors.toList());
|
||||
if (allStationIds.isEmpty()) {
|
||||
return new ConnectorStats(0, BigDecimal.ZERO);
|
||||
}
|
||||
connectors = pileConnectorInfoService.batchSelectConnectorList(allStationIds);
|
||||
} else if (stationIdList.isEmpty()) {
|
||||
return new ConnectorStats(0, BigDecimal.ZERO);
|
||||
} else {
|
||||
connectors = pileConnectorInfoService.batchSelectConnectorList(stationIdList);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(connectors)) {
|
||||
return new ConnectorStats(0, BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
BigDecimal totalRatedPower = BigDecimal.ZERO;
|
||||
for (ConnectorInfoVO connector : connectors) {
|
||||
totalRatedPower = totalRatedPower.add(BigDecimalUtils.parseOrZero(connector.getRatedPower()));
|
||||
}
|
||||
|
||||
return new ConnectorStats(connectors.size(), totalRatedPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算时间利用率(%)
|
||||
* = 总充电时长(分钟) / (枪口数 × 天数 × 1440分钟) × 100
|
||||
*/
|
||||
private BigDecimal calculateTimeUtilization(BigDecimal chargeTime, int connectorCount, long dayCount) {
|
||||
if (connectorCount == 0 || dayCount == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal totalAvailableMinutes = BigDecimal.valueOf(connectorCount)
|
||||
.multiply(BigDecimal.valueOf(dayCount))
|
||||
.multiply(new BigDecimal("1440"));
|
||||
BigDecimal utilization = BigDecimalUtils.safeDivide(
|
||||
BigDecimalUtils.nullToZero(chargeTime), totalAvailableMinutes, 4);
|
||||
return utilization.multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算功率利用率(%)
|
||||
* = 实际总能量 / 理论最大总能量 × 100
|
||||
* = useElectricity / (总额定功率 × 天数 × 24) × 100
|
||||
*/
|
||||
private BigDecimal calculatePowerUtilization(BigDecimal useElectricity, BigDecimal chargeTime,
|
||||
int connectorCount, long dayCount, BigDecimal totalRatedPower) {
|
||||
if (totalRatedPower.compareTo(BigDecimal.ZERO) == 0 || dayCount == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal theoreticalMaxEnergy = totalRatedPower
|
||||
.multiply(BigDecimal.valueOf(dayCount))
|
||||
.multiply(new BigDecimal("24"));
|
||||
BigDecimal utilization = BigDecimalUtils.safeDivide(
|
||||
BigDecimalUtils.nullToZero(useElectricity), theoreticalMaxEnergy, 4);
|
||||
return utilization.multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dailyMap构建经营效率曲线图数据
|
||||
* 每个指标按天计算(度均服务费=当天服务费/当天电量, 时间利用率=当天充电时长/可用时长, 等)
|
||||
*/
|
||||
private BusinessScaleChartVO buildEfficiencyChartFromMaps(String metricCode,
|
||||
BusinessOperationDateRangeDTO range,
|
||||
Map<String, SettleOrderReport> currentDailyMap,
|
||||
Map<String, SettleOrderReport> previousDailyMap,
|
||||
int connectorCount,
|
||||
BigDecimal totalRatedPower) {
|
||||
String metricName;
|
||||
String unit;
|
||||
switch (metricCode) {
|
||||
case EFFICIENCY_METRIC_TIME_UTILIZATION:
|
||||
metricName = "时间利用率";
|
||||
unit = "%";
|
||||
break;
|
||||
case EFFICIENCY_METRIC_GUN_AVG_ELECTRICITY:
|
||||
metricName = "枪均电量";
|
||||
unit = "度";
|
||||
break;
|
||||
case EFFICIENCY_METRIC_POWER_UTILIZATION:
|
||||
metricName = "功率利用率";
|
||||
unit = "%";
|
||||
break;
|
||||
default:
|
||||
metricName = "度均服务费";
|
||||
unit = "元";
|
||||
break;
|
||||
}
|
||||
|
||||
LocalDate currentStart = LocalDate.parse(range.getCurrentStart());
|
||||
LocalDate currentEnd = LocalDate.parse(range.getCurrentEnd());
|
||||
LocalDate previousStart = LocalDate.parse(range.getPreviousStart());
|
||||
|
||||
long dayCount = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
|
||||
DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("MM/dd");
|
||||
|
||||
List<BusinessScaleChartVO.ChartPoint> chartPoints = new ArrayList<>();
|
||||
for (long i = 0; i < dayCount; i++) {
|
||||
LocalDate currentDate = currentStart.plusDays(i);
|
||||
LocalDate previousDate = previousStart.plusDays(i);
|
||||
|
||||
SettleOrderReport currentReport = currentDailyMap.get(currentDate.toString());
|
||||
SettleOrderReport previousReport = previousDailyMap.get(previousDate.toString());
|
||||
|
||||
BigDecimal currentValue = calculateEfficiencyMetricValue(
|
||||
metricCode, currentReport, connectorCount, 1, totalRatedPower);
|
||||
BigDecimal previousValue = calculateEfficiencyMetricValue(
|
||||
metricCode, previousReport, connectorCount, 1, totalRatedPower);
|
||||
|
||||
chartPoints.add(BusinessScaleChartVO.ChartPoint.builder()
|
||||
.date(currentDate.format(displayFormatter))
|
||||
.fullDate(currentDate.toString())
|
||||
.value(BigDecimalUtils.normalize(currentValue))
|
||||
.previousValue(BigDecimalUtils.normalize(previousValue))
|
||||
.changeRate(BigDecimalUtils.calculateRate(
|
||||
BigDecimalUtils.nullToZero(currentValue),
|
||||
BigDecimalUtils.nullToZero(previousValue)))
|
||||
.build());
|
||||
}
|
||||
|
||||
return BusinessScaleChartVO.builder()
|
||||
.metricCode(metricCode)
|
||||
.metricName(metricName)
|
||||
.unit(unit)
|
||||
.chartData(chartPoints)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指标编码计算单条报表记录对应的经营效率指标值
|
||||
*/
|
||||
private BigDecimal calculateEfficiencyMetricValue(String metricCode, SettleOrderReport report,
|
||||
int connectorCount, long dayCount,
|
||||
BigDecimal totalRatedPower) {
|
||||
if (report == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal serviceAmount = BigDecimalUtils.nullToZero(report.getServiceAmount());
|
||||
BigDecimal useElectricity = BigDecimalUtils.nullToZero(report.getUseElectricity());
|
||||
BigDecimal chargeTime = BigDecimalUtils.parseOrZero(report.getChargeTime());
|
||||
|
||||
switch (metricCode) {
|
||||
case EFFICIENCY_METRIC_AVG_SERVICE_FEE_PER_DEGREE:
|
||||
// 度均服务费 = 服务费 / 用电量
|
||||
return BigDecimalUtils.safeDivide(serviceAmount, useElectricity, 2);
|
||||
case EFFICIENCY_METRIC_TIME_UTILIZATION:
|
||||
// 时间利用率 = 充电时长 / (枪口数 × 1天 × 1440) × 100
|
||||
return calculateTimeUtilization(chargeTime, connectorCount, dayCount);
|
||||
case EFFICIENCY_METRIC_GUN_AVG_ELECTRICITY:
|
||||
// 枪均电量 = 用电量 / 枪口数
|
||||
return connectorCount > 0
|
||||
? BigDecimalUtils.safeDivide(useElectricity, BigDecimal.valueOf(connectorCount), 2)
|
||||
: BigDecimal.ZERO;
|
||||
case EFFICIENCY_METRIC_POWER_UTILIZATION:
|
||||
// 功率利用率 = 用电量 / (总额定功率 × 1天 × 24) × 100
|
||||
return calculatePowerUtilization(useElectricity, chargeTime,
|
||||
connectorCount, dayCount, totalRatedPower);
|
||||
default:
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 枪口统计信息内部类
|
||||
*/
|
||||
private static class ConnectorStats {
|
||||
private final int connectorCount;
|
||||
private final BigDecimal totalRatedPower;
|
||||
|
||||
ConnectorStats(int connectorCount, BigDecimal totalRatedPower) {
|
||||
this.connectorCount = connectorCount;
|
||||
this.totalRatedPower = totalRatedPower;
|
||||
}
|
||||
|
||||
int getConnectorCount() {
|
||||
return connectorCount;
|
||||
}
|
||||
|
||||
BigDecimal getTotalRatedPower() {
|
||||
return totalRatedPower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3497,7 +3497,7 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
logger.debug("redis中没有实时数据了,去数据库查");
|
||||
// redis中为空,去查库
|
||||
OrderMonitorData orderMonitorData = orderMonitorDataService.selectByTransactionCode(transactionCode);
|
||||
if (orderMonitorData != null) {
|
||||
if (orderMonitorData != null && StringUtils.isNotBlank(orderMonitorData.getMonitorData())) {
|
||||
String monitorData = orderMonitorData.getMonitorData();
|
||||
List<RealTimeMonitorData> dataList = JSON.parseArray(monitorData, RealTimeMonitorData.class);
|
||||
resultList.addAll(dataList);
|
||||
|
||||
Reference in New Issue
Block a user