package com.zsElectric.boot.charging.service.impl; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.zsElectric.boot.business.mapper.*; import com.zsElectric.boot.business.model.entity.ChargeOrderInfo; import com.zsElectric.boot.business.model.entity.PolicyFee; import com.zsElectric.boot.business.model.entity.UserAccount; import com.zsElectric.boot.business.model.entity.UserFirm; 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; import com.zsElectric.boot.common.constant.ConnectivityConstants; import com.zsElectric.boot.common.constant.SystemConstants; import com.zsElectric.boot.common.util.DateUtils; import com.zsElectric.boot.common.util.electric.ChargingUtil; import com.zsElectric.boot.common.util.electric.RequestParmsEntity; import com.zsElectric.boot.common.util.electric.ResponseParmsEntity; import com.zsElectric.boot.core.exception.BusinessException; import com.zsElectric.boot.system.mapper.DictItemMapper; import com.zsElectric.boot.system.model.entity.DictItem; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static com.zsElectric.boot.common.constant.ConnectivityConstants.FAIL_REASON_NONE; import static com.zsElectric.boot.common.constant.ConnectivityConstants.STATUS_OK; import static com.zsElectric.boot.common.util.HmacMD5Util.genSign; import static com.zsElectric.boot.common.util.HmacMD5Util.verify; /** * 第三方充电推送接收服务实现 * * @author system * @since 2025-12-11 */ @Slf4j @Service @RequiredArgsConstructor public class ChargingReceptionServiceImpl implements ChargingReceptionService { private final ChargingUtil chargingUtil; private final ThirdPartyConnectorInfoMapper connectorInfoMapper; private final ThirdPartyChargeStatusMapper chargeStatusMapper; private final ObjectMapper objectMapper; private final ChargeOrderInfoService chargeOrderInfoService; private final RedissonClient redissonClient; private final UserAccountMapper userAccountMapper; private final UserFirmMapper userFirmMapper; private final PolicyFeeMapper policyFeeMapper; private final DictItemMapper dictItemMapper; private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper; private final ChargingBusinessService chargingBusinessService; private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper; private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper; /** * 熔断检查锁前缀 */ private static final String BREAK_CHECK_LOCK_KEY = "charging:break:check:"; /** * 锁等待时间(秒) */ private static final long LOCK_WAIT_TIME = 3; /** * 锁持有时间(秒) */ private static final long LOCK_LEASE_TIME = 10; private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // ==================== 接口实现 ==================== @Override public ResponseParmsEntity chargeResponse(RequestParmsEntity requestDTO) { log.info("接收推送启动充电结果请求参数:{}", requestDTO); return processStartChargeResultRequest(requestDTO); } @Override public ResponseParmsEntity chargeStatusResponse(RequestParmsEntity requestDTO) { log.info("接收推送充电状态请求参数:{}", requestDTO); return processChargeStatusRequest(requestDTO); } @Override public ResponseParmsEntity stopChargeResponse(RequestParmsEntity requestDTO) { log.info("接收推送停止充电结果请求参数:{}", requestDTO); return processStopChargeResultRequest(requestDTO); } @Override public ResponseParmsEntity chargeOrderResponse(RequestParmsEntity requestDTO) throws Exception { log.info("接收推送充电订单信息请求参数:{}", requestDTO); return processChargeRequest(requestDTO, jsonNode -> { log.debug("充电订单信息 - StartChargeSeq: {}", getTextValue(jsonNode, "StartChargeSeq")); }); } @Override public ResponseParmsEntity stationStatus(RequestParmsEntity requestDTO) { log.info("接收设备状态变化推送请求参数:{}", requestDTO); return processStationStatusRequest(requestDTO); } // ==================== 公共处理方法 ==================== /** * 通用充电请求处理模板 */ private ResponseParmsEntity processChargeRequest(RequestParmsEntity requestDTO, Consumer businessHandler) { try { JsonNode jsonNode = verifyAndDecrypt(requestDTO); //查询订单 String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq"); String stopReason = getTextValue(jsonNode, "StopReason"); String endTime = getTextValue(jsonNode, "EndTime"); String startTime = getTextValue(jsonNode, "StartTime"); String totalPower = getTextValue(jsonNode, "TotalPower"); String totalElecMoney = getTextValue(jsonNode, "TotalElecMoney"); String totalMoney = getTextValue(jsonNode, "TotalMoney"); String totalSeviceMoney = getTextValue(jsonNode, "TotalSeviceMoney"); String connectorID = getTextValue(jsonNode, "ConnectorID"); ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper() .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1")); chargeOrderInfo.setStopReason(stopReason); chargeOrderInfo.setStartTime(startTime); chargeOrderInfo.setEndTime(endTime); chargeOrderInfo.setTotalCharge(new BigDecimal(totalPower)); chargeOrderInfo.setThirdPartyTotalCost(new BigDecimal(totalMoney)); chargeOrderInfo.setThirdPartyServerfee(new BigDecimal(totalSeviceMoney)); chargeOrderInfo.setThirdPartyElecfee(new BigDecimal(totalElecMoney)); ThirdPartyConnectorInfo thirdPartyConnectorInfo = connectorInfoMapper.selectOne(Wrappers.lambdaQuery() .eq(ThirdPartyConnectorInfo::getConnectorId, connectorID).last("LIMIT 1")); String stationId = thirdPartyConnectorInfo.getStationId(); ThirdPartyStationInfo thirdPartyStationInfo = thirdPartyStationInfoMapper.selectOne(Wrappers.lambdaQuery() .eq(ThirdPartyStationInfo::getStationId, stationId).last("LIMIT 1")); if (ObjectUtil.isEmpty(thirdPartyConnectorInfo)) { log.error("thirdPartyConnectorInfo" +"为空==============================================="); } //平台服务费 BigDecimal serviceFee = BigDecimal.ZERO; JsonNode chargeDetails = jsonNode.get("ChargeDetails"); if (ObjectUtil.isNotEmpty(chargeDetails)) { for (JsonNode node : chargeDetails) { //提取字段值 String itemFlag = node.get("ItemFlag").asText(); node.get("DetailPower").asText(); BigDecimal detailPower = new BigDecimal(node.get("DetailPower").asText()); PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.lambdaQuery() .eq(PolicyFee::getStationInfoId, thirdPartyStationInfo.getId()) .eq(PolicyFee::getPeriodFlag, Integer.parseInt(itemFlag)) .last("LIMIT 1")); if (ObjectUtil.isNotEmpty(policyFee)) { BigDecimal opFee = policyFee.getOpFee(); log.info("策略费用:{}",opFee); serviceFee = serviceFee.add(opFee.multiply(detailPower)); } } } log.info("计算后的平台服务费:{}", serviceFee); chargeOrderInfo.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP)); //订单结算:平台实际收取金额 = 互联互通金额 + 中数电动金额(平台总服务费) chargeOrderInfo.setRealCost(chargeOrderInfo.getRealServiceCost().add(chargeOrderInfo.getThirdPartyTotalCost())); //订单状态->已完成 chargeOrderInfo.setStatus(SystemConstants.STATUS_THREE); //计算充电时间 chargeOrderInfo.setChargeTime(DateUtils.getDuration(chargeOrderInfo.getStartTime(), chargeOrderInfo.getEndTime())); //修改订单 chargeOrderInfoService.updateById(chargeOrderInfo); //账户余额扣减(积分增加) log.info("执行账户余额扣减(积分增加)"); chargeOrderInfoService.orderSettlement(chargeOrderInfo.getId()); // 执行业务处理 businessHandler.accept(jsonNode); // 构建响应 return buildChargeResponse(getTextValue(jsonNode, "StartChargeSeq")); } catch (BusinessException e) { throw e; } catch (Exception e) { log.error("处理请求失败:{}", e.getMessage()); throw new BusinessException("处理请求失败:" + e.getMessage(), e); } } /** * 处理启动充电结果推送请求 * 数据格式:{"ConnectorID":"xxx","StartChargeSeq":"xxx","StartChargeSeqStat":2,"StartTime":"xxx"} */ private ResponseParmsEntity processStartChargeResultRequest(RequestParmsEntity requestDTO) { try { JsonNode jsonNode = verifyAndDecrypt(requestDTO); // 启动充电结果业务处理 String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq"); Integer startChargeSeqStat = getIntValue(jsonNode, "StartChargeSeqStat"); String startTime = getTextValue(jsonNode, "StartTime"); ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper() .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1")); if (startChargeSeqStat != null) { switch (startChargeSeqStat) { case 1 -> log.info("启动中 - StartChargeSeq: {}", startChargeSeq); case 2 -> { log.info("充电中 - StartChargeSeq: {}", startChargeSeq); // 修改订单状态 if (ObjectUtil.isNotEmpty(chargeOrderInfo)) { if (Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ZERO)) { chargeOrderInfo.setStatus(SystemConstants.STATUS_ONE); chargeOrderInfo.setStartTime(startTime); chargeOrderInfoService.updateById(chargeOrderInfo); } } } case 3 -> log.info("停止中 - StartChargeSeq: {}", startChargeSeq); case 4 -> log.info("已结束 - StartChargeSeq: {}", startChargeSeq); case 5 -> log.info("未知 - StartChargeSeq: {}", startChargeSeq); default -> log.warn("未知状态 - StartChargeSeq: {}, Stat: {}", startChargeSeq, startChargeSeqStat); } } // 构建响应 return buildChargeResponse(startChargeSeq); } catch (BusinessException e) { throw e; } catch (Exception e) { log.error("处理启动充电结果推送失败:{}", e.getMessage(), e); throw new BusinessException("处理启动充电结果推送失败:" + e.getMessage(), e); } } /** * 处理停止充电结果推送请求 * 数据格式:{"ConnectorID":"xxx","FailReason":0,"StartChargeSeq":"xxx","StartChargeSeqStat":4,"SuccStat":0} */ private ResponseParmsEntity processStopChargeResultRequest(RequestParmsEntity requestDTO) { try { JsonNode jsonNode = verifyAndDecrypt(requestDTO); // 停止充电结果业务处理 String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq"); Integer startChargeSeqStat = getIntValue(jsonNode, "StartChargeSeqStat"); Integer succStat = getIntValue(jsonNode, "SuccStat"); Integer failReason = getIntValue(jsonNode, "FailReason"); log.info("停止充电结果 - StartChargeSeq: {}, Stat: {}, SuccStat: {}, FailReason: {}", startChargeSeq, startChargeSeqStat, succStat, failReason); ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper() .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1")); if (startChargeSeqStat != null && ObjectUtil.isNotEmpty(chargeOrderInfo)) { switch (startChargeSeqStat) { case 1 -> log.info("启动中 - StartChargeSeq: {}", startChargeSeq); case 2 -> log.info("充电中 - StartChargeSeq: {}", startChargeSeq); case 3 -> log.info("停止中 - StartChargeSeq: {}", startChargeSeq); case 4 -> { log.info("已结束 - StartChargeSeq: {}", startChargeSeq); // 修改订单状态为结算中 if (Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ONE) || Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_THREE)) { chargeOrderInfo.setStatus(SystemConstants.STATUS_TWO); chargeOrderInfoService.updateById(chargeOrderInfo); log.info("更新订单状态为结算中 - orderId: {}", chargeOrderInfo.getId()); } } case 5 -> log.info("未知 - StartChargeSeq: {}", startChargeSeq); default -> log.warn("未知状态 - StartChargeSeq: {}, Stat: {}", startChargeSeq, startChargeSeqStat); } } // 构建响应 return buildChargeResponse(startChargeSeq); } catch (BusinessException e) { throw e; } catch (Exception e) { log.error("处理停止充电结果推送失败:{}", e.getMessage(), e); throw new BusinessException("处理停止充电结果推送失败:" + e.getMessage(), e); } } /** * 处理实时充电状态推送请求 * 数据格式:{"ConnectorID":"xxx","ConnectorStatus":3,"TotalPower":1.95,"ElecMoney":1.89,...} */ private ResponseParmsEntity processChargeStatusRequest(RequestParmsEntity requestDTO) { try { JsonNode jsonNode = verifyAndDecrypt(requestDTO); // 保存或更新充电状态 saveOrUpdateChargeStatus(jsonNode); // 构建响应 return buildChargeResponse(getTextValue(jsonNode, "StartChargeSeq")); } catch (BusinessException e) { throw e; } catch (Exception e) { log.error("处理充电状态推送失败:{}", e.getMessage(), e); throw new BusinessException("处理充电状态推送失败:" + e.getMessage(), e); } } /** * 处理设备状态变化推送请求 */ private ResponseParmsEntity processStationStatusRequest(RequestParmsEntity requestDTO) { try { String decryptData = verifyAndDecryptRaw(requestDTO); // 解析并更新设备状态 QueryStationStatusVO stationStatusVO = objectMapper.readValue(decryptData, QueryStationStatusVO.class); updateConnectorStatus(stationStatusVO); // 构建响应 return buildStatusResponse(); } catch (BusinessException e) { throw e; } catch (Exception e) { log.error("处理设备状态推送失败:{}", e.getMessage()); throw new BusinessException("处理设备状态推送失败:" + e.getMessage(), e); } } /** * 验签并解密请求数据 */ private JsonNode verifyAndDecrypt(RequestParmsEntity requestDTO) throws Exception { String decryptData = verifyAndDecryptRaw(requestDTO); return objectMapper.readTree(decryptData); } /** * 验签并解密请求数据(返回原始字符串) */ private String verifyAndDecryptRaw(RequestParmsEntity requestDTO) throws Exception { String signData = requestDTO.getOperatorID() + requestDTO.getData() + requestDTO.getTimeStamp() + requestDTO.getSeq(); if (!verify(signData, ConnectivityConstants.SIG_SECRET, requestDTO.getSig())) { log.error("数据验签失败"); throw new BusinessException("数据验签失败"); } String decryptData = chargingUtil.decryptData(requestDTO.getData()); log.info("==================== 解密数据开始 ===================="); log.info("操作员ID: {}", requestDTO.getOperatorID()); log.info("解密后的数据:{}", decryptData); log.info("==================== 解密数据结束 ===================="); return decryptData; } // ==================== 响应构建 ==================== /** * 构建充电响应 */ private ResponseParmsEntity buildChargeResponse(String startChargeSeq) throws Exception { ChargeResponseVO chargeResponseVO = new ChargeResponseVO(); chargeResponseVO.setStartChargeSeq(startChargeSeq); chargeResponseVO.setSuccStat(STATUS_OK); chargeResponseVO.setFailReason(FAIL_REASON_NONE); String encryptData = chargingUtil.encryptData(objectMapper.writeValueAsString(chargeResponseVO)); String sign = genSign(STATUS_OK, "请求成功", encryptData, ConnectivityConstants.SIG_SECRET); ResponseParmsEntity response = new ResponseParmsEntity(); response.setRet(STATUS_OK); response.setMsg("请求成功"); response.setData(encryptData); response.setSig(sign); return response; } /** * 构建设备状态响应 */ private ResponseParmsEntity buildStatusResponse() throws Exception { Map statusMap = new HashMap<>(); statusMap.put("Status", 0); String encryptData = chargingUtil.encryptData(objectMapper.writeValueAsString(statusMap)); String sign = genSign(STATUS_OK, "", encryptData, ConnectivityConstants.SIG_SECRET); ResponseParmsEntity response = new ResponseParmsEntity(); response.setRet(STATUS_OK); response.setMsg(""); response.setData(encryptData); response.setSig(sign); return response; } // ==================== 状态更新 ==================== /** * 更新充电接口状态 */ private void updateConnectorStatus(QueryStationStatusVO stationStatusVO) { if (stationStatusVO == null) { return; } // 处理单个连接器状态推送格式:{"ConnectorStatusInfo":{"ConnectorID":"xxx","Status":2}} ConnectorStatusInfo singleConnector = stationStatusVO.getConnectorStatusInfo(); if (singleConnector != null && singleConnector.getConnectorID() != null) { connectorInfoMapper.update(null, Wrappers.lambdaUpdate() .eq(ThirdPartyConnectorInfo::getConnectorId, singleConnector.getConnectorID()) .set(ThirdPartyConnectorInfo::getStatus, singleConnector.getStatus()) .set(ThirdPartyConnectorInfo::getUpdateTime, LocalDateTime.now())); log.info("更新充电接口状态(单个) - connectorId: {}, status: {}", singleConnector.getConnectorID(), singleConnector.getStatus()); return; } // 处理批量连接器状态推送格式 if (CollectionUtils.isEmpty(stationStatusVO.getStationStatusInfos())) { return; } for (StationStatusInfo stationStatusInfo : stationStatusVO.getStationStatusInfos()) { List connectorStatusInfos = stationStatusInfo.getConnectorStatusInfos(); if (CollectionUtils.isEmpty(connectorStatusInfos)) { continue; } for (ConnectorStatusInfo connectorStatus : connectorStatusInfos) { connectorInfoMapper.update(null, Wrappers.lambdaUpdate() .eq(ThirdPartyConnectorInfo::getConnectorId, connectorStatus.getConnectorID()) .set(ThirdPartyConnectorInfo::getStatus, connectorStatus.getStatus()) .set(ThirdPartyConnectorInfo::getUpdateTime, LocalDateTime.now())); log.debug("更新充电接口状态 - connectorId: {}, status: {}", connectorStatus.getConnectorID(), connectorStatus.getStatus()); } } } /** * 保存或更新充电状态数据 */ private void saveOrUpdateChargeStatus(JsonNode jsonNode) { try { log.info("保存或更新充电状态数据 - StartChargeSeq: {}", jsonNode); String startChargeSeq = jsonNode.get("StartChargeSeq").asText(); 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() .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("limit 1")); if(ObjUtil.isNotEmpty(chargeOrderInfo)){ Integer connectorStatus = getIntValue(jsonNode, "ConnectorStatus"); // 计算平台实际收取金额(根据度数和平台价格策略计算) BigDecimal realCost = calculateRealCost(chargeOrderInfo, totalPower); 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.lambdaQuery() .eq(ThirdPartyChargeStatus::getStartChargeSeq, startChargeSeq) ); ThirdPartyChargeStatus chargeStatus = (existing != null) ? existing : new ThirdPartyChargeStatus(); // 设置字段值 chargeStatus.setStartChargeSeq(startChargeSeq); 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(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")); chargeStatus.setVoltageC(getDecimalValue(jsonNode, "VoltageC")); chargeStatus.setCurrentA(getDecimalValue(jsonNode, "CurrentA")); chargeStatus.setCurrentB(getDecimalValue(jsonNode, "CurrentB")); chargeStatus.setCurrentC(getDecimalValue(jsonNode, "CurrentC")); // 兼容处理:如果有ChargeDetails字段,则存储为JSON字符串 if (jsonNode.has("ChargeDetails") && !jsonNode.get("ChargeDetails").isNull()) { 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); } // 熔断保护 - 余额不足判断 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.lambdaQuery() .eq(UserFirm::getUserId, chargeOrderInfo.getUserId()) .eq(UserFirm::getIsDeleted, 0)); // 获取设备信息 ThirdPartyEquipmentInfo equipmentInfo = thirdPartyEquipmentInfoMapper.selectOne( Wrappers.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.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.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解析工具方法 ==================== private String getTextValue(JsonNode node, String fieldName) { JsonNode field = node.get(fieldName); return (field != null && !field.isNull()) ? field.asText() : null; } private Integer getIntValue(JsonNode node, String fieldName) { JsonNode field = node.get(fieldName); return (field != null && !field.isNull()) ? field.asInt() : null; } private BigDecimal getDecimalValue(JsonNode node, String fieldName) { JsonNode field = node.get(fieldName); return (field != null && !field.isNull()) ? new BigDecimal(field.asText()) : null; } private LocalDateTime parseDateTime(String dateTimeStr) { if (dateTimeStr == null || dateTimeStr.isEmpty()) { return null; } try { return LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER); } catch (Exception e) { log.warn("解析时间失败: {}", dateTimeStr); return null; } } /** * 根据充电订单号StartChargeSeq 获取订单信息判断是否需要熔断,提前跳枪 * 使用分布式锁防止并发重复检查 * * @param chargeStatus 充电状态 * @param chargeOrderInfo 订单信息 */ 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 { // 获取安全阈值配置 DictItem dictItem = dictItemMapper.selectOne( new LambdaQueryWrapper() .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.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); log.info("熔断检查 - startChargeSeq: {}, 用户余额: {}, 实时消费: {}, 安全阈值: {}, 剩余余额: {}", chargeStatus.getStartChargeSeq(), balance, realCost, safetyThreshold, remainingBalance); // 熔断条件:剩余余额 < 安全阈值 且 已产生实际消费(实时消费 > 0) // 这样可以避免刚开始充电(消费为0)时因余额小于阈值而被误熔断 if (remainingBalance.compareTo(safetyThreshold) < 0 && realCost.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(); } } else { log.warn("获取熔断检查锁超时 - startChargeSeq: {}", chargeStatus.getStartChargeSeq()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("获取熔断检查锁被中断 - startChargeSeq: {}", chargeStatus.getStartChargeSeq(), e); } catch (Exception e) { log.error("熔断检查失败 - startChargeSeq: {}", chargeStatus.getStartChargeSeq(), e); } } }