mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-12 11:19:52 +08:00
新增 查询效率分析接口
This commit is contained in:
@@ -12,10 +12,12 @@ import com.jsowell.pile.dto.business.BusinessEfficiencyQueryDTO;
|
||||
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.BusinessEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessStationRankVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -24,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 运营端财务信息相关Controller
|
||||
*
|
||||
@@ -178,4 +182,60 @@ public class BusinessFinancialController extends BaseController {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询效率分析(单均效率 + 枪均效率)
|
||||
*
|
||||
* @param dto 查询参数
|
||||
* @return 效率分析数据(含单均效率和枪均效率两个维度)
|
||||
*/
|
||||
@PostMapping("/efficiencyAnalysis")
|
||||
public RestApiResponse<?> getEfficiencyAnalysis(@RequestBody BusinessOperationAnalysisQueryDTO dto) {
|
||||
logger.info("查询效率分析 params:{}", JSONObject.toJSONString(dto));
|
||||
RestApiResponse<?> response;
|
||||
try {
|
||||
if (dto == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
BusinessEfficiencyAnalysisVO result = businessFinancialService.getEfficiencyAnalysis(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询场站订单排行
|
||||
*
|
||||
* @param dto 查询参数
|
||||
* @return 场站订单排行列表(按订单数量降序)
|
||||
*/
|
||||
@PostMapping("/stationOrderRank")
|
||||
public RestApiResponse<?> getStationOrderRank(@RequestBody BusinessOperationAnalysisQueryDTO dto) {
|
||||
logger.info("查询场站订单排行 params:{}", JSONObject.toJSONString(dto));
|
||||
RestApiResponse<?> response;
|
||||
try {
|
||||
if (dto == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||
}
|
||||
List<BusinessStationRankVO> result = businessFinancialService.getStationOrderRank(dto);
|
||||
response = new RestApiResponse<>(result);
|
||||
logger.info("查询场站订单排行成功 startTime:{}, endTime:{}, 站点数量:{}",
|
||||
dto.getStartTime(), dto.getEndTime(), result.size());
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@ import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessEfficiencyQueryDTO;
|
||||
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessStationRankVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import com.jsowell.pile.vo.web.ParkingCouponRecordVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface BusinessFinancialService {
|
||||
MerchantOrderReportVO getMyWallet(MerchantOrderReportDTO dto);
|
||||
|
||||
@@ -55,4 +59,20 @@ public interface BusinessFinancialService {
|
||||
* @return 经营效率数据(含指标卡片和曲线图)
|
||||
*/
|
||||
BusinessEfficiencyVO getBusinessEfficiency(BusinessEfficiencyQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 查询场站订单排行
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 场站订单排行列表(按订单数量降序)
|
||||
*/
|
||||
List<BusinessStationRankVO> getStationOrderRank(BusinessOperationAnalysisQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 查询效率分析(单均效率 + 枪均效率)
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 效率分析数据(含单均效率和枪均效率两个维度)
|
||||
*/
|
||||
BusinessEfficiencyAnalysisVO getEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ 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.BusinessEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessEfficiencyVO;
|
||||
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
|
||||
@@ -36,6 +37,7 @@ 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.uniapp.business.BusinessStationRankVO;
|
||||
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
|
||||
import com.jsowell.pile.vo.web.ParkingCouponRecordVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -795,6 +797,182 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询效率分析(单均效率 + 枪均效率)
|
||||
* 单均效率:按订单维度计算平均指标
|
||||
* 枪均效率:按枪口×天数维度计算日均指标
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 效率分析数据
|
||||
*/
|
||||
@Override
|
||||
public BusinessEfficiencyAnalysisVO getEfficiencyAnalysis(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());
|
||||
}
|
||||
|
||||
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
|
||||
|
||||
// 并行查询订单汇总和枪口数量
|
||||
CompletableFuture<BusinessOperationSummaryDTO> summaryFuture = CompletableFuture.supplyAsync(
|
||||
() -> buildSummaryFromReports(
|
||||
queryRawReports(stationIdList, dto.getStartTime(), dto.getEndTime())),
|
||||
FINANCIAL_THREAD_POOL);
|
||||
CompletableFuture<Integer> connectorCountFuture = CompletableFuture.supplyAsync(
|
||||
() -> countConnectors(stationIdList), FINANCIAL_THREAD_POOL);
|
||||
|
||||
BusinessOperationSummaryDTO summary = summaryFuture.join();
|
||||
int connectorCount = connectorCountFuture.join();
|
||||
|
||||
long dayCount = calculateInclusiveDays(dto.getStartTime(), dto.getEndTime());
|
||||
|
||||
// ===== 单均效率指标 =====
|
||||
// 单均服务费 = 服务费总额 / 订单数量
|
||||
BigDecimal avgServiceFeePerOrder = BigDecimalUtils.safeDivide(
|
||||
summary.getServiceAmount(), summary.getOrderCount(), 2);
|
||||
// 单均充电量 = 充电电量总量 / 订单数量
|
||||
BigDecimal avgElectricityPerOrder = BigDecimalUtils.safeDivide(
|
||||
summary.getUseElectricity(), summary.getOrderCount(), 4);
|
||||
// 单均充电时长 = 总充电时长 / 订单数量
|
||||
BigDecimal avgChargeTimeMinutesPerOrder = BigDecimalUtils.safeDivide(
|
||||
summary.getChargeTime(), summary.getOrderCount(), 2);
|
||||
String avgChargeTimePerOrder = formatMinutesOnly(avgChargeTimeMinutesPerOrder);
|
||||
|
||||
// ===== 枪均效率指标 =====
|
||||
BigDecimal connectorCountBd = BigDecimal.valueOf(connectorCount);
|
||||
BigDecimal dayCountBd = BigDecimal.valueOf(dayCount);
|
||||
BigDecimal connectorDayCount = connectorCountBd.multiply(dayCountBd);
|
||||
|
||||
// 枪日均服务费 = 服务费总额 / (枪口数量 × 天数)
|
||||
BigDecimal gunDailyAvgServiceFee = BigDecimalUtils.safeDivide(
|
||||
summary.getServiceAmount(), connectorDayCount, 2);
|
||||
// 枪日均充电量 = 充电电量总量 / (枪口数量 × 天数)
|
||||
BigDecimal gunDailyAvgElectricity = BigDecimalUtils.safeDivide(
|
||||
summary.getUseElectricity(), connectorDayCount, 4);
|
||||
// 枪日均充电时长 = 总充电时长 / (枪口数量 × 天数)
|
||||
BigDecimal gunDailyAvgChargeTimeMinutes = BigDecimalUtils.safeDivide(
|
||||
summary.getChargeTime(), connectorDayCount, 2);
|
||||
String gunDailyAvgChargeTime = formatMinutesToHourMinute(gunDailyAvgChargeTimeMinutes);
|
||||
|
||||
// ===== 共用指标 =====
|
||||
// 度均服务费 = 服务费总额 / 充电电量总量
|
||||
BigDecimal avgServiceFeePerDegree = BigDecimalUtils.safeDivide(
|
||||
summary.getServiceAmount(), summary.getUseElectricity(), 4);
|
||||
// 平均功率 = 充电电量总量 × 60 / 总充电时长(分钟)
|
||||
BigDecimal avgPower = BigDecimalUtils.safeDivide(
|
||||
summary.getUseElectricity().multiply(new BigDecimal("60")),
|
||||
summary.getChargeTime(),
|
||||
4);
|
||||
|
||||
return BusinessEfficiencyAnalysisVO.builder()
|
||||
.startTime(dto.getStartTime())
|
||||
.endTime(dto.getEndTime())
|
||||
.avgServiceFeePerOrder(BigDecimalUtils.normalize(avgServiceFeePerOrder))
|
||||
.avgElectricityPerOrder(BigDecimalUtils.normalize(avgElectricityPerOrder))
|
||||
.avgChargeTimePerOrder(avgChargeTimePerOrder)
|
||||
.gunDailyAvgServiceFee(BigDecimalUtils.normalize(gunDailyAvgServiceFee))
|
||||
.gunDailyAvgElectricity(BigDecimalUtils.normalize(gunDailyAvgElectricity))
|
||||
.gunDailyAvgChargeTime(gunDailyAvgChargeTime)
|
||||
.avgServiceFeePerDegree(BigDecimalUtils.normalize(avgServiceFeePerDegree))
|
||||
.avgPower(BigDecimalUtils.normalize(avgPower))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分钟数格式化为xxm格式(用于单均充电时长)
|
||||
*
|
||||
* @param minutes 分钟数
|
||||
* @return 格式化结果
|
||||
*/
|
||||
private String formatMinutesOnly(BigDecimal minutes) {
|
||||
long totalMinutes = BigDecimalUtils.nullToZero(minutes)
|
||||
.setScale(0, RoundingMode.HALF_UP)
|
||||
.longValue();
|
||||
return totalMinutes + "m";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询场站订单排行
|
||||
* 按站点分组统计订单数量,按订单数量降序排列
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @return 场站订单排行列表
|
||||
*/
|
||||
@Override
|
||||
public List<BusinessStationRankVO> getStationOrderRank(BusinessOperationAnalysisQueryDTO dto) {
|
||||
if (dto == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
|
||||
|
||||
// 查询结算报表原始数据
|
||||
List<SettleOrderReport> reportList = queryRawReports(
|
||||
stationIdList, dto.getStartTime(), dto.getEndTime());
|
||||
|
||||
if (CollectionUtils.isEmpty(reportList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 按站点ID分组,累加订单数量
|
||||
Map<String, Integer> stationOrderCountMap = new LinkedHashMap<>();
|
||||
for (SettleOrderReport report : reportList) {
|
||||
String sId = report.getStationId();
|
||||
if (StringUtils.isBlank(sId)) {
|
||||
continue;
|
||||
}
|
||||
int chargeNum = BigDecimalUtils.parseOrZero(report.getChargeNum()).intValue();
|
||||
stationOrderCountMap.merge(sId, chargeNum, Integer::sum);
|
||||
}
|
||||
|
||||
// 批量查询站点名称
|
||||
Map<String, String> stationNameMap = new LinkedHashMap<>();
|
||||
for (String sId : stationOrderCountMap.keySet()) {
|
||||
try {
|
||||
PileStationInfo stationInfo = pileStationInfoService.selectPileStationInfoById(Long.valueOf(sId));
|
||||
if (stationInfo != null) {
|
||||
stationNameMap.put(sId, stationInfo.getStationName());
|
||||
} else {
|
||||
stationNameMap.put(sId, "未知站点");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询站点名称失败 stationId:{}", sId, e);
|
||||
stationNameMap.put(sId, "未知站点");
|
||||
}
|
||||
}
|
||||
|
||||
// 按订单数量降序排列并构建排行列表
|
||||
List<BusinessStationRankVO> rankList = stationOrderCountMap.entrySet().stream()
|
||||
.map(entry -> BusinessStationRankVO.builder()
|
||||
.stationId(entry.getKey())
|
||||
.stationName(stationNameMap.getOrDefault(entry.getKey(), "未知站点"))
|
||||
.orderCount(entry.getValue())
|
||||
.build())
|
||||
.sorted((a, b) -> Integer.compare(
|
||||
b.getOrderCount() != null ? b.getOrderCount() : 0,
|
||||
a.getOrderCount() != null ? a.getOrderCount() : 0))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 设置排行名次
|
||||
for (int i = 0; i < rankList.size(); i++) {
|
||||
rankList.get(i).setRank(i + 1);
|
||||
}
|
||||
|
||||
return rankList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询经营效率
|
||||
* 优化:当前周期和上周期汇总并行查询,枪口数量与总额定功率并行查询
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
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 Lemon
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessEfficiencyAnalysisVO {
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
// ========== 单均效率指标 ==========
|
||||
|
||||
/**
|
||||
* 单均服务费(元)
|
||||
* = 服务费总额 / 订单数量
|
||||
*/
|
||||
private BigDecimal avgServiceFeePerOrder;
|
||||
|
||||
/**
|
||||
* 单均充电量(度)
|
||||
* = 充电电量总量 / 订单数量
|
||||
*/
|
||||
private BigDecimal avgElectricityPerOrder;
|
||||
|
||||
/**
|
||||
* 单均充电时长,格式:xxm
|
||||
* = 总充电时长(分钟) / 订单数量
|
||||
*/
|
||||
private String avgChargeTimePerOrder;
|
||||
|
||||
// ========== 枪均效率指标 ==========
|
||||
|
||||
/**
|
||||
* 枪日均服务费(元)
|
||||
* = 服务费总额 / (枪口数量 × 天数)
|
||||
*/
|
||||
private BigDecimal gunDailyAvgServiceFee;
|
||||
|
||||
/**
|
||||
* 枪日均充电量(度)
|
||||
* = 充电电量总量 / (枪口数量 × 天数)
|
||||
*/
|
||||
private BigDecimal gunDailyAvgElectricity;
|
||||
|
||||
/**
|
||||
* 枪日均充电时长,格式:xxhxxm
|
||||
* = 总充电时长(分钟) / (枪口数量 × 天数)
|
||||
*/
|
||||
private String gunDailyAvgChargeTime;
|
||||
|
||||
// ========== 共用指标 ==========
|
||||
|
||||
/**
|
||||
* 度均服务费(元)
|
||||
* = 服务费总额 / 充电电量总量
|
||||
*/
|
||||
private BigDecimal avgServiceFeePerDegree;
|
||||
|
||||
/**
|
||||
* 平均功率(KW)
|
||||
* = 充电电量总量 × 60 / 总充电时长(分钟)
|
||||
*/
|
||||
private BigDecimal avgPower;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.jsowell.pile.vo.uniapp.business;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 场站排行VO
|
||||
*
|
||||
* @author zhangziao
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class BusinessStationRankVO {
|
||||
|
||||
/**
|
||||
* 排行名次
|
||||
*/
|
||||
private Integer rank;
|
||||
|
||||
/**
|
||||
* 站点ID
|
||||
*/
|
||||
private String stationId;
|
||||
|
||||
/**
|
||||
* 场站名称
|
||||
*/
|
||||
private String stationName;
|
||||
|
||||
/**
|
||||
* 订单总数
|
||||
*/
|
||||
private Integer orderCount;
|
||||
}
|
||||
Reference in New Issue
Block a user