Prechádzať zdrojové kódy

refactor(charging): 优化充电接口详情使用接口编码替代ID

- 将AppletHomeService及实现中充电接口详情查询参数由connectorId(Long)改为connectorCode(String)
- 更新相关Controller接口请求参数类型及调用逻辑
- 调整MyBatis映射文件及SQL语句使用connectorCode查询
- 修正部分SQL关联查询中时间字段匹配规则,改用period_flag字段
- 取消stationInfoMapper查询中的固定设备所属方限制及政策配置校验
- 充电状态接收服务增强充电状态更新逻辑,实时更新订单消费字段和状态
- 新增根据充电时段与用户企业信息计算平台应收金额逻辑,支持跨时段计费
- 熔断机制改进,基于账户余额减实时消费与安全阈值判断,余额不足时触发系统自动停止充电
- 添加并完善熔断锁机制,防止并发重复执行
- ThirdPartyChargingService中添加对policy_fee表无数据时直接返回空结果的前置判断
- 完善异常捕获与日志,提升系统鲁棒性和可维护性
- 调整了application-dev.yml与application-prod.yml中第三方充电平台接口的安全排除URL列表,增加多项接口支持
SheepHy 3 dní pred
rodič
commit
f05883398b

+ 2 - 3
src/main/java/com/zsElectric/boot/business/controller/applet/AppletStationController.java

@@ -102,12 +102,11 @@ public class AppletStationController {
     /**
      * 获取充电设备接口详情
      *
-     * @param connectorId 充电设备接口ID
      * @return 设备接口详情
      */
     @Operation(summary = "获取充电设备接口详情")
     @GetMapping("/connector/detail")
-    public Result<AppletConnectorDetailVO> getConnectorDetail(@RequestParam Long connectorId) {
-        return Result.success(appletHomeService.getConnectorDetail(connectorId));
+    public Result<AppletConnectorDetailVO> getConnectorDetail(@RequestParam String connectorCode) {
+        return Result.success(appletHomeService.getConnectorDetail(connectorCode));
     }
 }

+ 1 - 2
src/main/java/com/zsElectric/boot/business/service/AppletHomeService.java

@@ -76,10 +76,9 @@ public interface AppletHomeService {
 
     /**
      * 获取充电设备接口详情
-     * @param connectorId 充电设备接口ID
      * @return 设备接口详情
      */
-    AppletConnectorDetailVO getConnectorDetail(Long connectorId);
+    AppletConnectorDetailVO getConnectorDetail(String connectorCode);
 
     /**
      * 计算当前用户可用充电金额

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

@@ -694,7 +694,7 @@ public class AppletHomeServiceImpl implements AppletHomeService {
     }
 
     @Override
-    public AppletConnectorDetailVO getConnectorDetail(Long connectorId) {
+    public AppletConnectorDetailVO getConnectorDetail(String connectorCode) {
         // 获取当前登录用户ID
         Long userId = SecurityUtils.getUserId();
 
@@ -748,11 +748,11 @@ public class AppletHomeServiceImpl implements AppletHomeService {
 
         // 调用Mapper方法,一次SQL查询获取所有信息
         AppletConnectorDetailVO result = thirdPartyConnectorInfoMapper.selectConnectorDetailById(
-                connectorId, userId, currentTime, userBalance, newUserDiscount
+                connectorCode, userId, currentTime, userBalance, newUserDiscount
         );
 
         if (result == null) {
-            log.warn("充电接口不存在,connectorId: {}", connectorId);
+            log.warn("充电接口不存在,connectorId: {}", connectorCode);
         }
 
         return result;

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

@@ -497,7 +497,14 @@ public class ThirdPartyChargingServiceImpl implements ThirdPartyChargingService
     public IPage<ThirdPartyStationInfoVO> getStationInfoPageByEquipment(ThirdPartyStationInfoQuery queryParams) {
         // 构建分页
         Page<ThirdPartyStationInfoVO> page = new Page<>(queryParams.getPageNum(), queryParams.getPageSize());
-        // 调用Mapper联表查询(固定设备所属方MA6DP6BE7)
+        
+        // 前置检查:如果c_policy_fee表没有数据,直接返回空结果
+        Long policyFeeCount = policyFeeMapper.selectCount(null);
+        if (policyFeeCount == null || policyFeeCount == 0) {
+            return page;
+        }
+        
+        // 调用Mapper联表查询
         IPage<ThirdPartyStationInfoVO> resultPage = stationInfoMapper.selectStationInfoPageByEquipment(page, queryParams);
         
         // 填充单位名称

+ 1 - 2
src/main/java/com/zsElectric/boot/charging/mapper/ThirdPartyConnectorInfoMapper.java

@@ -20,7 +20,6 @@ public interface ThirdPartyConnectorInfoMapper extends BaseMapper<ThirdPartyConn
     /**
      * 获取充电接口详情(包含基本信息、价格信息、企业价格)
      *
-     * @param connectorId 充电接口ID
      * @param userId 用户ID
      * @param currentTime 当前时间 HHmmss格式
      * @param userBalance 用户余额
@@ -28,7 +27,7 @@ public interface ThirdPartyConnectorInfoMapper extends BaseMapper<ThirdPartyConn
      * @return 充电接口详情
      */
     AppletConnectorDetailVO selectConnectorDetailById(
-            @Param("connectorId") Long connectorId,
+            @Param("connectorCode") String connectorCode,
             @Param("userId") Long userId,
             @Param("currentTime") String currentTime,
             @Param("userBalance") BigDecimal userBalance,

+ 11 - 0
src/main/java/com/zsElectric/boot/charging/mapper/ThirdPartyPolicyInfoMapper.java

@@ -33,4 +33,15 @@ public interface ThirdPartyPolicyInfoMapper extends BaseMapper<ThirdPartyPolicyI
      */
     ThirdPartyPolicyInfo selectElecAndServicePriceByStationAndPeriodFlag(@Param("stationInfoId") Long stationInfoId,
                                                                           @Param("periodFlag") Integer periodFlag);
+    
+    /**
+     * 根据stationInfoId和当前时间查询当前时段的价格策略
+     * 按 start_time <= currentTime 排序取最新的(即当前时段)
+     *
+     * @param stationInfoId 站点信息ID
+     * @param currentTime   当前时间(HHmm或HHmmss格式)
+     * @return 当前时段的价格策略明细(包含period_flag, elec_price, service_price)
+     */
+    ThirdPartyPolicyInfo selectCurrentPeriodPolicyByStation(@Param("stationInfoId") Long stationInfoId,
+                                                             @Param("currentTime") String currentTime);
 }

+ 225 - 82
src/main/java/com/zsElectric/boot/charging/service/impl/ChargingReceptionServiceImpl.java

@@ -14,6 +14,8 @@ import com.zsElectric.boot.business.service.ChargeOrderInfoService;
 import com.zsElectric.boot.charging.entity.*;
 import com.zsElectric.boot.charging.mapper.ThirdPartyChargeStatusMapper;
 import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper;
+import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
+import com.zsElectric.boot.charging.service.ChargingBusinessService;
 import com.zsElectric.boot.charging.service.ChargingReceptionService;
 import com.zsElectric.boot.charging.vo.ChargeResponseVO;
 import com.zsElectric.boot.charging.vo.QueryStationStatusVO;
@@ -69,6 +71,9 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
     private final PolicyFeeMapper policyFeeMapper;
     private final DictItemMapper dictItemMapper;
     private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper;
+    private final ChargingBusinessService chargingBusinessService;
+    private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper;
+    private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper;
 
 
     /**
@@ -325,46 +330,66 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
         try {
             log.info("保存或更新充电状态数据 - StartChargeSeq: {}", jsonNode);
             String startChargeSeq = jsonNode.get("StartChargeSeq").asText();
-//            //修改订单状态
-//            ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
-//                    .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("limit 1"));
-//            if(ObjUtil.isNotEmpty(chargeOrderInfo)){
-//                Integer connectorStatus = getIntValue(jsonNode, "ConnectorStatus");
-//                if (Objects.equals(connectorStatus, SystemConstants.STATUS_THREE) && Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ZERO)) {
-//                    // 充电中
-//                    log.info("充电中 - StartChargeSeq: {}", startChargeSeq);
-//                    chargeOrderInfo.setStatus(SystemConstants.STATUS_ONE);
-//                    chargeOrderInfoService.updateById(chargeOrderInfo);
-//                }
-//                if (Objects.equals(connectorStatus, SystemConstants.STATUS_FOUR) && Objects.equals(chargeOrderInfo.getStatus(),
-//                        SystemConstants.STATUS_ONE)) {
-//                    // 结算中
-//                    log.info("结算中 - StartChargeSeq: {}", startChargeSeq);
-//                    chargeOrderInfo.setStatus(SystemConstants.STATUS_TWO);
-//                    chargeOrderInfoService.updateById(chargeOrderInfo);
-//                }
-//            }
-
-            // 查询是否已存在该订单
-//            ThirdPartyChargeStatus existing = chargeStatusMapper.selectOne(
-//                    Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
-//                            .eq(ThirdPartyChargeStatus::getStartChargeSeq, startChargeSeq)
-//            );
-
-//            ThirdPartyChargeStatus chargeStatus = (existing != null) ? existing : new ThirdPartyChargeStatus();
-            ThirdPartyChargeStatus chargeStatus = new ThirdPartyChargeStatus();
+            String connectorId = getTextValue(jsonNode, "ConnectorID");
+            
+            // 获取第三方推送的实时数据
+            BigDecimal totalPower = getDecimalValue(jsonNode, "TotalPower");  // 实际充电度数
+            BigDecimal totalMoney = getDecimalValue(jsonNode, "TotalMoney");  // 第三方总费用
+            BigDecimal elecMoney = getDecimalValue(jsonNode, "ElecMoney");    // 第三方电费
+            BigDecimal serviceMoney = getDecimalValue(jsonNode, "SeviceMoney"); // 第三方服务费
+            
+            // 修改订单状态并实时更新消费字段
+            ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
+                    .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("limit 1"));
+            if(ObjUtil.isNotEmpty(chargeOrderInfo)){
+                Integer connectorStatus = getIntValue(jsonNode, "ConnectorStatus");
+                
+                // 实时更新订单表的消费字段
+                chargeOrderInfo.setTotalCharge(totalPower);                    // 实际充电度数
+                chargeOrderInfo.setThirdPartyTotalCost(totalMoney);            // 第三方充电消费总额
+                chargeOrderInfo.setThirdPartyElecfee(elecMoney);               // 第三方充电金额
+                chargeOrderInfo.setThirdPartyServerfee(serviceMoney);          // 第三方充电服务费
+                
+                // 计算平台实际收取金额(根据度数和平台价格策略计算)
+                BigDecimal realCost = calculateRealCost(chargeOrderInfo, totalPower);
+                chargeOrderInfo.setRealCost(realCost);
+                
+                if (Objects.equals(connectorStatus, SystemConstants.STATUS_THREE) && Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ZERO)) {
+                    // 充电中
+                    log.info("充电中 - StartChargeSeq: {}", startChargeSeq);
+                    chargeOrderInfo.setStatus(SystemConstants.STATUS_ONE);
+                }
+                if (Objects.equals(connectorStatus, SystemConstants.STATUS_FOUR) && Objects.equals(chargeOrderInfo.getStatus(),
+                        SystemConstants.STATUS_ONE)) {
+                    // 结算中
+                    log.info("结算中 - StartChargeSeq: {}", startChargeSeq);
+                    chargeOrderInfo.setStatus(SystemConstants.STATUS_TWO);
+                }
+                
+                chargeOrderInfoService.updateById(chargeOrderInfo);
+                log.info("实时更新订单消费 - startChargeSeq: {}, totalPower: {}, realCost: {}", 
+                        startChargeSeq, totalPower, realCost);
+            }
+
+            // 查询是否已存在该充电状态记录
+            ThirdPartyChargeStatus existing = chargeStatusMapper.selectOne(
+                    Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
+                            .eq(ThirdPartyChargeStatus::getStartChargeSeq, startChargeSeq)
+            );
+
+            ThirdPartyChargeStatus chargeStatus = (existing != null) ? existing : new ThirdPartyChargeStatus();
 
             // 设置字段值
             chargeStatus.setStartChargeSeq(startChargeSeq);
-            chargeStatus.setConnectorId(getTextValue(jsonNode, "ConnectorID"));
+            chargeStatus.setConnectorId(connectorId);
             chargeStatus.setConnectorStatus(getIntValue(jsonNode, "ConnectorStatus"));
             chargeStatus.setStartChargeSeqStat(getIntValue(jsonNode, "StartChargeSeqStat"));
             chargeStatus.setStartTime(parseDateTime(getTextValue(jsonNode, "StartTime")));
             chargeStatus.setEndTime(parseDateTime(getTextValue(jsonNode, "EndTime")));
-            chargeStatus.setTotalPower(getDecimalValue(jsonNode, "TotalPower"));
-            chargeStatus.setTotalMoney(getDecimalValue(jsonNode, "TotalMoney"));
-            chargeStatus.setElecMoney(getDecimalValue(jsonNode, "ElecMoney"));
-            chargeStatus.setServiceMoney(getDecimalValue(jsonNode, "SeviceMoney")); // 注意:第三方字段名SeviceMoney
+            chargeStatus.setTotalPower(totalPower);
+            chargeStatus.setTotalMoney(totalMoney);
+            chargeStatus.setElecMoney(elecMoney);
+            chargeStatus.setServiceMoney(serviceMoney);
             chargeStatus.setSoc(getIntValue(jsonNode, "Soc"));
             chargeStatus.setVoltageA(getDecimalValue(jsonNode, "VoltageA"));
             chargeStatus.setVoltageB(getDecimalValue(jsonNode, "VoltageB"));
@@ -378,25 +403,111 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
                 chargeStatus.setChargeDetails(jsonNode.get("ChargeDetails").toString());
             }
 
-//            if (existing != null) {
-//                chargeStatus.setUpdateTime(LocalDateTime.now());
-//                chargeStatusMapper.updateById(chargeStatus);
-//                log.info("更新充电状态成功 - startChargeSeq: {}", startChargeSeq);
-//            } else {
-//                chargeStatus.setCreateTime(LocalDateTime.now());
-//                chargeStatusMapper.insert(chargeStatus);
-//                log.info("新增充电状态成功 - startChargeSeq: {}", startChargeSeq);
-//            }
-            chargeStatus.setCreateTime(LocalDateTime.now());
-            chargeStatusMapper.insert(chargeStatus);
-            log.info("新增充电状态成功 - startChargeSeq: {}", startChargeSeq);
-
-            //熔断保护
-            isNeedBreak(chargeStatus);
+            if (existing != null) {
+                chargeStatus.setUpdateTime(LocalDateTime.now());
+                chargeStatusMapper.updateById(chargeStatus);
+                log.info("更新充电状态成功 - startChargeSeq: {}", startChargeSeq);
+            } else {
+                chargeStatus.setCreateTime(LocalDateTime.now());
+                chargeStatusMapper.insert(chargeStatus);
+                log.info("新增充电状态成功 - startChargeSeq: {}", startChargeSeq);
+            }
+
+            // 熔断保护 - 余额不足判断
+            isNeedBreak(chargeStatus, chargeOrderInfo);
         } catch (Exception e) {
             log.error("保存充电状态数据失败", e);
         }
     }
+    
+    /**
+     * 计算平台实际收取金额
+     * 根据充电度数 * 当前时段的平台价格策略计算(支持跨时段计费)
+     */
+    private BigDecimal calculateRealCost(ChargeOrderInfo chargeOrderInfo, BigDecimal totalPower) {
+        if (totalPower == null || totalPower.compareTo(BigDecimal.ZERO) <= 0) {
+            return BigDecimal.ZERO;
+        }
+        
+        try {
+            // 获取用户企业信息
+            UserFirm userFirm = userFirmMapper.selectOne(Wrappers.<UserFirm>lambdaQuery()
+                    .eq(UserFirm::getUserId, chargeOrderInfo.getUserId())
+                    .eq(UserFirm::getIsDeleted, 0));
+            
+            // 获取设备信息
+            ThirdPartyEquipmentInfo equipmentInfo = thirdPartyEquipmentInfoMapper.selectOne(
+                    Wrappers.<ThirdPartyEquipmentInfo>lambdaQuery()
+                            .eq(ThirdPartyEquipmentInfo::getEquipmentId, chargeOrderInfo.getEquipmentId())
+                            .eq(ThirdPartyEquipmentInfo::getIsDeleted, 0));
+            
+            if (equipmentInfo == null) {
+                log.warn("未找到设备信息 - equipmentId: {}", chargeOrderInfo.getEquipmentId());
+                return BigDecimal.ZERO;
+            }
+            
+            // 获取站点信息
+            ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
+                    Wrappers.<ThirdPartyStationInfo>lambdaQuery()
+                            .eq(ThirdPartyStationInfo::getStationId, equipmentInfo.getStationId())
+                            .eq(ThirdPartyStationInfo::getIsDeleted, 0));
+            
+            if (stationInfo == null) {
+                log.warn("未找到站点信息 - stationId: {}", equipmentInfo.getStationId());
+                return BigDecimal.ZERO;
+            }
+            
+            // 获取当前时间,格式为 HHmmss
+            String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss"));
+            
+            // 根据当前时间查询当前时段的价格策略(核心:支持跨时段计费)
+            ThirdPartyPolicyInfo currentPeriodPolicy = thirdPartyPolicyInfoMapper
+                    .selectCurrentPeriodPolicyByStation(stationInfo.getId(), currentTime);
+            
+            if (currentPeriodPolicy == null || currentPeriodPolicy.getPeriodFlag() == null) {
+                log.warn("未找到当前时段价格策略 - stationInfoId: {}, currentTime: {}", 
+                        stationInfo.getId(), currentTime);
+                return BigDecimal.ZERO;
+            }
+            
+            Integer currentPeriodFlag = currentPeriodPolicy.getPeriodFlag();
+            log.info("当前时段 - stationInfoId: {}, currentTime: {}, periodFlag: {}", 
+                    stationInfo.getId(), currentTime, currentPeriodFlag);
+            
+            // 查询当前时段对应的价格策略
+            Integer salesType = (userFirm != null) ? 1 : 0;  // 1-企业 0-平台
+            Long firmId = (userFirm != null) ? userFirm.getFirmId() : null;
+            
+            PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
+                    .eq(PolicyFee::getStationInfoId, stationInfo.getId())
+                    .eq(PolicyFee::getPeriodFlag, currentPeriodFlag)
+                    .eq(PolicyFee::getSalesType, salesType)
+                    .eq(salesType == 1, PolicyFee::getFirmId, firmId)
+                    .eq(PolicyFee::getIsDeleted, 0)
+                    .last("LIMIT 1"));
+            
+            if (policyFee == null) {
+                log.warn("未找到当前时段的价格策略 - stationInfoId: {}, periodFlag: {}, salesType: {}", 
+                        stationInfo.getId(), currentPeriodFlag, salesType);
+                return BigDecimal.ZERO;
+            }
+            
+            // 获取综合销售费作为单价
+            BigDecimal unitPrice = policyFee.getCompSalesFee() != null ? policyFee.getCompSalesFee() : BigDecimal.ZERO;
+            
+            // 实际收取金额 = 充电度数 * 当前时段单价
+            BigDecimal realCost = totalPower.multiply(unitPrice).setScale(2, BigDecimal.ROUND_HALF_UP);
+            
+            log.info("计算实际收取金额 - totalPower: {}, periodFlag: {}, unitPrice: {}, realCost: {}", 
+                    totalPower, currentPeriodFlag, unitPrice, realCost);
+            
+            return realCost;
+            
+        } catch (Exception e) {
+            log.error("计算平台实际收取金额失败", e);
+            return BigDecimal.ZERO;
+        }
+    }
 
     // ==================== JSON解析工具方法 ====================
 
@@ -430,49 +541,79 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
     /**
      * 根据充电订单号StartChargeSeq 获取订单信息判断是否需要熔断,提前跳枪
      * 使用分布式锁防止并发重复检查
+     * 
+     * @param chargeStatus 充电状态
+     * @param chargeOrderInfo 订单信息
      */
-    private void isNeedBreak(ThirdPartyChargeStatus chargeStatus) {
+    private void isNeedBreak(ThirdPartyChargeStatus chargeStatus, ChargeOrderInfo chargeOrderInfo) {
+        // 订单为空或不是充电中状态,不需要熔断检查
+        if (chargeOrderInfo == null || !Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ONE)) {
+            return;
+        }
+        
         String lockKey = BREAK_CHECK_LOCK_KEY + chargeStatus.getStartChargeSeq();
         RLock lock = redissonClient.getLock(lockKey);
         try {
             // 尝试获取锁,最多等待3秒,持有锁最多10秒
             if (lock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS)) {
                 try {
-                    ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(
-                            Wrappers.<ChargeOrderInfo>lambdaQuery()
-                                    .eq(ChargeOrderInfo::getChargeOrderNo, chargeStatus.getStartChargeSeq())
-                                    .eq(ChargeOrderInfo::getStatus, 1)
-                                    .eq(ChargeOrderInfo::getIsDeleted, 0)
-                    );
-                    if (null != chargeOrderInfo) {
-                        // TODO: 根据业务需求实现熔断条件判断(例如:余额不足、超时等)
-                        DictItem dictItem = dictItemMapper.selectOne(
-                                new LambdaQueryWrapper<DictItem>()
-                                        .eq(DictItem::getDictCode, "up_recharge")
-                                        .eq(DictItem::getStatus, 1)
-                                        .last("LIMIT 1"));
-                        //安全价
-                        BigDecimal upRecharge = new BigDecimal(dictItem.getValue());
-                        //获取用户余额
-                        BigDecimal balance = userAccountMapper.selectOne(Wrappers.<UserAccount>lambdaQuery()
-                                .eq(UserAccount::getUserId, chargeOrderInfo.getUserId())
-                                .eq(UserAccount::getIsDeleted, 0)).getBalance();
-                        //企业价
-                        BigDecimal firmPrice;
-                        //根据用户ID去查询当前用户是否有归属企业
-                        UserFirm userFirm = userFirmMapper.selectOne(Wrappers.<UserFirm>lambdaQuery()
-                                .eq(UserFirm::getUserId, chargeOrderInfo.getUserId())
-                                .eq(UserFirm::getIsDeleted, 0));
-                        if (null != userFirm) {
-                            ThirdPartyEquipmentInfo thirdPartyEquipmentInfo = thirdPartyEquipmentInfoMapper.selectOne(Wrappers.<ThirdPartyEquipmentInfo>lambdaQuery()
-                                    .eq(ThirdPartyEquipmentInfo::getEquipmentId, chargeOrderInfo.getEquipmentId())
-                                    .eq(ThirdPartyEquipmentInfo::getIsDeleted, 0));
-                            List<PolicyFee> policyFees = policyFeeMapper.selectList(Wrappers.<PolicyFee>lambdaQuery()
-                                    .eq(PolicyFee::getStationInfoId, thirdPartyEquipmentInfo.getStationId())
-                                    .eq(PolicyFee::getFirmId, userFirm.getFirmId()));
+                    // 获取安全阈值配置
+                    DictItem dictItem = dictItemMapper.selectOne(
+                            new LambdaQueryWrapper<DictItem>()
+                                    .eq(DictItem::getDictCode, "up_recharge")
+                                    .eq(DictItem::getStatus, 1)
+                                    .last("LIMIT 1"));
+                    
+                    BigDecimal safetyThreshold = BigDecimal.ZERO;
+                    if (dictItem != null && dictItem.getValue() != null) {
+                        safetyThreshold = new BigDecimal(dictItem.getValue());
+                    }
+                    
+                    // 获取用户余额
+                    UserAccount userAccount = userAccountMapper.selectOne(Wrappers.<UserAccount>lambdaQuery()
+                            .eq(UserAccount::getUserId, chargeOrderInfo.getUserId())
+                            .eq(UserAccount::getIsDeleted, 0));
+                    
+                    if (userAccount == null) {
+                        log.warn("未找到用户账户信息 - userId: {}", chargeOrderInfo.getUserId());
+                        return;
+                    }
+                    
+                    BigDecimal balance = userAccount.getBalance() != null ? userAccount.getBalance() : BigDecimal.ZERO;
+                    
+                    // 获取当前实时消费金额
+                    BigDecimal realCost = chargeOrderInfo.getRealCost() != null ? chargeOrderInfo.getRealCost() : BigDecimal.ZERO;
+                    
+                    // 剩余可用余额 = 账户余额 - 实时消费 - 安全阈值
+                    BigDecimal remainingBalance = balance.subtract(realCost).subtract(safetyThreshold);
+                    
+                    log.info("熔断检查 - startChargeSeq: {}, 用户余额: {}, 实时消费: {}, 安全阈值: {}, 剩余可用: {}",
+                            chargeStatus.getStartChargeSeq(), balance, realCost, safetyThreshold, remainingBalance);
+                    
+                    // 如果剩余可用余额小于0,触发熔断
+                    if (remainingBalance.compareTo(BigDecimal.ZERO) < 0) {
+                        log.warn("余额不足,触发熔断停止充电 - startChargeSeq: {}, 用户余额: {}, 实时消费: {}",
+                                chargeStatus.getStartChargeSeq(), balance, realCost);
+                        
+                        // 调用第三方停止充电接口
+                        try {
+                            chargingBusinessService.stopCharging(
+                                    chargeStatus.getStartChargeSeq(), 
+                                    chargeStatus.getConnectorId());
+                            log.info("已发送停止充电请求 - startChargeSeq: {}, connectorId: {}",
+                                    chargeStatus.getStartChargeSeq(), chargeStatus.getConnectorId());
+                            
+                            // 更新订单停止类型为余额不足停止
+                            chargeOrderInfo.setStopType(3);  // 3-余额不足停止
+                            chargeOrderInfo.setStopReason("余额不足,系统自动停止充电");
+                            chargeOrderInfoService.updateById(chargeOrderInfo);
+                            
+                        } catch (Exception e) {
+                            log.error("调用第三方停止充电接口失败 - startChargeSeq: {}", 
+                                    chargeStatus.getStartChargeSeq(), e);
                         }
-
                     }
+                    
                 } finally {
                     lock.unlock();
                 }
@@ -482,6 +623,8 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             log.error("获取熔断检查锁被中断 - startChargeSeq: {}", chargeStatus.getStartChargeSeq(), e);
+        } catch (Exception e) {
+            log.error("熔断检查失败 - startChargeSeq: {}", chargeStatus.getStartChargeSeq(), e);
         }
     }
 }

+ 7 - 1
src/main/resources/application-dev.yml

@@ -142,7 +142,13 @@ security:
     # 排除的 URL 路径(不进行安全检查,注意:默认已经排除了 /doc.html、/swagger-ui 等接口文档相关路径)
     exclude-urls:
       - /api/v1/auth/captcha  # 验证码接口
-      - /charge-business/v1/linkData  # 第三方充电平台接口(数据为加密的Base64,会误判)
+      - /charge-business/v1/linkData/  # 第三方充电平台接口(数据为加密的Base64,会误判)
+      - /charge-business/v1/linkData/notification_charge_order_info
+      - /charge-business/v1/linkData/notification_start_charge_result
+      - /charge-business/v1/linkData/notification_equip_charge_status
+      - /charge-business/v1/linkData/notification_stop_charge_result
+      - /charge-business/v1/linkData/notification_stationStatus
+      - /charge-business/v1/linkData/query_token
     # 额外需要检查的请求头(默认已检查 User-Agent、Referer、X-Forwarded-For)
     check-headers:
       - Referer

+ 7 - 1
src/main/resources/application-prod.yml

@@ -142,7 +142,13 @@ security:
     # 排除的 URL 路径(不进行安全检查,注意:默认已经排除了 /doc.html、/swagger-ui 等接口文档相关路径)
     exclude-urls:
       - /api/v1/auth/captcha  # 验证码接口
-      - /charge-business/v1/linkData  # 第三方充电平台接口(数据为加密的Base64,会误判)
+      - /charge-business/v1/linkData/  # 第三方充电平台接口(数据为加密的Base64,会误判)
+      - /charge-business/v1/linkData/notification_charge_order_info
+      - /charge-business/v1/linkData/notification_start_charge_result
+      - /charge-business/v1/linkData/notification_equip_charge_status
+      - /charge-business/v1/linkData/notification_stop_charge_result
+      - /charge-business/v1/linkData/notification_stationStatus
+      - /charge-business/v1/linkData/query_token
     # 额外需要检查的请求头(默认已检查 User-Agent、Referer、X-Forwarded-For)
     check-headers:
       - Referer

+ 15 - 23
src/main/resources/mapper/business/ThirdPartyStationInfoMapper.xml

@@ -108,8 +108,6 @@
             WHERE p1.is_deleted = 0
         ) pf ON pf.station_info_id = tpsi.id
         WHERE tpsi.is_deleted = 0
-        AND tpsi.equipment_owner_id = 'MA6DP6BE7'
-        AND tpsi.policy_configured = 1
         <if test="query.stationId != null and query.stationId != ''">
             AND tpsi.station_id = #{query.stationId}
         </if>
@@ -230,7 +228,7 @@
                         4
                     )
                 FROM c_policy_fee pf
-                INNER JOIN third_party_policy_info tppi ON pf.start_time = tppi.start_time
+                INNER JOIN third_party_policy_info tppi ON pf.period_flag = tppi.period_flag
                 INNER JOIN third_party_equipment_price_policy tpepp ON tppi.price_policy_id = tpepp.id AND tpepp.is_deleted = 0
                 INNER JOIN third_party_connector_info tpci3 ON tpepp.connector_id = tpci3.connector_id AND tpci3.is_deleted = 0
                 WHERE tpci3.station_id = tpsi.station_id
@@ -238,8 +236,8 @@
                     AND pf.sales_type = 0
                     AND pf.is_deleted = 0
                     AND tppi.is_deleted = 0
-                    AND pf.start_time &lt;= #{currentTime}
-                ORDER BY pf.start_time DESC
+                    AND tppi.start_time &lt;= #{currentTime}
+                ORDER BY tppi.start_time DESC
                 LIMIT 1
             ) AS platform_price,
             <!-- 企业价(如果有企业ID) -->
@@ -252,7 +250,7 @@
                         4
                     )
                 FROM c_policy_fee pf
-                INNER JOIN third_party_policy_info tppi ON pf.start_time = tppi.start_time
+                INNER JOIN third_party_policy_info tppi ON pf.period_flag = tppi.period_flag
                 INNER JOIN third_party_equipment_price_policy tpepp ON tppi.price_policy_id = tpepp.id AND tpepp.is_deleted = 0
                 INNER JOIN third_party_connector_info tpci4 ON tpepp.connector_id = tpci4.connector_id AND tpci4.is_deleted = 0
                 WHERE tpci4.station_id = tpsi.station_id
@@ -261,8 +259,8 @@
                     AND pf.firm_id = #{firmId}
                     AND pf.is_deleted = 0
                     AND tppi.is_deleted = 0
-                    AND pf.start_time &lt;= #{currentTime}
-                ORDER BY pf.start_time DESC
+                    AND tppi.start_time &lt;= #{currentTime}
+                ORDER BY tppi.start_time DESC
                 LIMIT 1
             ) AS enterprise_price
             </if>
@@ -272,8 +270,6 @@
         FROM third_party_station_info tpsi
         LEFT JOIN third_party_connector_info tpci ON tpsi.station_id = tpci.station_id AND tpci.is_deleted = 0
         WHERE tpsi.is_deleted = 0
-            AND tpsi.equipment_owner_id = 'MA6DP6BE7'
-            AND tpsi.policy_configured = 1
             <!-- 确保存在平台价配置 -->
             AND EXISTS (
                 SELECT 1 FROM c_policy_fee pf_check
@@ -394,7 +390,7 @@
                         4
                     )
                 FROM c_policy_fee pf
-                INNER JOIN third_party_policy_info tppi ON pf.start_time = tppi.start_time
+                INNER JOIN third_party_policy_info tppi ON pf.period_flag = tppi.period_flag
                 INNER JOIN third_party_equipment_price_policy tpepp ON tppi.price_policy_id = tpepp.id AND tpepp.is_deleted = 0
                 INNER JOIN third_party_connector_info tpci3 ON tpepp.connector_id = tpci3.connector_id AND tpci3.is_deleted = 0
                 WHERE tpci3.station_id = tpsi.station_id
@@ -402,8 +398,8 @@
                     AND pf.sales_type = 0
                     AND pf.is_deleted = 0
                     AND tppi.is_deleted = 0
-                    AND pf.start_time &lt;= #{currentTime}
-                ORDER BY pf.start_time DESC
+                    AND tppi.start_time &lt;= #{currentTime}
+                ORDER BY tppi.start_time DESC
                 LIMIT 1
             ) AS platform_price,
             <!-- 企业价(地图模式不需要) -->
@@ -411,8 +407,6 @@
         FROM third_party_station_info tpsi
         LEFT JOIN third_party_connector_info tpci ON tpsi.station_id = tpci.station_id AND tpci.is_deleted = 0
         WHERE tpsi.is_deleted = 0
-            AND tpsi.equipment_owner_id = 'MA6DP6BE7'
-            AND tpsi.policy_configured = 1
             <!-- 确保存在平台价配置 -->
             AND EXISTS (
                 SELECT 1 FROM c_policy_fee pf_check
@@ -477,7 +471,7 @@
                         4
                     )
                 FROM c_policy_fee pf
-                INNER JOIN third_party_policy_info tppi ON pf.start_time = tppi.start_time
+                INNER JOIN third_party_policy_info tppi ON pf.period_flag = tppi.period_flag
                 INNER JOIN third_party_equipment_price_policy tpepp ON tppi.price_policy_id = tpepp.id AND tpepp.is_deleted = 0
                 INNER JOIN third_party_connector_info tpci3 ON tpepp.connector_id = tpci3.connector_id AND tpci3.is_deleted = 0
                 WHERE tpci3.station_id = tpsi.station_id
@@ -485,8 +479,8 @@
                     AND pf.sales_type = 0
                     AND pf.is_deleted = 0
                     AND tppi.is_deleted = 0
-                    AND pf.start_time &lt;= #{currentTime}
-                ORDER BY pf.start_time DESC
+                    AND tppi.start_time &lt;= #{currentTime}
+                ORDER BY tppi.start_time DESC
                 LIMIT 1
             ) AS current_price,
             <!-- 当前时段名称 -->
@@ -547,7 +541,7 @@
                         4
                     )
                 FROM c_policy_fee pf
-                INNER JOIN third_party_policy_info tppi ON pf.start_time = tppi.start_time
+                INNER JOIN third_party_policy_info tppi ON pf.period_flag = tppi.period_flag
                 INNER JOIN third_party_equipment_price_policy tpepp ON tppi.price_policy_id = tpepp.id AND tpepp.is_deleted = 0
                 INNER JOIN third_party_connector_info tpci5 ON tpepp.connector_id = tpci5.connector_id AND tpci5.is_deleted = 0
                 WHERE tpci5.station_id = tpsi.station_id
@@ -556,8 +550,8 @@
                     AND pf.firm_id = #{firmId}
                     AND pf.is_deleted = 0
                     AND tppi.is_deleted = 0
-                    AND pf.start_time &lt;= #{currentTime}
-                ORDER BY pf.start_time DESC
+                    AND tppi.start_time &lt;= #{currentTime}
+                ORDER BY tppi.start_time DESC
                 LIMIT 1
             ) AS enterprise_price
             </if>
@@ -708,8 +702,6 @@
             ), 0) AS slow_total_count
         FROM third_party_station_info tpsi
         WHERE tpsi.is_deleted = 0
-            AND tpsi.equipment_owner_id = 'MA6DP6BE7'
-            AND tpsi.policy_configured = 1
             AND (
                 tpsi.station_name LIKE CONCAT('%', #{keyword}, '%')
                 OR tpsi.address LIKE CONCAT('%', #{keyword}, '%')

+ 3 - 3
src/main/resources/mapper/charging/ThirdPartyConnectorInfoMapper.xml

@@ -211,7 +211,7 @@
         
         -- 关联常规价格(sales_type = 0,不限制企业)
         LEFT JOIN c_policy_fee pf_common ON pf_common.station_info_id = tpsi.id 
-            AND pf_common.start_time = tppi.start_time
+            AND pf_common.period_flag = tppi.period_flag
             AND pf_common.is_deleted = 0
             AND pf_common.sales_type = 0
         
@@ -224,9 +224,9 @@
                 AND pf1.sales_type = 1
                 AND uf.user_id = #{userId}
         ) pf ON pf.station_info_id = tpsi.id 
-            AND pf.start_time = tppi.start_time
+            AND pf.period_flag = tppi.period_flag
         
-        WHERE tpci.id = #{connectorId}
+        WHERE tpci.connector_id = #{connectorCode}
             AND tpci.is_deleted = 0
         LIMIT 1
     </select>

+ 22 - 0
src/main/resources/mapper/charging/ThirdPartyPolicyInfoMapper.xml

@@ -41,5 +41,27 @@
             AND tppi.period_flag = #{periodFlag}
         LIMIT 1
     </select>
+    
+    <!-- 根据stationInfoId和当前时间查询当前时段的价格策略 -->
+    <select id="selectCurrentPeriodPolicyByStation" resultType="com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo">
+        SELECT 
+            tppi.id,
+            tppi.elec_price,
+            tppi.service_price,
+            tppi.period_flag,
+            tppi.start_time
+        FROM third_party_station_info tpsi
+        INNER JOIN third_party_connector_info tpci 
+            ON tpci.station_id = tpsi.station_id AND tpci.is_deleted = 0
+        INNER JOIN third_party_equipment_price_policy tpepp 
+            ON tpepp.connector_id = tpci.connector_id AND tpepp.is_deleted = 0
+        INNER JOIN third_party_policy_info tppi 
+            ON tppi.price_policy_id = tpepp.id AND tppi.is_deleted = 0
+        WHERE tpsi.id = #{stationInfoId}
+            AND tpsi.is_deleted = 0
+            AND tppi.start_time &lt;= #{currentTime}
+        ORDER BY tppi.start_time DESC
+        LIMIT 1
+    </select>
 
 </mapper>