@@ -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 ;
}
}
}