Bläddra i källkod

feat(charging): 增加时段明细支持并优化充电费用计算

- 在AppChargingCostVO中新增chargeDetails字段,用于跨时段充电明细展示
- 修改AppletHomeServiceImpl,增加充电费用中运营费的计算逻辑
- 修正ChargeOrderInfoServiceImpl中查询订单的字段由id改为chargeOrderNo
- 更新ThirdPartyStationInfoMapper.xml,新增charge_details字段映射
SheepHy 2 dagar sedan
förälder
incheckning
8ece6e1523

+ 3 - 0
src/main/java/com/zsElectric/boot/business/model/vo/applet/AppChargingCostVO.java

@@ -75,4 +75,7 @@ public class AppChargingCostVO implements Serializable {
 
     @Schema(description = "充电接口编码")
     private String connectorCode;
+
+    @Schema(description = "时段明细JSON(跨时段充电时有值)")
+    private String chargeDetails;
 }

+ 213 - 1
src/main/java/com/zsElectric/boot/business/service/impl/AppletHomeServiceImpl.java

@@ -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();
+    }
+
 
 }

+ 1 - 1
src/main/java/com/zsElectric/boot/business/service/impl/ChargeOrderInfoServiceImpl.java

@@ -255,7 +255,7 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
             AppChargeVO appInvokeChargeVO = new AppChargeVO();
 
             //订单
-            ChargeOrderInfo chargeOrderInfo = this.getOne(Wrappers.lambdaQuery(ChargeOrderInfo.class).eq(ChargeOrderInfo::getId,
+            ChargeOrderInfo chargeOrderInfo = this.getOne(Wrappers.lambdaQuery(ChargeOrderInfo.class).eq(ChargeOrderInfo::getChargeOrderNo,
                     formData.getChargeOrderNo()).last("limit 1"));
             if(ObjectUtil.isEmpty(chargeOrderInfo)){
                 throw new BusinessException("订单不存在");

+ 1 - 0
src/main/resources/mapper/business/ThirdPartyStationInfoMapper.xml

@@ -591,6 +591,7 @@
             IFNULL(tcs.elec_money, 0) AS elec_money,
             IFNULL(tcs.service_money, 0) AS service_money,
             IFNULL(tcs.total_money, 0) AS total_money,
+            tcs.charge_details,
             tcs.soc,
             COALESCE(tcs.current_a, tcs.current_b, tcs.current_c) AS `current`,
             COALESCE(tcs.voltage_a, tcs.voltage_b, tcs.voltage_c) AS voltage,