|
|
@@ -21,6 +21,7 @@ import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
|
|
|
import com.zsElectric.boot.security.util.SecurityUtils;
|
|
|
import com.zsElectric.boot.system.mapper.DictItemMapper;
|
|
|
import com.zsElectric.boot.system.model.entity.DictItem;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
@@ -692,7 +693,15 @@ public class AppletHomeServiceImpl implements AppletHomeService {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- return thirdPartyStationInfoMapper.selectCurrentChargingCost(userId);
|
|
|
+ AppChargingCostVO result = thirdPartyStationInfoMapper.selectCurrentChargingCost(userId);
|
|
|
+ if (result == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算服务费和总金额(加上运营费)
|
|
|
+ calculateChargingCostWithOpFee(result);
|
|
|
+
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -992,5 +1001,208 @@ public class AppletHomeServiceImpl implements AppletHomeService {
|
|
|
return availableAmount;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 计算充电费用(含运营费)
|
|
|
+ * 根据 charge_details 是否有数据决定计算方式:
|
|
|
+ * 1. 有 charge_details:按照时段明细计算,不同时段使用对应的 op_fee
|
|
|
+ * 2. 没有 charge_details:直接使用当前时段的 op_fee 计算
|
|
|
+ *
|
|
|
+ * @param costVO 充电费用VO
|
|
|
+ */
|
|
|
+ private void calculateChargingCostWithOpFee(AppChargingCostVO costVO) {
|
|
|
+ try {
|
|
|
+ // 获取充电站ID(通过 connectorCode 查询)
|
|
|
+ ThirdPartyConnectorInfo connector = thirdPartyConnectorInfoMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ThirdPartyConnectorInfo>()
|
|
|
+ .eq(ThirdPartyConnectorInfo::getConnectorId, costVO.getConnectorCode())
|
|
|
+ .eq(ThirdPartyConnectorInfo::getIsDeleted, 0)
|
|
|
+ );
|
|
|
+ if (connector == null) {
|
|
|
+ log.warn("未找到充电接口信息 - connectorCode: {}", costVO.getConnectorCode());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ThirdPartyStationInfo station = thirdPartyStationInfoMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ThirdPartyStationInfo>()
|
|
|
+ .eq(ThirdPartyStationInfo::getStationId, connector.getStationId())
|
|
|
+ .eq(ThirdPartyStationInfo::getIsDeleted, 0)
|
|
|
+ );
|
|
|
+ if (station == null) {
|
|
|
+ log.warn("未找到充电站信息 - stationId: {}", connector.getStationId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal opFeeCost = BigDecimal.ZERO;
|
|
|
+ String chargeDetails = costVO.getChargeDetails();
|
|
|
+
|
|
|
+ // 判断是否有时段明细
|
|
|
+ if (StrUtil.isNotBlank(chargeDetails)) {
|
|
|
+ // 有时段明细,按照时段计算运营费
|
|
|
+ opFeeCost = calculateOpFeeByCrossPeriod(station.getId(), chargeDetails);
|
|
|
+ } else {
|
|
|
+ // 没有时段明细,使用当前时段计算
|
|
|
+ opFeeCost = calculateOpFeeByCurrentPeriod(station.getId(), costVO.getTotalPower());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新服务费和总金额
|
|
|
+ BigDecimal serviceMoney = costVO.getServiceMoney();
|
|
|
+ if (serviceMoney == null) {
|
|
|
+ serviceMoney = BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ costVO.setServiceMoney(serviceMoney.add(opFeeCost));
|
|
|
+
|
|
|
+ BigDecimal totalMoney = costVO.getTotalMoney();
|
|
|
+ if (totalMoney == null) {
|
|
|
+ totalMoney = BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ costVO.setTotalMoney(totalMoney.add(opFeeCost));
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("计算充电费用失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据时段明细计算运营费(跨时段充电)
|
|
|
+ * chargeDetails 格式:[{DetailStartTime, DetailEndTime, ElecPrice, SevicePrice, DetailPower, DetailElecMoney, DetailSeviceMoney}]
|
|
|
+ */
|
|
|
+ private BigDecimal calculateOpFeeByCrossPeriod(Long stationId, String chargeDetails) {
|
|
|
+ try {
|
|
|
+ // 解析 JSON
|
|
|
+ com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
|
|
+ com.fasterxml.jackson.databind.JsonNode detailsArray = objectMapper.readTree(chargeDetails);
|
|
|
+
|
|
|
+ if (!detailsArray.isArray()) {
|
|
|
+ log.warn("时段明细格式错误 - chargeDetails: {}", chargeDetails);
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal totalOpFeeCost = BigDecimal.ZERO;
|
|
|
+
|
|
|
+ // 遍历每个时段
|
|
|
+ for (com.fasterxml.jackson.databind.JsonNode detail : detailsArray) {
|
|
|
+ String startTime = detail.has("DetailStartTime") ? detail.get("DetailStartTime").asText() : null;
|
|
|
+ BigDecimal detailPower = detail.has("DetailPower") ? new BigDecimal(detail.get("DetailPower").asText()) : BigDecimal.ZERO;
|
|
|
+
|
|
|
+ if (startTime == null || detailPower.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据时间查询对应的时段 period_flag
|
|
|
+ String timeHHmmss = startTime.length() >= 6 ? startTime.substring(0, 6) : startTime;
|
|
|
+ Integer periodFlag = getPeriodFlagByTime(stationId, timeHHmmss);
|
|
|
+
|
|
|
+ // 查询该时段的运营费
|
|
|
+ BigDecimal opFee = getOpFeeByPeriodFlag(stationId, periodFlag);
|
|
|
+
|
|
|
+ // 计算该时段的运营费
|
|
|
+ BigDecimal periodOpFeeCost = detailPower.multiply(opFee);
|
|
|
+ totalOpFeeCost = totalOpFeeCost.add(periodOpFeeCost);
|
|
|
+
|
|
|
+ log.debug("时段运营费 - startTime: {}, periodFlag: {}, detailPower: {}, opFee: {}, cost: {}",
|
|
|
+ startTime, periodFlag, detailPower, opFee, periodOpFeeCost);
|
|
|
+ }
|
|
|
+
|
|
|
+ return totalOpFeeCost.setScale(2, RoundingMode.HALF_UP);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("解析时段明细失败 - chargeDetails: {}", chargeDetails, e);
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据当前时段计算运营费(非跨时段充电)
|
|
|
+ */
|
|
|
+ private BigDecimal calculateOpFeeByCurrentPeriod(Long stationId, BigDecimal totalPower) {
|
|
|
+ if (totalPower == null || totalPower.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前时间
|
|
|
+ String currentTime = LocalTime.now().format(TIME_FORMATTER);
|
|
|
+
|
|
|
+ // 获取当前时段标志
|
|
|
+ Integer periodFlag = getPeriodFlagByTime(stationId, currentTime);
|
|
|
+
|
|
|
+ // 查询该时段的运营费
|
|
|
+ BigDecimal opFee = getOpFeeByPeriodFlag(stationId, periodFlag);
|
|
|
+
|
|
|
+ // 计算运营费
|
|
|
+ BigDecimal opFeeCost = totalPower.multiply(opFee);
|
|
|
+
|
|
|
+ log.debug("当前时段运营费 - periodFlag: {}, totalPower: {}, opFee: {}, cost: {}",
|
|
|
+ periodFlag, totalPower, opFee, opFeeCost);
|
|
|
+
|
|
|
+ return opFeeCost.setScale(2, RoundingMode.HALF_UP);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据时间获取时段标志
|
|
|
+ */
|
|
|
+ private Integer getPeriodFlagByTime(Long stationId, String timeHHmmss) {
|
|
|
+ // 查询充电接口
|
|
|
+ ThirdPartyConnectorInfo connector = thirdPartyConnectorInfoMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ThirdPartyConnectorInfo>()
|
|
|
+ .eq(ThirdPartyConnectorInfo::getStationId,
|
|
|
+ thirdPartyStationInfoMapper.selectById(stationId).getStationId())
|
|
|
+ .eq(ThirdPartyConnectorInfo::getIsDeleted, 0)
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+ if (connector == null) {
|
|
|
+ log.warn("未找到充电接口 - stationId: {}", stationId);
|
|
|
+ return 3; // 默认平时段
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询价格策略
|
|
|
+ ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ThirdPartyEquipmentPricePolicy>()
|
|
|
+ .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, connector.getConnectorId())
|
|
|
+ .eq(ThirdPartyEquipmentPricePolicy::getIsDeleted, 0)
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+ if (pricePolicy == null) {
|
|
|
+ log.warn("未找到价格策略 - connectorId: {}", connector.getConnectorId());
|
|
|
+ return 3; // 默认平时段
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询当前时间对应的时段
|
|
|
+ ThirdPartyPolicyInfo policyInfo = thirdPartyPolicyInfoMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ThirdPartyPolicyInfo>()
|
|
|
+ .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
|
|
|
+ .eq(ThirdPartyPolicyInfo::getIsDeleted, 0)
|
|
|
+ .le(ThirdPartyPolicyInfo::getStartTime, timeHHmmss)
|
|
|
+ .orderByDesc(ThirdPartyPolicyInfo::getStartTime)
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+
|
|
|
+ if (policyInfo == null || policyInfo.getPeriodFlag() == null) {
|
|
|
+ log.warn("未找到时段信息 - pricePolicyId: {}, time: {}", pricePolicy.getId(), timeHHmmss);
|
|
|
+ return 3; // 默认平时段
|
|
|
+ }
|
|
|
+
|
|
|
+ return policyInfo.getPeriodFlag();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据时段标志获取运营费
|
|
|
+ */
|
|
|
+ private BigDecimal getOpFeeByPeriodFlag(Long stationId, Integer periodFlag) {
|
|
|
+ PolicyFee policyFee = policyFeeMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<PolicyFee>()
|
|
|
+ .eq(PolicyFee::getStationInfoId, stationId)
|
|
|
+ .eq(PolicyFee::getPeriodFlag, periodFlag)
|
|
|
+ .eq(PolicyFee::getSalesType, 0) // 平台价格
|
|
|
+ .eq(PolicyFee::getIsDeleted, 0)
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+
|
|
|
+ if (policyFee == null || policyFee.getOpFee() == null) {
|
|
|
+ log.warn("未找到运营费配置 - stationId: {}, periodFlag: {}", stationId, periodFlag);
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ return policyFee.getOpFee();
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
}
|