From 19c89db26534758ef1872d79d5edc847a176d2bf Mon Sep 17 00:00:00 2001 From: Lemon Date: Wed, 1 Apr 2026 14:13:29 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E8=BF=90=E8=90=A5?= =?UTF-8?q?=E8=AF=8A=E6=96=AD=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/BusinessFinancialController.java | 41 ++++ .../jsowell/common/util/BigDecimalUtils.java | 100 ++++++++ .../BusinessOperationAnalysisQueryDTO.java | 41 ++++ .../BusinessOperationDateRangeDTO.java | 39 +++ .../business/BusinessOperationSummaryDTO.java | 55 +++++ .../service/BusinessFinancialService.java | 10 + .../impl/BusinessFinancialServiceImpl.java | 222 ++++++++++++++++++ .../business/BusinessOperationAnalysisVO.java | 51 ++++ .../BusinessOperationDiagnosisItemVO.java | 61 +++++ .../business/BusinessOperationMetricVO.java | 51 ++++ 10 files changed, 671 insertions(+) create mode 100644 jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java diff --git a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java index dab69e459..6d6d4e70a 100644 --- a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java +++ b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java @@ -8,7 +8,9 @@ import com.jsowell.common.exception.BusinessException; import com.jsowell.common.response.RestApiResponse; import com.jsowell.pile.dto.MerchantOrderReportDTO; import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO; +import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO; import com.jsowell.pile.service.BusinessFinancialService; +import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -87,4 +89,43 @@ public class BusinessFinancialController extends BaseController { } return response; } + + /** + * 查询经营分析 + * + * @param dto 查询参数 + * @return 经营分析 + */ + @PostMapping("/operationAnalysis") + public RestApiResponse getBusinessOperationAnalysis(@RequestBody BusinessOperationAnalysisQueryDTO dto) { + logger.info("查询经营分析 params:{}", JSONObject.toJSONString(dto)); + RestApiResponse response; + try { + validateBusinessOperationQuery(dto); + BusinessOperationAnalysisVO result = businessFinancialService.getBusinessOperationAnalysis(dto); + response = new RestApiResponse<>(result); + logger.info("查询经营分析成功 startTime:{}, endTime:{}, selectedMetricCode:{}", + dto.getStartTime(), dto.getEndTime(), dto.getSelectedMetricCode()); + } catch (BusinessException e) { + logger.warn("查询经营分析业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e); + response = new RestApiResponse<>(e.getCode(), e.getMessage()); + } catch (Exception e) { + logger.error("查询经营分析系统异常 params:{}", JSONObject.toJSONString(dto), e); + response = new RestApiResponse<>(e); + } + return response; + } + + /** + * 校验经营分析查询参数 + * + * @param dto 查询参数 + */ + private void validateBusinessOperationQuery(BusinessOperationAnalysisQueryDTO dto) { + if (dto == null + || StringUtils.isBlank(dto.getStartTime()) + || StringUtils.isBlank(dto.getEndTime())) { + throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); + } + } } diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java b/jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java new file mode 100644 index 000000000..27371878a --- /dev/null +++ b/jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java @@ -0,0 +1,100 @@ +package com.jsowell.common.util; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * BigDecimal工具类 + * + * @author Auto + * @Date 2026/4/1 + */ +public class BigDecimalUtils { + + /** + * 工具类不允许实例化 + */ + private BigDecimalUtils() { + } + + /** + * 空值转零 + * + * @param value 原始值 + * @return 非空数值 + */ + public static BigDecimal nullToZero(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } + + /** + * 安全除法 + * + * @param dividend 被除数 + * @param divisor 除数 + * @param scale 保留小数位 + * @return 计算结果 + */ + public static BigDecimal safeDivide(BigDecimal dividend, BigDecimal divisor, int scale) { + BigDecimal safeDividend = nullToZero(dividend); + BigDecimal safeDivisor = nullToZero(divisor); + if (safeDivisor.compareTo(BigDecimal.ZERO) == 0) { + return BigDecimal.ZERO; + } + return safeDividend.divide(safeDivisor, scale, RoundingMode.HALF_UP); + } + + /** + * 标准化数值显示 + * + * @param value 原始值 + * @return 标准化后的数值 + */ + public static BigDecimal normalize(BigDecimal value) { + BigDecimal safeValue = nullToZero(value); + if (safeValue.compareTo(BigDecimal.ZERO) == 0) { + return BigDecimal.ZERO; + } + return safeValue.stripTrailingZeros(); + } + + /** + * 将字符串安全转换为BigDecimal + * + * @param value 原始字符串 + * @return BigDecimal值 + */ + public static BigDecimal parseOrZero(String value) { + if (StringUtils.isBlank(value)) { + return BigDecimal.ZERO; + } + try { + return new BigDecimal(value.trim()); + } catch (Exception e) { + return BigDecimal.ZERO; + } + } + + /** + * 计算环比百分比 + * + * @param currentValue 当前周期值 + * @param previousValue 上周期值 + * @return 环比字符串 + */ + public static String calculateRate(BigDecimal currentValue, BigDecimal previousValue) { + BigDecimal safeCurrentValue = nullToZero(currentValue); + BigDecimal safePreviousValue = nullToZero(previousValue); + if (safePreviousValue.compareTo(BigDecimal.ZERO) == 0) { + return "0%"; + } + BigDecimal rate = safeCurrentValue.subtract(safePreviousValue) + .divide(safePreviousValue, 4, RoundingMode.HALF_UP) + .multiply(new BigDecimal("100")); + BigDecimal normalizedRate = normalize(rate); + if (normalizedRate.compareTo(BigDecimal.ZERO) > 0) { + return "+" + normalizedRate.toPlainString() + "%"; + } + return normalizedRate.toPlainString() + "%"; + } +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java new file mode 100644 index 000000000..e172e2891 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java @@ -0,0 +1,41 @@ +package com.jsowell.pile.dto.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 经营状态/经营诊断查询DTO + * + * @author Auto + * @Date 2026/4/1 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessOperationAnalysisQueryDTO { + + /** + * 开始时间,格式:yyyy-MM-dd + */ + private String startTime; + + /** + * 结束时间,格式:yyyy-MM-dd + */ + private String endTime; + + /** + * 站点id列表 + */ + private List stationIdList; + + /** + * 当前选中的指标编码 + */ + private String selectedMetricCode; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java new file mode 100644 index 000000000..5fe90a8f7 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java @@ -0,0 +1,39 @@ +package com.jsowell.pile.dto.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 经营分析日期区间DTO + * + * @author Auto + * @Date 2026/4/1 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessOperationDateRangeDTO { + + /** + * 当前周期开始时间 + */ + private String currentStart; + + /** + * 当前周期结束时间 + */ + private String currentEnd; + + /** + * 上周期开始时间 + */ + private String previousStart; + + /** + * 上周期结束时间 + */ + private String previousEnd; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java new file mode 100644 index 000000000..a237b2b30 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java @@ -0,0 +1,55 @@ +package com.jsowell.pile.dto.business; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 经营分析汇总DTO + * + * @author Auto + * @Date 2026/4/1 + */ +@Data +public class BusinessOperationSummaryDTO { + + /** + * 订单总额 + */ + private BigDecimal orderAmount = BigDecimal.ZERO; + + /** + * 服务费总额 + */ + private BigDecimal serviceAmount = BigDecimal.ZERO; + + /** + * 总用电度数 + */ + private BigDecimal useElectricity = BigDecimal.ZERO; + + /** + * 订单量 + */ + private BigDecimal orderCount = BigDecimal.ZERO; + + /** + * 总充电时长 + */ + private BigDecimal chargeTime = BigDecimal.ZERO; + + /** + * 单均服务费 + */ + private BigDecimal avgServiceFee = BigDecimal.ZERO; + + /** + * 单均度数 + */ + private BigDecimal avgElectricity = BigDecimal.ZERO; + + /** + * 单均充电时长 + */ + private BigDecimal avgChargeTime = BigDecimal.ZERO; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java index 5f12db792..4b306a772 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java @@ -3,6 +3,8 @@ package com.jsowell.pile.service; 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.vo.uniapp.business.BusinessOperationAnalysisVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import com.jsowell.pile.vo.web.ParkingCouponRecordVO; @@ -16,4 +18,12 @@ public interface BusinessFinancialService { * @return 分页结果 */ PageResponse queryParkingCouponRecords(ParkingCouponRecordQueryDTO dto); + + /** + * 查询经营分析 + * + * @param dto 查询条件 + * @return 经营分析 + */ + BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto); } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java index 2e069d914..550cee72d 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java @@ -5,23 +5,37 @@ import com.github.pagehelper.PageInfo; import com.huifu.adapay.core.exception.BaseAdaPayException; import com.jsowell.adapay.service.AdapayService; import com.jsowell.adapay.vo.AdapayAccountBalanceVO; +import com.jsowell.common.util.BigDecimalUtils; import com.jsowell.common.core.domain.vo.AuthorizedDeptVO; import com.jsowell.common.core.page.PageResponse; +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.BusinessOperationAnalysisQueryDTO; +import com.jsowell.pile.dto.business.BusinessOperationDateRangeDTO; +import com.jsowell.pile.dto.business.BusinessOperationSummaryDTO; +import com.jsowell.pile.domain.SettleOrderReport; import com.jsowell.pile.service.BusinessFinancialService; import com.jsowell.pile.service.CarCouponRecordService; import com.jsowell.pile.service.ClearingWithdrawInfoService; import com.jsowell.pile.service.PileStationInfoService; import com.jsowell.pile.service.SettleOrderReportService; import com.jsowell.pile.util.UserUtils; +import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; +import com.jsowell.pile.vo.uniapp.business.BusinessOperationDiagnosisItemVO; +import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import com.jsowell.pile.vo.web.ParkingCouponRecordVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; /** @@ -31,6 +45,13 @@ import java.util.List; @Service public class BusinessFinancialServiceImpl implements BusinessFinancialService { + private static final String METRIC_ORDER_AMOUNT = "ORDER_AMOUNT"; + private static final String METRIC_AVG_SERVICE_FEE = "AVG_SERVICE_FEE"; + private static final String METRIC_AVG_ELECTRICITY = "AVG_ELECTRICITY"; + private static final String FACTOR_ORDER_COUNT = "ORDER_COUNT"; + private static final String FACTOR_AVG_ELECTRICITY = "AVG_ELECTRICITY"; + private static final String FACTOR_AVG_CHARGE_TIME = "AVG_CHARGE_TIME"; + @Autowired private SettleOrderReportService settleOrderReportService; @@ -148,4 +169,205 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { .total(pageInfo.getTotal()) .build(); } + + /** + * 查询经营分析 + * + * @param dto 查询条件 + * @return 经营分析 + */ + @Override + public BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto) { + BusinessOperationDateRangeDTO range = buildDateRange(dto); + List stationIdList = resolveStationIds(dto.getStationIdList()); + BusinessOperationSummaryDTO currentSummary = buildOperationSummary( + stationIdList, range.getCurrentStart(), range.getCurrentEnd()); + BusinessOperationSummaryDTO previousSummary = buildOperationSummary( + stationIdList, range.getPreviousStart(), range.getPreviousEnd()); + + List metricList = new ArrayList<>(); + metricList.add(buildMetric(METRIC_ORDER_AMOUNT, "订单总额", + currentSummary.getOrderAmount(), previousSummary.getOrderAmount())); + metricList.add(buildMetric(METRIC_AVG_SERVICE_FEE, "单均服务费", + currentSummary.getAvgServiceFee(), previousSummary.getAvgServiceFee())); + metricList.add(buildMetric(METRIC_AVG_ELECTRICITY, "单均度数", + currentSummary.getAvgElectricity(), previousSummary.getAvgElectricity())); + + BusinessOperationDiagnosisItemVO diagnosisItem = buildSelectedDiagnosisItem(dto, currentSummary, previousSummary); + + return BusinessOperationAnalysisVO.builder() + .currentStartTime(range.getCurrentStart()) + .currentEndTime(range.getCurrentEnd()) + .previousStartTime(range.getPreviousStart()) + .previousEndTime(range.getPreviousEnd()) + .metricList(metricList) + .diagnosisItem(diagnosisItem) + .build(); + } + + /** + * 构建当前选中指标对应的最大影响因素 + * + * @param dto 查询条件 + * @param currentSummary 当前周期汇总 + * @param previousSummary 上周期汇总 + * @return 经营诊断指标 + */ + private BusinessOperationDiagnosisItemVO buildSelectedDiagnosisItem(BusinessOperationAnalysisQueryDTO dto, + BusinessOperationSummaryDTO currentSummary, + BusinessOperationSummaryDTO previousSummary) { + String selectedMetricCode = dto.getSelectedMetricCode(); + if (METRIC_AVG_SERVICE_FEE.equals(selectedMetricCode)) { + return buildDiagnosis(METRIC_AVG_SERVICE_FEE, "单均服务费", + FACTOR_AVG_ELECTRICITY, "单均度数", + currentSummary.getAvgElectricity(), previousSummary.getAvgElectricity()); + } + if (METRIC_AVG_ELECTRICITY.equals(selectedMetricCode)) { + return buildDiagnosis(METRIC_AVG_ELECTRICITY, "单均度数", + FACTOR_AVG_CHARGE_TIME, "单均充电时长", + currentSummary.getAvgChargeTime(), previousSummary.getAvgChargeTime()); + } + return buildDiagnosis(METRIC_ORDER_AMOUNT, "订单总额", + FACTOR_ORDER_COUNT, "订单量", + currentSummary.getOrderCount(), previousSummary.getOrderCount()); + } + + /** + * 构建经营状态指标项 + * + * @param metricCode 指标编码 + * @param metricName 指标名称 + * @param currentValue 当前周期值 + * @param previousValue 上周期值 + * @return 经营状态指标 + */ + private BusinessOperationMetricVO buildMetric(String metricCode, String metricName, + BigDecimal currentValue, BigDecimal previousValue) { + BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue); + BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue); + BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue)); + return BusinessOperationMetricVO.builder() + .metricCode(metricCode) + .metricName(metricName) + .currentValue(BigDecimalUtils.normalize(safeCurrentValue)) + .previousValue(BigDecimalUtils.normalize(safePreviousValue)) + .changeValue(changeValue) + .changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue)) + .build(); + } + + /** + * 构建经营诊断指标项 + * + * @param metricCode 指标编码 + * @param metricName 指标名称 + * @param factorCode 影响因素编码 + * @param factorName 影响因素名称 + * @param currentValue 当前周期值 + * @param previousValue 上周期值 + * @return 经营诊断指标 + */ + private BusinessOperationDiagnosisItemVO buildDiagnosis(String metricCode, String metricName, + String factorCode, String factorName, + BigDecimal currentValue, BigDecimal previousValue) { + BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue); + BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue); + BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue)); + return BusinessOperationDiagnosisItemVO.builder() + .metricCode(metricCode) + .metricName(metricName) + .factorCode(factorCode) + .factorName(factorName) + .currentValue(BigDecimalUtils.normalize(safeCurrentValue)) + .previousValue(BigDecimalUtils.normalize(safePreviousValue)) + .changeValue(changeValue) + .changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue)) + .build(); + } + + /** + * 根据查询条件构建当前周期和上周期日期区间 + * + * @param dto 查询条件 + * @return 日期区间 + */ + private BusinessOperationDateRangeDTO buildDateRange(BusinessOperationAnalysisQueryDTO 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); + } + } + + /** + * 解析本次查询可用的站点ID列表 + * + * @param stationIdList 前端传入站点ID列表 + * @return 实际查询站点ID列表 + */ + private List resolveStationIds(List stationIdList) { + if (!CollectionUtils.isEmpty(stationIdList)) { + return stationIdList; + } + try { + AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap(); + if (authorizedMap == null) { + return stationIdList; + } + if (!CollectionUtils.isEmpty(authorizedMap.getStationDeptIds())) { + return pileStationInfoService.queryByStationDeptIds(authorizedMap.getStationDeptIds()); + } + if (!CollectionUtils.isEmpty(authorizedMap.getMerchantDeptIds())) { + return pileStationInfoService.getStationIdsByMerchantIds(authorizedMap.getMerchantDeptIds()); + } + } catch (Exception e) { + log.warn("解析经营分析站点权限失败", e); + } + return stationIdList; + } + + /** + * 汇总指定周期内的经营指标 + * + * @param stationIdList 站点ID列表 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 汇总结果 + */ + private BusinessOperationSummaryDTO buildOperationSummary(List stationIdList, + String startTime, + String endTime) { + List reportList = settleOrderReportService.queryOrderReport( + stationIdList, startTime, endTime); + if (CollectionUtils.isEmpty(reportList)) { + return new BusinessOperationSummaryDTO(); + } + BusinessOperationSummaryDTO summary = new BusinessOperationSummaryDTO(); + for (SettleOrderReport report : reportList) { + summary.setOrderAmount(summary.getOrderAmount().add(BigDecimalUtils.nullToZero(report.getTotalAmount()))); + summary.setServiceAmount(summary.getServiceAmount().add(BigDecimalUtils.nullToZero(report.getServiceAmount()))); + summary.setUseElectricity(summary.getUseElectricity().add(BigDecimalUtils.nullToZero(report.getUseElectricity()))); + summary.setOrderCount(summary.getOrderCount().add(BigDecimalUtils.parseOrZero(report.getChargeNum()))); + summary.setChargeTime(summary.getChargeTime().add(BigDecimalUtils.parseOrZero(report.getChargeTime()))); + } + summary.setAvgServiceFee(BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getOrderCount(), 2)); + summary.setAvgElectricity(BigDecimalUtils.safeDivide(summary.getUseElectricity(), summary.getOrderCount(), 2)); + summary.setAvgChargeTime(BigDecimalUtils.safeDivide(summary.getChargeTime(), summary.getOrderCount(), 2)); + return summary; + } } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java new file mode 100644 index 000000000..1a0893bdc --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java @@ -0,0 +1,51 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 经营分析VO + * + * @author Auto + * @Date 2026/4/1 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessOperationAnalysisVO { + + /** + * 当前周期开始时间 + */ + private String currentStartTime; + + /** + * 当前周期结束时间 + */ + private String currentEndTime; + + /** + * 上周期开始时间 + */ + private String previousStartTime; + + /** + * 上周期结束时间 + */ + private String previousEndTime; + + /** + * 顶部指标列表 + */ + private List metricList; + + /** + * 当前选中指标对应的最大影响因素 + */ + private BusinessOperationDiagnosisItemVO diagnosisItem; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java new file mode 100644 index 000000000..ac66d2ca3 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java @@ -0,0 +1,61 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 经营诊断项VO + * + * @author Auto + * @Date 2026/4/1 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessOperationDiagnosisItemVO { + + /** + * 指标编码 + */ + private String metricCode; + + /** + * 指标名称 + */ + private String metricName; + + /** + * 最大影响因素编码 + */ + private String factorCode; + + /** + * 最大影响因素名称 + */ + private String factorName; + + /** + * 当前周期值 + */ + private BigDecimal currentValue; + + /** + * 上周期值 + */ + private BigDecimal previousValue; + + /** + * 变化值 + */ + private BigDecimal changeValue; + + /** + * 环比 + */ + private String changeRate; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java new file mode 100644 index 000000000..eb52264aa --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java @@ -0,0 +1,51 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 经营指标VO + * + * @author Auto + * @Date 2026/4/1 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessOperationMetricVO { + + /** + * 指标编码 + */ + private String metricCode; + + /** + * 指标名称 + */ + private String metricName; + + /** + * 当前周期值 + */ + private BigDecimal currentValue; + + /** + * 上周期值 + */ + private BigDecimal previousValue; + + /** + * 变化值 + */ + private BigDecimal changeValue; + + /** + * 环比 + */ + private String changeRate; +} From f7d541d44ab137393f1189ec397653c6a5dbcec2 Mon Sep 17 00:00:00 2001 From: Lemon Date: Thu, 2 Apr 2026 14:13:23 +0800 Subject: [PATCH 2/5] =?UTF-8?q?update=20=E8=BF=90=E8=90=A5=E5=88=86?= =?UTF-8?q?=E6=9E=90=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/BusinessFinancialController.java | 9 ++++++--- .../src/main/resources/application-dev.yml | 2 +- .../service/impl/BusinessFinancialServiceImpl.java | 14 +++++++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java index 6d6d4e70a..a37e9cc1f 100644 --- a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java +++ b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java @@ -122,9 +122,12 @@ public class BusinessFinancialController extends BaseController { * @param dto 查询参数 */ private void validateBusinessOperationQuery(BusinessOperationAnalysisQueryDTO dto) { - if (dto == null - || StringUtils.isBlank(dto.getStartTime()) - || StringUtils.isBlank(dto.getEndTime())) { + if (dto == null) { + throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); + } + boolean startTimeBlank = StringUtils.isBlank(dto.getStartTime()); + boolean endTimeBlank = StringUtils.isBlank(dto.getEndTime()); + if (startTimeBlank ^ endTimeBlank) { throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); } } diff --git a/jsowell-admin/src/main/resources/application-dev.yml b/jsowell-admin/src/main/resources/application-dev.yml index 657922925..5de23e895 100644 --- a/jsowell-admin/src/main/resources/application-dev.yml +++ b/jsowell-admin/src/main/resources/application-dev.yml @@ -38,7 +38,7 @@ spring: master: # url: jdbc:mysql://106.14.94.149:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # username: jsowell_pre - url: jdbc:mysql://192.168.0.4:3306/jsowell_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://192.168.0.32:3306/jsowell_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: jsowell_dev password: 123456 # 从库数据源 diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java index 550cee72d..d78eaf7c1 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java @@ -28,6 +28,7 @@ import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import com.jsowell.pile.vo.web.ParkingCouponRecordVO; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -48,6 +49,7 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { private static final String METRIC_ORDER_AMOUNT = "ORDER_AMOUNT"; private static final String METRIC_AVG_SERVICE_FEE = "AVG_SERVICE_FEE"; private static final String METRIC_AVG_ELECTRICITY = "AVG_ELECTRICITY"; + private static final String FACTOR_ORDER_COUNT = "ORDER_COUNT"; private static final String FACTOR_AVG_ELECTRICITY = "AVG_ELECTRICITY"; private static final String FACTOR_AVG_CHARGE_TIME = "AVG_CHARGE_TIME"; @@ -178,6 +180,16 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { */ @Override public BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO 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 = buildDateRange(dto); List stationIdList = resolveStationIds(dto.getStationIdList()); BusinessOperationSummaryDTO currentSummary = buildOperationSummary( @@ -227,7 +239,7 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { FACTOR_AVG_CHARGE_TIME, "单均充电时长", currentSummary.getAvgChargeTime(), previousSummary.getAvgChargeTime()); } - return buildDiagnosis(METRIC_ORDER_AMOUNT, "订单总额", + return buildDiagnosis(METRIC_ORDER_AMOUNT, "订单总数", FACTOR_ORDER_COUNT, "订单量", currentSummary.getOrderCount(), previousSummary.getOrderCount()); } From 80a0f2ec229568038ce0368b306c5aed5371048a Mon Sep 17 00:00:00 2001 From: Lemon Date: Tue, 7 Apr 2026 10:33:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=9E=AA=E5=9D=87=E6=95=88=E7=8E=87=E5=88=86=E6=9E=90=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/BusinessFinancialController.java | 35 +++-- .../service/BusinessFinancialService.java | 9 ++ .../impl/BusinessFinancialServiceImpl.java | 126 +++++++++++++++++- 3 files changed, 156 insertions(+), 14 deletions(-) diff --git a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java index a37e9cc1f..8955228fd 100644 --- a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java +++ b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java @@ -11,6 +11,7 @@ import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO; import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO; import com.jsowell.pile.service.BusinessFinancialService; import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; +import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -101,7 +102,9 @@ public class BusinessFinancialController extends BaseController { logger.info("查询经营分析 params:{}", JSONObject.toJSONString(dto)); RestApiResponse response; try { - validateBusinessOperationQuery(dto); + if (dto == null) { + throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); + } BusinessOperationAnalysisVO result = businessFinancialService.getBusinessOperationAnalysis(dto); response = new RestApiResponse<>(result); logger.info("查询经营分析成功 startTime:{}, endTime:{}, selectedMetricCode:{}", @@ -117,18 +120,30 @@ public class BusinessFinancialController extends BaseController { } /** - * 校验经营分析查询参数 + * 查询枪均效率分析 * * @param dto 查询参数 + * @return 枪均效率分析 */ - private void validateBusinessOperationQuery(BusinessOperationAnalysisQueryDTO dto) { - if (dto == null) { - throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); - } - boolean startTimeBlank = StringUtils.isBlank(dto.getStartTime()); - boolean endTimeBlank = StringUtils.isBlank(dto.getEndTime()); - if (startTimeBlank ^ endTimeBlank) { - throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); + @PostMapping("/gunEfficiencyAnalysis") + public RestApiResponse getBusinessGunEfficiencyAnalysis(@RequestBody BusinessOperationAnalysisQueryDTO dto) { + logger.info("查询枪均效率分析 params:{}", JSONObject.toJSONString(dto)); + RestApiResponse response; + try { + if (dto == null) { + throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); + } + BusinessGunEfficiencyAnalysisVO result = businessFinancialService.getBusinessGunEfficiencyAnalysis(dto); + response = new RestApiResponse<>(result); + logger.info("查询枪均效率分析成功 startTime:{}, endTime:{}", + dto.getStartTime(), dto.getEndTime()); + } catch (BusinessException e) { + logger.warn("查询枪均效率分析业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e); + response = new RestApiResponse<>(e.getCode(), e.getMessage()); + } catch (Exception e) { + logger.error("查询枪均效率分析系统异常 params:{}", JSONObject.toJSONString(dto), e); + response = new RestApiResponse<>(e); } + return response; } } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java index 4b306a772..3abc86bf1 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java @@ -4,6 +4,7 @@ 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.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import com.jsowell.pile.vo.web.ParkingCouponRecordVO; @@ -26,4 +27,12 @@ public interface BusinessFinancialService { * @return 经营分析 */ BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto); + + /** + * 查询枪均效率分析 + * + * @param dto 查询条件 + * @return 枪均效率分析 + */ + BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto); } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java index d78eaf7c1..ae2576b38 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java @@ -15,13 +15,16 @@ import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO; 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.domain.PileConnectorInfo; import com.jsowell.pile.domain.SettleOrderReport; import com.jsowell.pile.service.BusinessFinancialService; import com.jsowell.pile.service.CarCouponRecordService; import com.jsowell.pile.service.ClearingWithdrawInfoService; +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.uniapp.business.BusinessGunEfficiencyAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationDiagnosisItemVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO; @@ -34,10 +37,12 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * 运营端小程序财务相关Service @@ -69,6 +74,9 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { @Autowired private PileStationInfoService pileStationInfoService; + @Autowired + private PileConnectorInfoService pileConnectorInfoService; + /** * 我的钱包查询 * @param dto @@ -217,6 +225,53 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { .build(); } + /** + * 查询枪均效率分析 + * + * @param dto 查询条件 + * @return 枪均效率分析 + */ + @Override + public BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO 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 = buildDateRange(dto); + List stationIdList = resolveStationIds(dto.getStationIdList()); + BusinessOperationSummaryDTO summary = buildOperationSummary( + stationIdList, range.getCurrentStart(), range.getCurrentEnd()); + + BigDecimal connectorCount = BigDecimal.valueOf(countConnectors(stationIdList)); + BigDecimal dayCount = BigDecimal.valueOf(calculateInclusiveDays(range.getCurrentStart(), range.getCurrentEnd())); + BigDecimal connectorDayCount = connectorCount.multiply(dayCount); + + BigDecimal gunAvgDailyServiceFee = BigDecimalUtils.safeDivide(summary.getServiceAmount(), connectorDayCount, 2); + BigDecimal gunAvgDailyElectricity = BigDecimalUtils.safeDivide(summary.getUseElectricity(), connectorDayCount, 4); + BigDecimal avgServiceFeePerDegree = BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getUseElectricity(), 4); + BigDecimal gunAvgDailyChargeTimeMinutes = BigDecimalUtils.safeDivide(summary.getChargeTime(), connectorDayCount, 2); + BigDecimal avgPower = BigDecimalUtils.safeDivide( + summary.getUseElectricity().multiply(new BigDecimal("60")), + summary.getChargeTime(), + 4); + + return BusinessGunEfficiencyAnalysisVO.builder() + .startTime(range.getCurrentStart()) + .endTime(range.getCurrentEnd()) + .gunAvgDailyServiceFee(BigDecimalUtils.normalize(gunAvgDailyServiceFee)) + .gunAvgDailyElectricity(BigDecimalUtils.normalize(gunAvgDailyElectricity)) + .avgServiceFeePerDegree(BigDecimalUtils.normalize(avgServiceFeePerDegree)) + .gunAvgDailyChargeTime(formatMinutesToHourMinute(gunAvgDailyChargeTimeMinutes)) + .avgPower(BigDecimalUtils.normalize(avgPower)) + .build(); + } + /** * 构建当前选中指标对应的最大影响因素 * @@ -333,13 +388,29 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { * @return 实际查询站点ID列表 */ private List resolveStationIds(List stationIdList) { - if (!CollectionUtils.isEmpty(stationIdList)) { - return stationIdList; + List authorizedStationIds = getAuthorizedStationIds(); + if (authorizedStationIds == null) { + return CollectionUtils.isEmpty(stationIdList) ? null : stationIdList; } + if (!CollectionUtils.isEmpty(stationIdList)) { + return stationIdList.stream() + .filter(authorizedStationIds::contains) + .distinct() + .collect(Collectors.toList()); + } + return authorizedStationIds; + } + + /** + * 获取当前账号可访问的站点ID列表,平台账号返回null表示不限制 + * + * @return 站点ID列表 + */ + private List getAuthorizedStationIds() { try { AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap(); if (authorizedMap == null) { - return stationIdList; + return null; } if (!CollectionUtils.isEmpty(authorizedMap.getStationDeptIds())) { return pileStationInfoService.queryByStationDeptIds(authorizedMap.getStationDeptIds()); @@ -350,7 +421,7 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { } catch (Exception e) { log.warn("解析经营分析站点权限失败", e); } - return stationIdList; + return null; } /** @@ -364,6 +435,9 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { private BusinessOperationSummaryDTO buildOperationSummary(List stationIdList, String startTime, String endTime) { + if (stationIdList != null && stationIdList.isEmpty()) { + return new BusinessOperationSummaryDTO(); + } List reportList = settleOrderReportService.queryOrderReport( stationIdList, startTime, endTime); if (CollectionUtils.isEmpty(reportList)) { @@ -382,4 +456,48 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { summary.setAvgChargeTime(BigDecimalUtils.safeDivide(summary.getChargeTime(), summary.getOrderCount(), 2)); return summary; } + + /** + * 统计站点范围内的枪口数量 + * + * @param stationIdList 站点ID列表 + * @return 枪口数量 + */ + private int countConnectors(List stationIdList) { + if (stationIdList == null) { + return pileConnectorInfoService.selectPileConnectorInfoList(new PileConnectorInfo()).size(); + } + if (stationIdList.isEmpty()) { + return 0; + } + return pileConnectorInfoService.batchSelectConnectorList(stationIdList).size(); + } + + /** + * 计算包含起止日期在内的天数 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 天数 + */ + private long calculateInclusiveDays(String startTime, String endTime) { + LocalDate startDate = LocalDate.parse(startTime); + LocalDate endDate = LocalDate.parse(endTime); + return ChronoUnit.DAYS.between(startDate, endDate) + 1; + } + + /** + * 将分钟数转换为xxhxxm格式 + * + * @param minutes 分钟数 + * @return 格式化结果 + */ + private String formatMinutesToHourMinute(BigDecimal minutes) { + long totalMinutes = BigDecimalUtils.nullToZero(minutes) + .setScale(0, RoundingMode.HALF_UP) + .longValue(); + long hours = totalMinutes / 60; + long remainMinutes = totalMinutes % 60; + return String.format("%02dh%02dm", hours, remainMinutes); + } } From 0eb86c999d2f807c28efb1a91f3c994aecdc003d Mon Sep 17 00:00:00 2001 From: Lemon Date: Fri, 10 Apr 2026 10:47:25 +0800 Subject: [PATCH 4/5] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BB=8F=E8=90=A5=E8=A7=84=E6=A8=A1=E6=9B=B2=E7=BA=BF=E5=9B=BE?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/BusinessFinancialController.java | 24 +- .../dto/business/BusinessScaleQueryDTO.java | 43 ++++ .../service/BusinessFinancialService.java | 10 + .../impl/BusinessFinancialServiceImpl.java | 241 ++++++++++++++++++ .../BusinessGunEfficiencyAnalysisVO.java | 56 ++++ .../uniapp/business/BusinessScaleChartVO.java | 77 ++++++ .../business/BusinessScaleMetricVO.java | 56 ++++ .../vo/uniapp/business/BusinessScaleVO.java | 51 ++++ 8 files changed, 547 insertions(+), 11 deletions(-) create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java create mode 100644 jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java diff --git a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java index 8955228fd..f80ae21e1 100644 --- a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java +++ b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java @@ -9,9 +9,11 @@ import com.jsowell.common.response.RestApiResponse; 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.BusinessScaleQueryDTO; import com.jsowell.pile.service.BusinessFinancialService; -import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO; +import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; +import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -120,28 +122,28 @@ public class BusinessFinancialController extends BaseController { } /** - * 查询枪均效率分析 + * 查询经营规模 * * @param dto 查询参数 - * @return 枪均效率分析 + * @return 经营规模数据(含指标卡片和曲线图) */ - @PostMapping("/gunEfficiencyAnalysis") - public RestApiResponse getBusinessGunEfficiencyAnalysis(@RequestBody BusinessOperationAnalysisQueryDTO dto) { - logger.info("查询枪均效率分析 params:{}", JSONObject.toJSONString(dto)); + @PostMapping("/businessScale") + public RestApiResponse getBusinessScale(@RequestBody BusinessScaleQueryDTO dto) { + logger.info("查询经营规模 params:{}", JSONObject.toJSONString(dto)); RestApiResponse response; try { if (dto == null) { throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); } - BusinessGunEfficiencyAnalysisVO result = businessFinancialService.getBusinessGunEfficiencyAnalysis(dto); + BusinessScaleVO result = businessFinancialService.getBusinessScale(dto); response = new RestApiResponse<>(result); - logger.info("查询枪均效率分析成功 startTime:{}, endTime:{}", - dto.getStartTime(), dto.getEndTime()); + logger.info("查询经营规模成功 startTime:{}, endTime:{}, selectedMetricCode:{}", + dto.getStartTime(), dto.getEndTime(), dto.getSelectedMetricCode()); } catch (BusinessException e) { - logger.warn("查询枪均效率分析业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e); + logger.warn("查询经营规模业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e); response = new RestApiResponse<>(e.getCode(), e.getMessage()); } catch (Exception e) { - logger.error("查询枪均效率分析系统异常 params:{}", JSONObject.toJSONString(dto), e); + logger.error("查询经营规模系统异常 params:{}", JSONObject.toJSONString(dto), e); response = new RestApiResponse<>(e); } return response; diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java new file mode 100644 index 000000000..779d0a775 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java @@ -0,0 +1,43 @@ +package com.jsowell.pile.dto.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 经营规模查询DTO + * + * @author zhangziao + * @date 2026/4/10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessScaleQueryDTO { + + /** + * 开始时间,格式:yyyy-MM-dd + */ + private String startTime; + + /** + * 结束时间,格式:yyyy-MM-dd + */ + private String endTime; + + /** + * 站点id列表 + */ + private List stationIdList; + + /** + * 当前选中的指标编码,用于查询对应的曲线图数据 + * ORDER_AMOUNT-订单总额, ORDER_COUNT-充电订单量, + * SERVICE_AMOUNT-充电服务费, USE_ELECTRICITY-充电电量 + */ + private String selectedMetricCode; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java index 3abc86bf1..ea94965fc 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java @@ -4,8 +4,10 @@ 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.BusinessScaleQueryDTO; import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; +import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import com.jsowell.pile.vo.web.ParkingCouponRecordVO; @@ -35,4 +37,12 @@ public interface BusinessFinancialService { * @return 枪均效率分析 */ BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto); + + /** + * 查询经营规模 + * + * @param dto 查询条件 + * @return 经营规模数据(含指标卡片和曲线图) + */ + BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO dto); } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java index ae2576b38..acba854a6 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java @@ -15,6 +15,7 @@ import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO; 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.SettleOrderReport; import com.jsowell.pile.service.BusinessFinancialService; @@ -28,6 +29,9 @@ import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationDiagnosisItemVO; import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO; +import com.jsowell.pile.vo.uniapp.business.BusinessScaleChartVO; +import com.jsowell.pile.vo.uniapp.business.BusinessScaleMetricVO; +import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO; import com.jsowell.pile.vo.web.MerchantOrderReportVO; import com.jsowell.pile.vo.web.ParkingCouponRecordVO; import lombok.extern.slf4j.Slf4j; @@ -39,9 +43,12 @@ import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -59,6 +66,11 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { private static final String FACTOR_AVG_ELECTRICITY = "AVG_ELECTRICITY"; private static final String FACTOR_AVG_CHARGE_TIME = "AVG_CHARGE_TIME"; + private static final String SCALE_METRIC_ORDER_AMOUNT = "ORDER_AMOUNT"; + private static final String SCALE_METRIC_ORDER_COUNT = "ORDER_COUNT"; + private static final String SCALE_METRIC_SERVICE_AMOUNT = "SERVICE_AMOUNT"; + private static final String SCALE_METRIC_USE_ELECTRICITY = "USE_ELECTRICITY"; + @Autowired private SettleOrderReportService settleOrderReportService; @@ -500,4 +512,233 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { long remainMinutes = totalMinutes % 60; return String.format("%02dh%02dm", hours, remainMinutes); } + + /** + * 查询经营规模 + * + * @param dto 查询条件 + * @return 经营规模数据 + */ + @Override + public BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO 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 = buildDateRangeFromScale(dto); + List stationIdList = resolveStationIds(dto.getStationIdList()); + + // 当前周期汇总 + BusinessOperationSummaryDTO currentSummary = buildOperationSummary( + stationIdList, range.getCurrentStart(), range.getCurrentEnd()); + // 上周期汇总 + BusinessOperationSummaryDTO previousSummary = buildOperationSummary( + stationIdList, range.getPreviousStart(), range.getPreviousEnd()); + + // 构建指标卡片 + List metricList = new ArrayList<>(); + metricList.add(buildScaleMetric(SCALE_METRIC_ORDER_AMOUNT, "订单总额", "元", + currentSummary.getOrderAmount(), previousSummary.getOrderAmount())); + metricList.add(buildScaleMetric(SCALE_METRIC_ORDER_COUNT, "充电订单量", "单", + currentSummary.getOrderCount(), previousSummary.getOrderCount())); + metricList.add(buildScaleMetric(SCALE_METRIC_SERVICE_AMOUNT, "充电服务费", "元", + currentSummary.getServiceAmount(), previousSummary.getServiceAmount())); + metricList.add(buildScaleMetric(SCALE_METRIC_USE_ELECTRICITY, "充电电量", "度", + currentSummary.getUseElectricity(), previousSummary.getUseElectricity())); + + // 构建选中指标的曲线图数据 + String selectedMetricCode = StringUtils.isBlank(dto.getSelectedMetricCode()) + ? SCALE_METRIC_USE_ELECTRICITY : dto.getSelectedMetricCode(); + BusinessScaleChartVO chartData = buildScaleChart( + selectedMetricCode, stationIdList, range); + + return BusinessScaleVO.builder() + .currentStartTime(range.getCurrentStart()) + .currentEndTime(range.getCurrentEnd()) + .previousStartTime(range.getPreviousStart()) + .previousEndTime(range.getPreviousEnd()) + .metricList(metricList) + .chartData(chartData) + .build(); + } + + /** + * 构建经营规模日期区间 + */ + private BusinessOperationDateRangeDTO buildDateRangeFromScale(BusinessScaleQueryDTO 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 BusinessScaleMetricVO buildScaleMetric(String metricCode, String metricName, String unit, + BigDecimal currentValue, BigDecimal previousValue) { + BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue); + BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue); + BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue)); + return BusinessScaleMetricVO.builder() + .metricCode(metricCode) + .metricName(metricName) + .unit(unit) + .currentValue(BigDecimalUtils.normalize(safeCurrentValue)) + .previousValue(BigDecimalUtils.normalize(safePreviousValue)) + .changeValue(changeValue) + .changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue)) + .build(); + } + + /** + * 构建经营规模曲线图数据 + */ + private BusinessScaleChartVO buildScaleChart(String metricCode, List stationIdList, + BusinessOperationDateRangeDTO range) { + String metricName; + String unit; + switch (metricCode) { + case SCALE_METRIC_ORDER_AMOUNT: + metricName = "订单总额"; + unit = "元"; + break; + case SCALE_METRIC_ORDER_COUNT: + metricName = "充电订单量"; + unit = "单"; + break; + case SCALE_METRIC_SERVICE_AMOUNT: + metricName = "充电服务费"; + unit = "元"; + break; + default: + metricName = "充电电量"; + unit = "度"; + break; + } + + // 查询当前周期每日数据 + Map currentDailyMap = queryDailyReportMap( + stationIdList, range.getCurrentStart(), range.getCurrentEnd()); + // 查询上周期每日数据 + Map previousDailyMap = queryDailyReportMap( + stationIdList, range.getPreviousStart(), range.getPreviousEnd()); + + 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 chartPoints = new ArrayList<>(); + for (long i = 0; i < dayCount; i++) { + LocalDate currentDate = currentStart.plusDays(i); + LocalDate previousDate = previousStart.plusDays(i); + + String dateKey = currentDate.toString(); + SettleOrderReport currentReport = currentDailyMap.get(dateKey); + SettleOrderReport previousReport = previousDailyMap.get(previousDate.toString()); + + BigDecimal currentValue = extractMetricValue(metricCode, currentReport); + BigDecimal previousValue = extractMetricValue(metricCode, previousReport); + + chartPoints.add(BusinessScaleChartVO.ChartPoint.builder() + .date(currentDate.format(displayFormatter)) + .fullDate(dateKey) + .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(); + } + + /** + * 查询每日报表数据,按日期分组返回Map + */ + private Map queryDailyReportMap(List stationIdList, + String startTime, String endTime) { + if (stationIdList != null && stationIdList.isEmpty()) { + return new LinkedHashMap<>(); + } + List reportList = settleOrderReportService.queryOrderReport( + stationIdList, startTime, endTime); + // 按tradeDate分组汇总 + Map dailyMap = new LinkedHashMap<>(); + if (!CollectionUtils.isEmpty(reportList)) { + for (SettleOrderReport report : reportList) { + String tradeDate = report.getTradeDate(); + if (StringUtils.isBlank(tradeDate)) { + continue; + } + SettleOrderReport existing = dailyMap.get(tradeDate); + if (existing == null) { + dailyMap.put(tradeDate, report); + } else { + // 合并同一天多个站点的数据 + existing.setTotalAmount(existing.getTotalAmount().add( + BigDecimalUtils.nullToZero(report.getTotalAmount()))); + existing.setServiceAmount(existing.getServiceAmount().add( + BigDecimalUtils.nullToZero(report.getServiceAmount()))); + existing.setUseElectricity(existing.getUseElectricity().add( + BigDecimalUtils.nullToZero(report.getUseElectricity()))); + existing.setChargeNum(String.valueOf( + BigDecimalUtils.parseOrZero(existing.getChargeNum()) + .add(BigDecimalUtils.parseOrZero(report.getChargeNum())))); + } + } + } + return dailyMap; + } + + /** + * 从报表记录中提取指定指标的值 + */ + private BigDecimal extractMetricValue(String metricCode, SettleOrderReport report) { + if (report == null) { + return BigDecimal.ZERO; + } + switch (metricCode) { + case SCALE_METRIC_ORDER_AMOUNT: + return BigDecimalUtils.nullToZero(report.getTotalAmount()); + case SCALE_METRIC_ORDER_COUNT: + return BigDecimalUtils.parseOrZero(report.getChargeNum()); + case SCALE_METRIC_SERVICE_AMOUNT: + return BigDecimalUtils.nullToZero(report.getServiceAmount()); + case SCALE_METRIC_USE_ELECTRICITY: + default: + return BigDecimalUtils.nullToZero(report.getUseElectricity()); + } + } } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java new file mode 100644 index 000000000..957bf73ec --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java @@ -0,0 +1,56 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 枪均效率分析VO + * + * @author Auto + * @Date 2026/4/3 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessGunEfficiencyAnalysisVO { + + /** + * 开始时间 + */ + private String startTime; + + /** + * 结束时间 + */ + private String endTime; + + /** + * 枪日均服务费(元) + */ + private BigDecimal gunAvgDailyServiceFee; + + /** + * 枪日均充电量(度) + */ + private BigDecimal gunAvgDailyElectricity; + + /** + * 度均服务费(元) + */ + private BigDecimal avgServiceFeePerDegree; + + /** + * 枪日均充电时长,格式:xxhxxm + */ + private String gunAvgDailyChargeTime; + + /** + * 平均功率(KW) + */ + private BigDecimal avgPower; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java new file mode 100644 index 000000000..1839cf47e --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java @@ -0,0 +1,77 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 经营规模曲线图数据VO + * + * @author zhangziao + * @date 2026/4/10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessScaleChartVO { + + /** + * 指标编码 + */ + private String metricCode; + + /** + * 指标名称 + */ + private String metricName; + + /** + * 指标单位 + */ + private String unit; + + /** + * 曲线图数据点列表 + */ + private List chartData; + + /** + * 曲线图单个数据点 + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class ChartPoint { + + /** + * 日期,格式:MM/dd + */ + private String date; + + /** + * 完整日期,格式:yyyy-MM-dd + */ + private String fullDate; + + /** + * 当前周期值 + */ + private BigDecimal value; + + /** + * 上周期同日值 + */ + private BigDecimal previousValue; + + /** + * 环比 + */ + private String changeRate; + } +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java new file mode 100644 index 000000000..c70c154e1 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java @@ -0,0 +1,56 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 经营规模指标卡片VO + * + * @author zhangziao + * @date 2026/4/10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessScaleMetricVO { + + /** + * 指标编码 + */ + private String metricCode; + + /** + * 指标名称 + */ + private String metricName; + + /** + * 指标单位 + */ + private String unit; + + /** + * 当前周期值 + */ + private BigDecimal currentValue; + + /** + * 上周期值 + */ + private BigDecimal previousValue; + + /** + * 变化值 + */ + private BigDecimal changeValue; + + /** + * 环比,格式如 +3.91% 或 -3.91% + */ + private String changeRate; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java new file mode 100644 index 000000000..9ddb32778 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java @@ -0,0 +1,51 @@ +package com.jsowell.pile.vo.uniapp.business; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 经营规模VO + * + * @author zhangziao + * @date 2026/4/10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BusinessScaleVO { + + /** + * 当前周期开始时间 + */ + private String currentStartTime; + + /** + * 当前周期结束时间 + */ + private String currentEndTime; + + /** + * 上周期开始时间 + */ + private String previousStartTime; + + /** + * 上周期结束时间 + */ + private String previousEndTime; + + /** + * 指标卡片列表(订单总额、充电订单量、充电服务费、充电电量) + */ + private List metricList; + + /** + * 当前选中指标对应的曲线图数据 + */ + private BusinessScaleChartVO chartData; +} From 216613b67c53ac15931cf646665e71dba2c0d7e2 Mon Sep 17 00:00:00 2001 From: Lemon Date: Fri, 10 Apr 2026 14:38:27 +0800 Subject: [PATCH 5/5] =?UTF-8?q?update=20=20=E8=BF=90=E8=90=A5=E7=AB=AF?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E4=BC=98=E5=8C=96=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/BusinessFinancialServiceImpl.java | 288 ++++++++++-------- 1 file changed, 166 insertions(+), 122 deletions(-) diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java index acba854a6..8cbe41293 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java @@ -49,6 +49,9 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; /** @@ -71,6 +74,17 @@ 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"; + /** + * 财务查询通用线程池(支持多方法并行查询) + * 核心线程数4:覆盖经营规模(2路)、我的钱包(3路)、经营分析(2路)、枪均效率(2路)等场景 + */ + private static final ExecutorService FINANCIAL_THREAD_POOL = Executors.newFixedThreadPool( + 4, r -> { + Thread t = new Thread(r, "business-financial-query"); + t.setDaemon(true); + return t; + }); + @Autowired private SettleOrderReportService settleOrderReportService; @@ -91,47 +105,58 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { /** * 我的钱包查询 + * 优化:订单报表DB、汇付余额HTTP、提现金额DB 三个独立数据源并行查询 * @param dto * @return */ @Override public MerchantOrderReportVO getMyWallet(MerchantOrderReportDTO dto) { - // 查询运营商订单报表 - MerchantOrderReportVO result = settleOrderReportService.getMerchantOrderReportV2(dto); + String merchantId = dto.getMerchantId(); - // 查询账户余额 - AdapayAccountBalanceVO accountBalanceVO = new AdapayAccountBalanceVO(); + // 并行查询三个独立数据源 + CompletableFuture reportFuture = CompletableFuture.supplyAsync( + () -> settleOrderReportService.getMerchantOrderReportV2(dto), FINANCIAL_THREAD_POOL); + CompletableFuture balanceFuture = CompletableFuture.supplyAsync(() -> { + try { + return adapayService.queryAdapayAccountBalance(merchantId); + } catch (BaseAdaPayException e) { + log.error("查询汇付账户余额异常 merchantId:{}", merchantId, e); + return null; + } + }, FINANCIAL_THREAD_POOL); + CompletableFuture withdrawFuture = CompletableFuture.supplyAsync(() -> { + try { + BigDecimal withdraw = clearingWithdrawInfoService.queryTotalWithdraw(merchantId); + return withdraw != null ? withdraw : BigDecimal.ZERO; + } catch (Exception e) { + log.error("查询累计提现金额异常 merchantId:{}", merchantId, e); + return BigDecimal.ZERO; + } + }, FINANCIAL_THREAD_POOL); + + // 等待所有查询完成 + MerchantOrderReportVO result = reportFuture.join(); + AdapayAccountBalanceVO accountBalanceVO = balanceFuture.join(); + BigDecimal totalWithdraw = withdrawFuture.join(); + + // 设置账户余额 BigDecimal acctBalance = BigDecimal.ZERO; BigDecimal pendingAmount = BigDecimal.ZERO; - try { - accountBalanceVO = adapayService.queryAdapayAccountBalance(dto.getMerchantId()); - if (accountBalanceVO == null) { - log.error("我的钱包查询异常 查询出accountBalanceVO 为null"); - return result; - } + BigDecimal canWithdrawAmount = null; + if (accountBalanceVO != null) { if (accountBalanceVO.getAcctBalance() != null) { acctBalance = accountBalanceVO.getAcctBalance(); } if (accountBalanceVO.getPendingAmount() != null) { pendingAmount = accountBalanceVO.getPendingAmount(); } - } catch (BaseAdaPayException e) { - log.error("查询汇付账户余额异常 merchantId:{}", dto.getMerchantId(), e); + canWithdrawAmount = accountBalanceVO.getLastAvlBalance(); + } else { + log.error("我的钱包查询异常 查询出accountBalanceVO 为null"); } result.getMerchantOrderReport().setAcctBalance(acctBalance); result.getMerchantOrderReport().setPendingAmount(pendingAmount); - result.getMerchantOrderReport().setCanWithdrawAmount(accountBalanceVO.getLastAvlBalance()); - - // 查询累计提现金额 - BigDecimal totalWithdraw = BigDecimal.ZERO; - try { - BigDecimal withdraw = clearingWithdrawInfoService.queryTotalWithdraw(dto.getMerchantId()); - if (withdraw != null) { - totalWithdraw = withdraw; - } - } catch (Exception e) { - log.error("查询累计提现金额异常 merchantId:{}", dto.getMerchantId(), e); - } + result.getMerchantOrderReport().setCanWithdrawAmount(canWithdrawAmount); result.getMerchantOrderReport().setTotalWithdraw(totalWithdraw); return result; @@ -194,6 +219,7 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { /** * 查询经营分析 + * 优化:当前周期和上周期汇总并行查询 * * @param dto 查询条件 * @return 经营分析 @@ -212,10 +238,17 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { BusinessOperationDateRangeDTO range = buildDateRange(dto); List stationIdList = resolveStationIds(dto.getStationIdList()); - BusinessOperationSummaryDTO currentSummary = buildOperationSummary( - stationIdList, range.getCurrentStart(), range.getCurrentEnd()); - BusinessOperationSummaryDTO previousSummary = buildOperationSummary( - stationIdList, range.getPreviousStart(), range.getPreviousEnd()); + + // 并行查询当前周期和上周期原始数据 + CompletableFuture> currentReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()), + FINANCIAL_THREAD_POOL); + CompletableFuture> previousReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()), + FINANCIAL_THREAD_POOL); + + BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReportFuture.join()); + BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReportFuture.join()); List metricList = new ArrayList<>(); metricList.add(buildMetric(METRIC_ORDER_AMOUNT, "订单总额", @@ -239,6 +272,7 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { /** * 查询枪均效率分析 + * 优化:订单汇总DB查询与枪口数量DB查询并行执行 * * @param dto 查询条件 * @return 枪均效率分析 @@ -257,12 +291,21 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { BusinessOperationDateRangeDTO range = buildDateRange(dto); List stationIdList = resolveStationIds(dto.getStationIdList()); - BusinessOperationSummaryDTO summary = buildOperationSummary( - stationIdList, range.getCurrentStart(), range.getCurrentEnd()); - BigDecimal connectorCount = BigDecimal.valueOf(countConnectors(stationIdList)); + // 并行查询订单汇总和枪口数量 + CompletableFuture summaryFuture = CompletableFuture.supplyAsync( + () -> buildSummaryFromReports( + queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd())), + FINANCIAL_THREAD_POOL); + CompletableFuture connectorCountFuture = CompletableFuture.supplyAsync( + () -> countConnectors(stationIdList), FINANCIAL_THREAD_POOL); + + BusinessOperationSummaryDTO summary = summaryFuture.join(); + int connectorCount = connectorCountFuture.join(); + + BigDecimal connectorCountBd = BigDecimal.valueOf(connectorCount); BigDecimal dayCount = BigDecimal.valueOf(calculateInclusiveDays(range.getCurrentStart(), range.getCurrentEnd())); - BigDecimal connectorDayCount = connectorCount.multiply(dayCount); + BigDecimal connectorDayCount = connectorCountBd.multiply(dayCount); BigDecimal gunAvgDailyServiceFee = BigDecimalUtils.safeDivide(summary.getServiceAmount(), connectorDayCount, 2); BigDecimal gunAvgDailyElectricity = BigDecimalUtils.safeDivide(summary.getUseElectricity(), connectorDayCount, 4); @@ -436,39 +479,6 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { return null; } - /** - * 汇总指定周期内的经营指标 - * - * @param stationIdList 站点ID列表 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 汇总结果 - */ - private BusinessOperationSummaryDTO buildOperationSummary(List stationIdList, - String startTime, - String endTime) { - if (stationIdList != null && stationIdList.isEmpty()) { - return new BusinessOperationSummaryDTO(); - } - List reportList = settleOrderReportService.queryOrderReport( - stationIdList, startTime, endTime); - if (CollectionUtils.isEmpty(reportList)) { - return new BusinessOperationSummaryDTO(); - } - BusinessOperationSummaryDTO summary = new BusinessOperationSummaryDTO(); - for (SettleOrderReport report : reportList) { - summary.setOrderAmount(summary.getOrderAmount().add(BigDecimalUtils.nullToZero(report.getTotalAmount()))); - summary.setServiceAmount(summary.getServiceAmount().add(BigDecimalUtils.nullToZero(report.getServiceAmount()))); - summary.setUseElectricity(summary.getUseElectricity().add(BigDecimalUtils.nullToZero(report.getUseElectricity()))); - summary.setOrderCount(summary.getOrderCount().add(BigDecimalUtils.parseOrZero(report.getChargeNum()))); - summary.setChargeTime(summary.getChargeTime().add(BigDecimalUtils.parseOrZero(report.getChargeTime()))); - } - summary.setAvgServiceFee(BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getOrderCount(), 2)); - summary.setAvgElectricity(BigDecimalUtils.safeDivide(summary.getUseElectricity(), summary.getOrderCount(), 2)); - summary.setAvgChargeTime(BigDecimalUtils.safeDivide(summary.getChargeTime(), summary.getOrderCount(), 2)); - return summary; - } - /** * 统计站点范围内的枪口数量 * @@ -534,12 +544,24 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { BusinessOperationDateRangeDTO range = buildDateRangeFromScale(dto); List stationIdList = resolveStationIds(dto.getStationIdList()); - // 当前周期汇总 - BusinessOperationSummaryDTO currentSummary = buildOperationSummary( - stationIdList, range.getCurrentStart(), range.getCurrentEnd()); - // 上周期汇总 - BusinessOperationSummaryDTO previousSummary = buildOperationSummary( - stationIdList, range.getPreviousStart(), range.getPreviousEnd()); + // 并行查询当前周期和上周期的原始报表数据,各只查一次 + CompletableFuture> currentReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()), + FINANCIAL_THREAD_POOL); + CompletableFuture> previousReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()), + FINANCIAL_THREAD_POOL); + + // 等待两个查询完成 + List currentReports = currentReportFuture.join(); + List previousReports = previousReportFuture.join(); + + // 从同一份原始数据分别构建汇总指标和每日明细 + BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReports); + BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReports); + + Map currentDailyMap = buildDailyMapFromReports(currentReports); + Map previousDailyMap = buildDailyMapFromReports(previousReports); // 构建指标卡片 List metricList = new ArrayList<>(); @@ -552,11 +574,11 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { metricList.add(buildScaleMetric(SCALE_METRIC_USE_ELECTRICITY, "充电电量", "度", currentSummary.getUseElectricity(), previousSummary.getUseElectricity())); - // 构建选中指标的曲线图数据 + // 构建选中指标的曲线图数据(复用已查询的dailyMap) String selectedMetricCode = StringUtils.isBlank(dto.getSelectedMetricCode()) ? SCALE_METRIC_USE_ELECTRICITY : dto.getSelectedMetricCode(); - BusinessScaleChartVO chartData = buildScaleChart( - selectedMetricCode, stationIdList, range); + BusinessScaleChartVO chartData = buildScaleChartFromMaps( + selectedMetricCode, range, currentDailyMap, previousDailyMap); return BusinessScaleVO.builder() .currentStartTime(range.getCurrentStart()) @@ -614,10 +636,77 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { } /** - * 构建经营规模曲线图数据 + * 查询原始报表数据(供并行调用) */ - private BusinessScaleChartVO buildScaleChart(String metricCode, List stationIdList, - BusinessOperationDateRangeDTO range) { + private List queryRawReports(List stationIdList, + String startTime, String endTime) { + if (stationIdList != null && stationIdList.isEmpty()) { + return new ArrayList<>(); + } + List reportList = settleOrderReportService.queryOrderReport( + stationIdList, startTime, endTime); + return CollectionUtils.isEmpty(reportList) ? new ArrayList<>() : reportList; + } + + /** + * 从原始报表数据构建汇总指标 + */ + private BusinessOperationSummaryDTO buildSummaryFromReports(List reportList) { + BusinessOperationSummaryDTO summary = new BusinessOperationSummaryDTO(); + if (CollectionUtils.isEmpty(reportList)) { + return summary; + } + for (SettleOrderReport report : reportList) { + summary.setOrderAmount(summary.getOrderAmount().add(BigDecimalUtils.nullToZero(report.getTotalAmount()))); + summary.setServiceAmount(summary.getServiceAmount().add(BigDecimalUtils.nullToZero(report.getServiceAmount()))); + summary.setUseElectricity(summary.getUseElectricity().add(BigDecimalUtils.nullToZero(report.getUseElectricity()))); + summary.setOrderCount(summary.getOrderCount().add(BigDecimalUtils.parseOrZero(report.getChargeNum()))); + summary.setChargeTime(summary.getChargeTime().add(BigDecimalUtils.parseOrZero(report.getChargeTime()))); + } + summary.setAvgServiceFee(BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getOrderCount(), 2)); + summary.setAvgElectricity(BigDecimalUtils.safeDivide(summary.getUseElectricity(), summary.getOrderCount(), 2)); + summary.setAvgChargeTime(BigDecimalUtils.safeDivide(summary.getChargeTime(), summary.getOrderCount(), 2)); + return summary; + } + + /** + * 从原始报表数据构建按日期分组的Map + */ + private Map buildDailyMapFromReports(List reportList) { + Map dailyMap = new LinkedHashMap<>(); + if (CollectionUtils.isEmpty(reportList)) { + return dailyMap; + } + for (SettleOrderReport report : reportList) { + String tradeDate = report.getTradeDate(); + if (StringUtils.isBlank(tradeDate)) { + continue; + } + SettleOrderReport existing = dailyMap.get(tradeDate); + if (existing == null) { + dailyMap.put(tradeDate, report); + } else { + existing.setTotalAmount(existing.getTotalAmount().add( + BigDecimalUtils.nullToZero(report.getTotalAmount()))); + existing.setServiceAmount(existing.getServiceAmount().add( + BigDecimalUtils.nullToZero(report.getServiceAmount()))); + existing.setUseElectricity(existing.getUseElectricity().add( + BigDecimalUtils.nullToZero(report.getUseElectricity()))); + existing.setChargeNum(String.valueOf( + BigDecimalUtils.parseOrZero(existing.getChargeNum()) + .add(BigDecimalUtils.parseOrZero(report.getChargeNum())))); + } + } + return dailyMap; + } + + /** + * 从已有的dailyMap构建曲线图数据(不再重复查DB) + */ + private BusinessScaleChartVO buildScaleChartFromMaps(String metricCode, + BusinessOperationDateRangeDTO range, + Map currentDailyMap, + Map previousDailyMap) { String metricName; String unit; switch (metricCode) { @@ -639,13 +728,6 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { break; } - // 查询当前周期每日数据 - Map currentDailyMap = queryDailyReportMap( - stationIdList, range.getCurrentStart(), range.getCurrentEnd()); - // 查询上周期每日数据 - Map previousDailyMap = queryDailyReportMap( - stationIdList, range.getPreviousStart(), range.getPreviousEnd()); - LocalDate currentStart = LocalDate.parse(range.getCurrentStart()); LocalDate currentEnd = LocalDate.parse(range.getCurrentEnd()); LocalDate previousStart = LocalDate.parse(range.getPreviousStart()); @@ -684,44 +766,6 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService { .build(); } - /** - * 查询每日报表数据,按日期分组返回Map - */ - private Map queryDailyReportMap(List stationIdList, - String startTime, String endTime) { - if (stationIdList != null && stationIdList.isEmpty()) { - return new LinkedHashMap<>(); - } - List reportList = settleOrderReportService.queryOrderReport( - stationIdList, startTime, endTime); - // 按tradeDate分组汇总 - Map dailyMap = new LinkedHashMap<>(); - if (!CollectionUtils.isEmpty(reportList)) { - for (SettleOrderReport report : reportList) { - String tradeDate = report.getTradeDate(); - if (StringUtils.isBlank(tradeDate)) { - continue; - } - SettleOrderReport existing = dailyMap.get(tradeDate); - if (existing == null) { - dailyMap.put(tradeDate, report); - } else { - // 合并同一天多个站点的数据 - existing.setTotalAmount(existing.getTotalAmount().add( - BigDecimalUtils.nullToZero(report.getTotalAmount()))); - existing.setServiceAmount(existing.getServiceAmount().add( - BigDecimalUtils.nullToZero(report.getServiceAmount()))); - existing.setUseElectricity(existing.getUseElectricity().add( - BigDecimalUtils.nullToZero(report.getUseElectricity()))); - existing.setChargeNum(String.valueOf( - BigDecimalUtils.parseOrZero(existing.getChargeNum()) - .add(BigDecimalUtils.parseOrZero(report.getChargeNum())))); - } - } - } - return dailyMap; - } - /** * 从报表记录中提取指定指标的值 */