package com.zsElectric.boot.business.service.impl; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.core.JsonProcessingException; import com.zsElectric.boot.business.converter.ChargeOrderInfoConverter; import com.zsElectric.boot.business.mapper.*; import com.zsElectric.boot.business.mapper.ThirdPartyStationInfoMapper; import com.zsElectric.boot.business.model.dto.ChargeOrderInfoExportDTO; import com.zsElectric.boot.business.model.entity.*; import com.zsElectric.boot.business.model.form.ChargeOrderInfoForm; import com.zsElectric.boot.business.model.form.applet.AppInvokeChargeForm; import com.zsElectric.boot.business.model.form.applet.AppStopChargeForm; import com.zsElectric.boot.business.model.query.ChargeOrderInfoQuery; import com.zsElectric.boot.business.model.query.applet.AppChargeOrderInfoQuery; import com.zsElectric.boot.business.model.vo.ChargeOrderInfoVO; import com.zsElectric.boot.business.model.vo.UserVehicleVO; import com.zsElectric.boot.business.model.vo.applet.AppChargeVO; import com.zsElectric.boot.business.model.vo.applet.AppUserInfoVO; import com.zsElectric.boot.business.service.AppletHomeService; import com.zsElectric.boot.business.service.ChargeOrderInfoService; import com.zsElectric.boot.business.service.UserAccountService; import com.zsElectric.boot.business.service.UserInfoService; import com.zsElectric.boot.charging.dto.StartChargingRequestDTO; import com.zsElectric.boot.charging.dto.StartChargingResponseVO; import com.zsElectric.boot.charging.service.ChargingBusinessService; import com.zsElectric.boot.charging.vo.EquipmentAuthResponseVO; import com.zsElectric.boot.charging.vo.StopChargingOperationResponseVO; import com.zsElectric.boot.common.constant.ConnectivityConstants; import com.zsElectric.boot.common.constant.SystemConstants; import com.zsElectric.boot.core.exception.BusinessException; import com.zsElectric.boot.security.util.SecurityUtils; import com.zsElectric.boot.system.mapper.UserMapper; import com.zsElectric.boot.system.model.entity.DictItem; import com.zsElectric.boot.system.model.entity.User; import com.zsElectric.boot.system.service.DictItemService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import com.zsElectric.boot.charging.entity.ThirdPartyChargeStatus; import com.zsElectric.boot.charging.entity.ThirdPartyConnectorInfo; import com.zsElectric.boot.charging.entity.ThirdPartyStationInfo; import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentPricePolicy; import com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo; import com.zsElectric.boot.charging.mapper.ThirdPartyChargeStatusMapper; import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper; import com.zsElectric.boot.charging.mapper.ThirdPartyEquipmentPricePolicyMapper; import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper; import com.zsElectric.boot.charging.mapper.ThirdPartyApiLogMapper; import com.zsElectric.boot.charging.entity.ThirdPartyApiLog; import com.zsElectric.boot.common.util.DateUtils; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.transaction.annotation.Transactional; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import java.util.concurrent.TimeUnit; import com.zsElectric.boot.business.model.entity.FirmAccountLog; import com.zsElectric.boot.common.util.OkHttpUtil; import static com.zsElectric.boot.business.service.WFTOrderService.USER_FUND_LOCK_KEY; import static com.zsElectric.boot.business.service.WFTOrderService.USER_FUND_LOCK_EXPIRE; /** * 充电订单信息服务实现类 * * @author zsElectric * @since 2025-12-17 19:13 */ @Slf4j @Service @RequiredArgsConstructor public class ChargeOrderInfoServiceImpl extends ServiceImpl implements ChargeOrderInfoService { private final ChargeOrderInfoConverter chargeOrderInfoConverter; private final ChargingBusinessService chargingBusinessService; private final ThirdPartyInfoMapper thirdPartyInfoMapper; private final UserInfoMapper userInfoMapper; private final UserInfoService userInfoService; private final FirmInfoMapper firmInfoMapper; private final CouponMapper couponMapper; private final CouponTemplateMapper couponTemplateMapper; private final UserAccountService userAccountService; private final AppletHomeService appletHomeService; private final UserMapper userMapper; private final UserVehicleMapper userVehicleMapper; private final ThirdPartyChargeStatusMapper chargeStatusMapper; private final ThirdPartyConnectorInfoMapper connectorInfoMapper; private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper; private final PolicyFeeMapper policyFeeMapper; private final ThirdPartyEquipmentPricePolicyMapper thirdPartyEquipmentPricePolicyMapper; private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper; private final DiscountsActivityMapper discountsActivityMapper; private final ThirdPartyApiLogMapper thirdPartyApiLogMapper; private final ObjectMapper objectMapper; private final DictItemService dictItemService; private final RedissonClient redissonClient; private final OkHttpUtil okHttpUtil; private final FirmAccountLogMapper firmAccountLogMapper; //充电订单号前缀 private final String ORDER_NO_PREFIX = "CD"; //设备流水号前缀 private final String EQUIPMENT_NO_PREFIX = "SB"; /** * 获取充电订单信息分页列表 * * @param queryParams 查询参数 * @return {@link IPage } 充电订单信息分页列表 */ @Override public IPage getChargeOrderInfoPage(ChargeOrderInfoQuery queryParams) { Page pageVO = this.baseMapper.getChargeOrderInfoPage( new Page<>(queryParams.getPageNum(), queryParams.getPageSize()), queryParams ); return pageVO; } /** * 获取充电订单信息表单数据 * * @param id 充电订单信息ID * @return 充电订单信息表单数据 */ @Override public ChargeOrderInfoForm getChargeOrderInfoFormData(Long id) { ChargeOrderInfo entity = this.getById(id); return chargeOrderInfoConverter.toForm(entity); } /** * 新增充电订单信息 * * @param formData 充电订单信息表单对象 * @return 是否新增成功 */ @Override public boolean saveChargeOrderInfo(ChargeOrderInfoForm formData) { ChargeOrderInfo entity = chargeOrderInfoConverter.toEntity(formData); return this.save(entity); } /** * 更新充电订单信息 * * @param id 充电订单信息ID * @param formData 充电订单信息表单对象 * @return 是否修改成功 */ @Override public boolean updateChargeOrderInfo(Long id,ChargeOrderInfoForm formData) { ChargeOrderInfo entity = chargeOrderInfoConverter.toEntity(formData); return this.updateById(entity); } /** * 删除充电订单信息 * * @param ids 充电订单信息ID,多个以英文逗号(,)分割 * @return 是否删除成功 */ @Override public boolean deleteChargeOrderInfos(String ids) { Assert.isTrue(StrUtil.isNotBlank(ids), "删除的充电订单信息数据为空"); // 逻辑删除 List idList = Arrays.stream(ids.split(",")) .map(Long::parseLong) .toList(); return this.removeByIds(idList); } @Override public IPage getPage(AppChargeOrderInfoQuery queryParams) { queryParams.setUserId(SecurityUtils.getUserId()); Page pageVO = this.baseMapper.getPage( new Page<>(queryParams.getPageNum(), queryParams.getPageSize()), queryParams ); return pageVO; } @Override public AppChargeVO invokeCharge(AppInvokeChargeForm formData) { log.info("启动充电开始,用户ID:{},设备认证流水号:{},充电桩编号:{}", SecurityUtils.getUserId(), formData.getEquipAuthSeq(), formData.getEquipmentId()); //校验设备占用状态 chargingBusinessService.checkEquipmentOccupancyStatus(formData.getConnectorId()); // 渠道方启动充电不需要用户资金锁 if (Objects.equals(formData.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) { log.info("渠道方启动充电开始,用户ID:{},设备认证流水号:{},充电桩编号:{}", SecurityUtils.getUserId(), formData.getEquipAuthSeq(), formData.getEquipmentId()); try { return channelInvokeCharge(formData); } catch (Exception e) { log.error("渠道方启动充电失败", e); throw new BusinessException("启动充电失败 !" + e.getMessage()); } } // 必要校验 Long userId = SecurityUtils.getUserId(); Assert.isTrue(userId != null, "用户ID不能为空"); // 获取用户资金操作统一锁,防止充电启动与退款并发 String lockKey = USER_FUND_LOCK_KEY + userId; RLock lock = redissonClient.getLock(lockKey); boolean locked = false; try { // 尝试获取锁,等待3秒,锁过期时间60秒 locked = lock.tryLock(3, USER_FUND_LOCK_EXPIRE, TimeUnit.SECONDS); if (!locked) { log.warn("用户:{}资金操作正在进行中,无法启动充电", userId); throw new BusinessException("操作正在进行中,请稍后重试"); } return doInvokeCharge(formData, userId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BusinessException("启动充电失败,请稍后重试"); } finally { // 释放锁 if (locked && lock.isHeldByCurrentThread()) { lock.unlock(); } } } /** * 执行启动充电逻辑(内部方法) */ private AppChargeVO doInvokeCharge(AppInvokeChargeForm formData, Long userId) { try { AppChargeVO appInvokeChargeVO = new AppChargeVO(); AppUserInfoVO userInfo = userInfoMapper.getAppletUserInfo(userId); Assert.isTrue(userInfo != null, "用户信息不存在"); //判断有没有正在进行中的订单 Long count = this.baseMapper.selectCount(Wrappers.lambdaQuery(ChargeOrderInfo.class).eq(ChargeOrderInfo::getUserId, userId).in(ChargeOrderInfo::getStatus, SystemConstants.STATUS_ZERO, SystemConstants.STATUS_ONE, SystemConstants.STATUS_TWO)); if (count > 0){ throw new BusinessException("您有正在进行中的订单,请先停止充电"); } //校验用户余额是否满足起充价(防止充值后立即退款绕过前端校验) DictItem upRecharge = dictItemService.getOne(Wrappers.lambdaQuery() .eq(DictItem::getDictCode, "up_recharge") .last("limit 1") ); if (ObjectUtil.isNotEmpty(upRecharge)) { BigDecimal chargeFee = new BigDecimal(upRecharge.getValue()); UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class) .eq(UserAccount::getUserId, userId) .last("limit 1")); if (userAccount == null || userAccount.getBalance().compareTo(chargeFee) < 0) { throw new BusinessException("用户余额低于起充值 " + chargeFee + " 元,请前往充值后再充电!"); } } //生成系统充电订单号及互联互通充电订单号 startChargeSeq equipAuthSeq (格式"运营商ID+唯一编号") String chargeOrderNo = generateNo(ORDER_NO_PREFIX, userId); String seq = ConnectivityConstants.OPERATOR_ID + chargeOrderNo; //请求设备认证 EquipmentAuthResponseVO equipmentAuthResponseVO = chargingBusinessService.queryEquipAuth(seq, formData.getConnectorId()); if (Objects.equals(equipmentAuthResponseVO.getSuccStat(), SystemConstants.STATUS_ONE)) { throw new BusinessException("设备认证失败,请检查枪是否正确接入充电槽!"); }else { log.info("设备认证成功,设备认证流水号:{}", equipmentAuthResponseVO.getEquipAuthSeq()); } //创建订单 ChargeOrderInfo chargeOrderInfo = new ChargeOrderInfo(); chargeOrderInfo.setUserId(userId); chargeOrderInfo.setConnectorId(formData.getConnectorId()); chargeOrderInfo.setEquipmentId(formData.getEquipmentId()); chargeOrderInfo.setEquipAuthSeq(seq); chargeOrderInfo.setChargeOrderNo(chargeOrderNo); chargeOrderInfo.setStartChargeSeq(seq); chargeOrderInfo.setPhoneNum(userInfo.getPhone()); chargeOrderInfo.setThirdPartyStationId(formData.getStationId()); chargeOrderInfo.setOrderType(formData.getOrderType()); //预支付金额 BigDecimal preAmt = appletHomeService.calculateAvailableChargingAmount(formData.getConnectorId()); chargeOrderInfo.setPreAmt(preAmt); //渠道方订单设置运营商ID if (Objects.equals(formData.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)){ chargeOrderInfo.setOperatorId(formData.getOperatorId()); chargeOrderInfo.setPreAmt(formData.getChannelPreAmt()); } //判断用户是否绑定企业 if (ObjectUtil.isNotEmpty(userInfo.getFirmId())) { FirmInfo firmInfo = firmInfoMapper.selectById(userInfo.getFirmId()); if(firmInfo != null && Objects.equals(firmInfo.getStatus(), SystemConstants.STATUS_ONE)) { chargeOrderInfo.setFirmId(firmInfo.getId()); chargeOrderInfo.setOrderType(SystemConstants.STATUS_ONE); } } //优惠券 if(ObjectUtil.isNotEmpty(formData.getCouponId())) { Coupon coupon = couponMapper.selectById(formData.getCouponId()); if(coupon != null && Objects.equals(coupon.getStatus(), SystemConstants.STATUS_ONE)) { CouponTemplate couponTemplate = couponTemplateMapper.selectById(coupon.getTemplateId()); chargeOrderInfo.setCouponId(coupon.getId()); chargeOrderInfo.setCouponPrice(couponTemplate.getDiscountPrice()); } } //启动充电 StartChargingRequestDTO requestDTO = new StartChargingRequestDTO(); requestDTO .setStartChargeSeq(seq) .setConnectorID(formData.getConnectorId()) .setPhoneNum(userInfo.getPhone()) //预支付金额 .setChargingAmt(preAmt.toString()) ; //车牌号 if(ObjectUtil.isNotEmpty(formData.getPlateNum())) { chargeOrderInfo.setPlateNum(formData.getPlateNum()); requestDTO.setPlateNum(formData.getPlateNum()); }else { //获取当前用户的默认车牌号 UserVehicle userVehicle = userVehicleMapper.selectOne(Wrappers.lambdaQuery(UserVehicle.class).eq(UserVehicle::getUserId, userId).eq(UserVehicle::getIsDefault, SystemConstants.STATUS_ONE).last("limit 1")); if (ObjectUtil.isNotEmpty(userVehicle)) { requestDTO.setPlateNum(userVehicle.getLicensePlate()); } } StartChargingResponseVO startChargingResponseVO = chargingBusinessService.startCharging(requestDTO); if (!Objects.equals(startChargingResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) { throw new BusinessException(startChargingResponseVO.getFailReasonMsg()); } //保存订单 this.save(chargeOrderInfo); appInvokeChargeVO.setChargeOrderNo(chargeOrderNo); appInvokeChargeVO.setChargeOrderId(chargeOrderInfo.getId()); return appInvokeChargeVO; } catch (Exception e) { log.error("启动充电失败,系统错误", e); throw new BusinessException("启动充电失败 !" + e.getMessage()); } } /** * 渠道方启动充电 * * @param formData * @return */ public AppChargeVO channelInvokeCharge(AppInvokeChargeForm formData) throws JsonProcessingException { if (StrUtil.isBlank(formData.getOperatorId())) { throw new BusinessException("运营商ID不能为空"); } if (StrUtil.isBlank(formData.getChannelOrderNo())) { throw new BusinessException("渠道订单号不能为空"); } if (StrUtil.isBlank(formData.getChannelUserPhone())) { throw new BusinessException("渠道用户手机号不能为空"); } if (formData.getChannelPreAmt() == null || formData.getChannelPreAmt().compareTo(BigDecimal.ZERO) <= 0) { throw new BusinessException("渠道预支付金额必须大于0"); } ThirdPartyInfo thirdPartyInfo = thirdPartyInfoMapper.selectOne( Wrappers.lambdaQuery(ThirdPartyInfo.class) .eq(ThirdPartyInfo::getOperatorId, formData.getOperatorId()) .last("limit 1")); if (thirdPartyInfo == null) { throw new BusinessException("渠道运营商不存在"); } UserInfo channelUser = userInfoService.getUserInfoByPhoneAndOperatorId( formData.getChannelUserPhone(), thirdPartyInfo.getId() ); if (channelUser == null) { channelUser = userInfoService.registerThirdPartyUserByPhone( formData.getChannelUserPhone(), thirdPartyInfo.getId() ); } if (channelUser == null) { throw new BusinessException("渠道用户不存在且自动注册失败"); } String seq = ConnectivityConstants.OPERATOR_ID + formData.getChannelOrderNo(); //请求设备认证 EquipmentAuthResponseVO equipmentAuthResponseVO = chargingBusinessService.queryEquipAuth(seq, formData.getConnectorId()); if (Objects.equals(equipmentAuthResponseVO.getSuccStat(), SystemConstants.STATUS_ONE)) { throw new BusinessException("设备认证失败,请检查枪是否正确接入充电槽!"); } else { log.info("设备认证成功,设备认证流水号:{}", equipmentAuthResponseVO.getEquipAuthSeq()); } //创建订单 ChargeOrderInfo chargeOrderInfo = new ChargeOrderInfo(); Long currentUserId = SecurityUtils.getUserId(); chargeOrderInfo.setUserId(channelUser.getId()); User user = currentUserId == null ? null : userMapper.selectById(currentUserId); if(ObjectUtil.isNotEmpty(user)){ FirmInfo firmInfo = firmInfoMapper.selectOne(Wrappers.lambdaQuery(FirmInfo.class).eq(FirmInfo::getDeptId, user.getDeptId()).last("limit 1")); if(firmInfo != null) { chargeOrderInfo.setFirmId(firmInfo.getId()); } } chargeOrderInfo.setOrderType(SystemConstants.CHARGE_ORDER_TYPE_CHANNEL); chargeOrderInfo.setConnectorId(formData.getConnectorId()); chargeOrderInfo.setEquipmentId(formData.getEquipmentId()); chargeOrderInfo.setEquipAuthSeq(seq); chargeOrderInfo.setChargeOrderNo(formData.getChannelOrderNo()); chargeOrderInfo.setStartChargeSeq(seq); chargeOrderInfo.setPhoneNum(formData.getChannelUserPhone()); chargeOrderInfo.setThirdPartyStationId(formData.getStationId()); //渠道手机号 if(ObjectUtil.isNotEmpty(formData.getChannelUserPhone())){ chargeOrderInfo.setPhoneNum(formData.getChannelUserPhone()); } //预支付金额 chargeOrderInfo.setPreAmt(formData.getChannelPreAmt()); //渠道方订单设置运营商ID chargeOrderInfo.setOperatorId(formData.getOperatorId()); //启动充电 StartChargingRequestDTO requestDTO = new StartChargingRequestDTO(); requestDTO .setStartChargeSeq(seq) .setConnectorID(formData.getConnectorId()) .setPhoneNum(formData.getChannelUserPhone()) //预支付金额 .setChargingAmt(formData.getChannelPreAmt().toString()); //车牌号 if(ObjectUtil.isNotEmpty(formData.getPlateNum())) { chargeOrderInfo.setPlateNum(formData.getPlateNum()); requestDTO.setPlateNum(formData.getPlateNum()); } StartChargingResponseVO startChargingResponseVO = chargingBusinessService.startCharging(requestDTO); if (!Objects.equals(startChargingResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) { throw new BusinessException(startChargingResponseVO.getFailReasonMsg()); } //保存订单 this.save(chargeOrderInfo); AppChargeVO appInvokeChargeVO = new AppChargeVO(); appInvokeChargeVO.setChargeOrderId(chargeOrderInfo.getId()); appInvokeChargeVO.setChargeOrderNo(chargeOrderInfo.getChargeOrderNo()); appInvokeChargeVO.setStatus(SystemConstants.STATUS_ZERO); return appInvokeChargeVO; } @Override public AppChargeVO stopCharge(AppStopChargeForm formData) { try { AppChargeVO appInvokeChargeVO = new AppChargeVO(); //订单 ChargeOrderInfo chargeOrderInfo = this.getOne(Wrappers.lambdaQuery(ChargeOrderInfo.class) .eq(ChargeOrderInfo::getChargeOrderNo, formData.getChargeOrderNo()) .eq(StrUtil.isNotBlank(formData.getOperatorId()), ChargeOrderInfo::getOperatorId, formData.getOperatorId()) .last("limit 1")); if(ObjectUtil.isEmpty(chargeOrderInfo)){ throw new BusinessException("订单不存在"); } StopChargingOperationResponseVO stopChargingOperationResponseVO = chargingBusinessService.stopCharging(chargeOrderInfo.getStartChargeSeq(), chargeOrderInfo.getConnectorId()); if (!Objects.equals(stopChargingOperationResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) { throw new BusinessException("停止充电失败,请稍后再重试!"); } chargeOrderInfo.setStopType(1); // 1-主动停止 this.updateById(chargeOrderInfo); appInvokeChargeVO.setChargeOrderId(chargeOrderInfo.getId()); appInvokeChargeVO.setChargeOrderNo(chargeOrderInfo.getChargeOrderNo()); return appInvokeChargeVO; } catch (Exception e) { log.error("停止充电失败,系统错误", e); throw new BusinessException("停止充电失败,请稍后再重试!"); } } @Override public void orderSettlement(Long chargeOrderId) { ChargeOrderInfo chargeOrderInfo = this.getById(chargeOrderId); Long userId = chargeOrderInfo.getUserId(); //平台收费总金额 BigDecimal totalCharge = chargeOrderInfo.getRealCost(); //加积分 userAccountService.update(Wrappers.lambdaUpdate(UserAccount.class) .eq(UserAccount::getUserId, userId) .setSql("`integral` = `integral` +" + totalCharge) ); //账户变动及日志记录 userAccountService.updateAccountBalanceAndLog( userId, totalCharge, SystemConstants.CHANGE_TYPE_REDUCE, SystemConstants.CHARGE_DEDUCT_NOTE, chargeOrderInfo.getId() ); } @Override public ChargeOrderInfoVO queryOrder(String chargeOrderNo) { return baseMapper.queryOrder(chargeOrderNo); } /** * 获取充电订单信息导出列表 * * @param queryParams 查询参数 * @return 充电订单信息导出列表 */ @Override public List listExportChargeOrderInfo(ChargeOrderInfoQuery queryParams) { return baseMapper.listExportChargeOrderInfo(queryParams); } /** * 创建商户订单号 * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一 * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位 * * @param head 例如 商品-SP 退款-TK 等等 * @param id 用户id * @return */ public String generateNo(String head, Long id) { StringBuilder uid = new StringBuilder(id.toString()); Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); int length = uid.length(); for (int i = 0; i < 9 - length; i++) { uid.insert(0, "0"); } return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000); } /** * 修复未处理的充电订单 * 优先通过third_party_api_log表获取推送充电订单信息, * 若没有则通过third_party_charge_status表查询total_power>0的数据 * 重新处理这些订单:更新状态、计算费用、扣减余额 */ @Override @Transactional(rollbackFor = Exception.class) public String repairUnprocessedOrders() { log.info("开始修复未处理的充电订单..."); // 1. 查询状态为5(未成功充电)的订单 List unprocessedOrders = this.list(Wrappers.lambdaQuery() .eq(ChargeOrderInfo::getStatus, 5) .isNotNull(ChargeOrderInfo::getStartChargeSeq)); if (unprocessedOrders.isEmpty()) { log.info("没有找到需要修复的订单"); return "没有找到需要修复的订单"; } int totalCount = 0; int successCount = 0; int skipCount = 0; int apiLogCount = 0; int chargeStatusCount = 0; List failedOrders = new ArrayList<>(); for (ChargeOrderInfo order : unprocessedOrders) { totalCount++; try { // 2. 优先从third_party_api_log表查询推送充电订单信息 ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息") .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq()) .orderByDesc(ThirdPartyApiLog::getCreatedTime) .last("LIMIT 1")); if (apiLog != null && apiLog.getDecryptedRequestData() != null) { // 使用API日志中的推送数据处理订单 boolean success = processOrderFromApiLog(order, apiLog); if (success) { // 设置补偿状态为已补偿 order.setCompensateStatus(1); this.updateById(order); successCount++; apiLogCount++; log.info("订单{}通过API日志修复成功", order.getChargeOrderNo()); continue; } } // 3. 备选方案:从third_party_charge_status表查询 ThirdPartyChargeStatus chargeStatus = chargeStatusMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyChargeStatus::getStartChargeSeq, order.getStartChargeSeq())); if (chargeStatus == null || chargeStatus.getTotalPower() == null || chargeStatus.getTotalPower().compareTo(BigDecimal.ZERO) <= 0) { log.info("订单{}无有效充电数据,设置为异常无须补偿", order.getChargeOrderNo()); // 设置补偿状态为异常无须补偿 order.setCompensateStatus(2); this.updateById(order); skipCount++; continue; } log.info("开始通过充电状态表修复订单: {}, 充电量: {}", order.getChargeOrderNo(), chargeStatus.getTotalPower()); // 4. 获取站点信息 ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId()) .last("LIMIT 1")); if (connectorInfo == null) { log.warn("订单{}找不到充电接口信息", order.getChargeOrderNo()); failedOrders.add(order.getChargeOrderNo()); continue; } ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId()) .last("LIMIT 1")); if (stationInfo == null) { log.warn("订单{}找不到站点信息", order.getChargeOrderNo()); failedOrders.add(order.getChargeOrderNo()); continue; } // 5. 设置第三方费用信息 order.setTotalCharge(chargeStatus.getTotalPower()); order.setThirdPartyTotalCost(chargeStatus.getTotalMoney() != null ? chargeStatus.getTotalMoney() : BigDecimal.ZERO); order.setThirdPartyServerfee(chargeStatus.getServiceMoney() != null ? chargeStatus.getServiceMoney() : BigDecimal.ZERO); order.setThirdPartyElecfee(chargeStatus.getElecMoney() != null ? chargeStatus.getElecMoney() : BigDecimal.ZERO); if (StrUtil.isNotBlank(chargeStatus.getChargeDetails())) { order.setChargeDetails(chargeStatus.getChargeDetails()); } // 6. 计算平台服务费 BigDecimal serviceFee = calculateServiceFee(order, chargeStatus, stationInfo); // 7. 更新订单信息 order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP)); order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost())); order.setStatus(SystemConstants.STATUS_THREE); // 已完成 // 设置充电时间 if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); order.setStartTime(chargeStatus.getStartTime().format(formatter)); order.setEndTime(chargeStatus.getEndTime().format(formatter)); order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime())); } // 设置修复备注 order.setRemark("通过补偿修复处理"); // 设置补偿状态为已补偿 order.setCompensateStatus(1); this.updateById(order); // 8. 执行账户余额扣减(仅平台订单和企业订单) if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM) || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) { orderSettlement(order.getId()); log.info("订单{}余额扣减完成", order.getChargeOrderNo()); } successCount++; chargeStatusCount++; log.info("订单{}通过充电状态表修复成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost()); } catch (Exception e) { log.error("修复订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e); failedOrders.add(order.getChargeOrderNo()); } } String result = String.format("修复完成!总计: %d, 成功: %d (API日志: %d, 充电状态: %d), 跳过: %d, 失败: %d", totalCount, successCount, apiLogCount, chargeStatusCount, skipCount, failedOrders.size()); if (!failedOrders.isEmpty()) { result += ", 失败订单: " + String.join(",", failedOrders); } log.info(result); return result; } /** * 通过API日志中的推送数据处理订单 * @param order 订单信息 * @param apiLog API日志 * @return 是否处理成功 */ private boolean processOrderFromApiLog(ChargeOrderInfo order, ThirdPartyApiLog apiLog) { try { JsonNode jsonNode = objectMapper.readTree(apiLog.getDecryptedRequestData()); // 解析推送数据 String startChargeSeq = getJsonTextValue(jsonNode, "StartChargeSeq"); if (startChargeSeq == null || !startChargeSeq.equals(order.getStartChargeSeq())) { log.warn("订单{}的StartChargeSeq不匹配,跳过API日志处理", order.getChargeOrderNo()); return false; } String totalPowerStr = getJsonTextValue(jsonNode, "TotalPower"); if (totalPowerStr == null || new BigDecimal(totalPowerStr).compareTo(BigDecimal.ZERO) <= 0) { log.info("订单{}的API日志充电量为0,跳过处理", order.getChargeOrderNo()); return false; } log.info("开始通过API日志修复订单: {}, 充电量: {}", order.getChargeOrderNo(), totalPowerStr); // 设置充电信息 order.setTotalCharge(new BigDecimal(totalPowerStr)); order.setStopReason(getJsonTextValue(jsonNode, "StopReason")); order.setStartTime(getJsonTextValue(jsonNode, "StartTime")); order.setEndTime(getJsonTextValue(jsonNode, "EndTime")); order.setChargeDetails(jsonNode.toString()); // 第三方费用 String totalMoney = getJsonTextValue(jsonNode, "TotalMoney"); String totalSeviceMoney = getJsonTextValue(jsonNode, "TotalSeviceMoney"); String totalElecMoney = getJsonTextValue(jsonNode, "TotalElecMoney"); order.setThirdPartyTotalCost(totalMoney != null ? new BigDecimal(totalMoney) : BigDecimal.ZERO); order.setThirdPartyServerfee(totalSeviceMoney != null ? new BigDecimal(totalSeviceMoney) : BigDecimal.ZERO); order.setThirdPartyElecfee(totalElecMoney != null ? new BigDecimal(totalElecMoney) : BigDecimal.ZERO); // 获取连接器和站点信息 String connectorId = getJsonTextValue(jsonNode, "ConnectorID"); if (connectorId == null) { connectorId = order.getConnectorId(); } ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyConnectorInfo::getConnectorId, connectorId) .last("LIMIT 1")); if (connectorInfo == null) { log.warn("订单{}找不到充电接口信息", order.getChargeOrderNo()); return false; } ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId()) .last("LIMIT 1")); if (stationInfo == null) { log.warn("订单{}找不到站点信息", order.getChargeOrderNo()); return false; } // 计算平台服务费(从ChargeDetails中解析) BigDecimal serviceFee = calculateServiceFeeFromChargeDetails(order, jsonNode, stationInfo); // 更新订单信息 order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP)); order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost())); order.setStatus(SystemConstants.STATUS_THREE); // 已完成 // 计算充电时间 if (order.getStartTime() != null && order.getEndTime() != null) { order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime())); } // 设置修复备注 order.setRemark("通过补偿修复处理"); this.updateById(order); // 执行账户余额扣减(仅平台订单和企业订单) if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM) || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) { orderSettlement(order.getId()); log.info("订单{}余额扣减完成", order.getChargeOrderNo()); } log.info("订单{}通过API日志处理成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost()); return true; } catch (Exception e) { log.error("通过API日志处理订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e); return false; } } /** * 从ChargeDetails计算平台服务费 */ private BigDecimal calculateServiceFeeFromChargeDetails(ChargeOrderInfo order, JsonNode jsonNode, ThirdPartyStationInfo stationInfo) { BigDecimal serviceFee = BigDecimal.ZERO; JsonNode chargeDetails = jsonNode.get("ChargeDetails"); if (chargeDetails != null && chargeDetails.isArray()) { for (JsonNode node : chargeDetails) { String itemFlag = getJsonTextValue(node, "ItemFlag"); String detailPowerStr = getJsonTextValue(node, "DetailPower"); if (itemFlag != null && detailPowerStr != null) { BigDecimal detailPower = new BigDecimal(detailPowerStr); PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.lambdaQuery() .eq(PolicyFee::getStationInfoId, stationInfo.getId()) .eq(PolicyFee::getPeriodFlag, Integer.parseInt(itemFlag)) .last("LIMIT 1")); if (policyFee != null && policyFee.getOpFee() != null) { serviceFee = serviceFee.add(policyFee.getOpFee().multiply(detailPower)); } } } } else { // 无明细时,使用最高费用时段计算 log.info("订单{}无充电明细,使用简化计算", order.getChargeOrderNo()); ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, order.getConnectorId()) .last("LIMIT 1")); if (pricePolicy != null) { List policyInfos = thirdPartyPolicyInfoMapper.selectList( Wrappers.lambdaQuery() .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId()) .eq(ThirdPartyPolicyInfo::getIsDeleted, 0)); PolicyFee maxPolicyFee = null; for (ThirdPartyPolicyInfo policyInfo : policyInfos) { PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.lambdaQuery() .eq(PolicyFee::getStationInfoId, stationInfo.getId()) .eq(PolicyFee::getPeriodFlag, policyInfo.getPeriodFlag()) .last("LIMIT 1")); if (policyFee != null && policyFee.getOpFee() != null) { if (maxPolicyFee == null || policyFee.getOpFee().compareTo(maxPolicyFee.getOpFee()) > 0) { maxPolicyFee = policyFee; } } } if (maxPolicyFee != null) { serviceFee = maxPolicyFee.getOpFee().multiply(order.getTotalCharge()); } } } return serviceFee; } /** * 安全获取JSON节点的文本值 */ private String getJsonTextValue(JsonNode node, String fieldName) { if (node == null || !node.has(fieldName) || node.get(fieldName).isNull()) { return null; } return node.get(fieldName).asText(); } /** * 计算平台服务费 */ private BigDecimal calculateServiceFee(ChargeOrderInfo order, ThirdPartyChargeStatus chargeStatus, ThirdPartyStationInfo stationInfo) { BigDecimal serviceFee = BigDecimal.ZERO; // 查询价格策略 ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, chargeStatus.getConnectorId()) .last("LIMIT 1")); if (pricePolicy == null) { log.warn("订单{}找不到价格策略", order.getChargeOrderNo()); return serviceFee; } // 查询时段信息 List allPolicyInfos = thirdPartyPolicyInfoMapper.selectList( Wrappers.lambdaQuery() .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId()) .eq(ThirdPartyPolicyInfo::getIsDeleted, 0) .orderByAsc(ThirdPartyPolicyInfo::getStartTime)); if (allPolicyInfos.isEmpty()) { log.warn("订单{}找不到时段信息", order.getChargeOrderNo()); return serviceFee; } // 根据充电时间段匹配费用 if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) { String chargeStartTimeStr = chargeStatus.getStartTime().format(DateTimeFormatter.ofPattern("HHmmss")); String chargeEndTimeStr = chargeStatus.getEndTime().format(DateTimeFormatter.ofPattern("HHmmss")); // 找到充电时间跨越的所有时段,使用最高费用时段计算 PolicyFee maxPolicyFee = null; for (ThirdPartyPolicyInfo policyInfo : allPolicyInfos) { if (chargeStartTimeStr.compareTo(policyInfo.getStartTime()) >= 0 || chargeEndTimeStr.compareTo(policyInfo.getStartTime()) >= 0) { PolicyFee policyFee = policyFeeMapper.selectOne( Wrappers.lambdaQuery() .eq(PolicyFee::getStationInfoId, stationInfo.getId()) .eq(PolicyFee::getPeriodFlag, policyInfo.getPeriodFlag()) .last("LIMIT 1")); if (policyFee != null && policyFee.getOpFee() != null) { if (maxPolicyFee == null || policyFee.getOpFee().compareTo(maxPolicyFee.getOpFee()) > 0) { maxPolicyFee = policyFee; } } } } if (maxPolicyFee != null) { serviceFee = maxPolicyFee.getOpFee().multiply(order.getTotalCharge()); } } return serviceFee; } /** * 根据充电订单号修复已完成订单 * 修复状态为3(已完成)的订单,重新处理订单状态变更、数据修改及余额扣减 * 优先通过third_party_api_log表获取推送数据,备选通过third_party_charge_status表查询 */ @Override @Transactional(rollbackFor = Exception.class) public String repairOrderByOrderNo(String chargeOrderNo) { log.info("开始根据订单号修复订单: {}", chargeOrderNo); // 1. 查询订单 ChargeOrderInfo order = this.getOne(Wrappers.lambdaQuery() .eq(ChargeOrderInfo::getChargeOrderNo, chargeOrderNo) .last("LIMIT 1")); if (order == null) { return "订单不存在: " + chargeOrderNo; } log.info("开始修复订单: {}, 当前状态: {}", chargeOrderNo, order.getStatus()); // 2. 检查订单是否需要修复(只有充电度数、平台费用、三方费用同时为0扏null的订单才处理) boolean needRepair = isZeroOrNull(order.getTotalCharge()) && isZeroOrNull(order.getRealCost()) && isZeroOrNull(order.getRealServiceCost()) && isZeroOrNull(order.getThirdPartyTotalCost()) && isZeroOrNull(order.getThirdPartyServerfee()); if (!needRepair) { return "订单" + chargeOrderNo + "已有充电数据,无需修复。充电度数:" + order.getTotalCharge() + ", 平台收取金额:" + order.getRealCost() + ", 平台服务费:" + order.getRealServiceCost() + ", 三方消费总额:" + order.getThirdPartyTotalCost() + ", 三方服务费:" + order.getThirdPartyServerfee(); } if (order.getStartChargeSeq() == null) { return "订单缺少StartChargeSeq,无法修复"; } try { // 3. 优先从third_party_api_log表查询推送充电订单信息 ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息") .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq()) .orderByDesc(ThirdPartyApiLog::getCreatedTime) .last("LIMIT 1")); if (apiLog != null && apiLog.getDecryptedRequestData() != null) { // 使用API日志中的推送数据处理订单 boolean success = repairOrderFromApiLogByOrderNo(order, apiLog); if (success) { // 设置补偿状态为已补偿 order.setCompensateStatus(1); order.setStatus(3); this.updateById(order); log.info("订单{}通过API日志修复成功", chargeOrderNo); return "订单" + chargeOrderNo + "通过API日志修复成功,实际费用: " + order.getRealCost(); } } // 4. 备选方案:从third_party_charge_status表查询 ThirdPartyChargeStatus chargeStatus = chargeStatusMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyChargeStatus::getStartChargeSeq, order.getStartChargeSeq())); if (chargeStatus == null || chargeStatus.getTotalPower() == null || chargeStatus.getTotalPower().compareTo(BigDecimal.ZERO) <= 0) { // 设置补偿状态为异常无须补偿 order.setCompensateStatus(2); order.setStatus(5); this.updateById(order); return "订单" + chargeOrderNo + "无有效充电数据,设置为异常无须补偿"; } log.info("开始通过充电状态表修复订单: {}, 充电量: {}", chargeOrderNo, chargeStatus.getTotalPower()); // 5. 获取站点信息 ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId()) .last("LIMIT 1")); if (connectorInfo == null) { return "订单" + chargeOrderNo + "找不到充电接口信息"; } ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId()) .last("LIMIT 1")); if (stationInfo == null) { return "订单" + chargeOrderNo + "找不到站点信息"; } // 6. 设置第三方费用信息 order.setTotalCharge(chargeStatus.getTotalPower()); order.setThirdPartyTotalCost(chargeStatus.getTotalMoney() != null ? chargeStatus.getTotalMoney() : BigDecimal.ZERO); order.setThirdPartyServerfee(chargeStatus.getServiceMoney() != null ? chargeStatus.getServiceMoney() : BigDecimal.ZERO); order.setThirdPartyElecfee(chargeStatus.getElecMoney() != null ? chargeStatus.getElecMoney() : BigDecimal.ZERO); if (StrUtil.isNotBlank(chargeStatus.getChargeDetails())) { order.setChargeDetails(chargeStatus.getChargeDetails()); } // 7. 计算平台服务费 BigDecimal serviceFee = calculateServiceFee(order, chargeStatus, stationInfo); // 8. 更新订单信息 order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP)); order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost())); // 设置充电时间 if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); order.setStartTime(chargeStatus.getStartTime().format(formatter)); order.setEndTime(chargeStatus.getEndTime().format(formatter)); order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime())); } // 设置修复备注 order.setRemark("通过补偿修复处理"); // 设置补偿状态为已补偿 order.setCompensateStatus(1); order.setStatus(3); this.updateById(order); // 9. 执行账户余额扣减(仅平台订单和企业订单) if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM) || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) { orderSettlement(order.getId()); log.info("订单{}余额扣减完成", chargeOrderNo); } log.info("订单{}通过充电状态表修复成功,实际费用: {}", chargeOrderNo, order.getRealCost()); return "订单" + chargeOrderNo + "通过充电状态表修复成功,实际费用: " + order.getRealCost(); } catch (Exception e) { log.error("修复订单{}失败: {}", chargeOrderNo, e.getMessage(), e); throw new BusinessException("修复订单失败: " + e.getMessage()); } } /** * 通过API日志修复单个订单(根据订单号) * @param order 订单信息 * @param apiLog API日志 * @return 是否处理成功 */ private boolean repairOrderFromApiLogByOrderNo(ChargeOrderInfo order, ThirdPartyApiLog apiLog) { try { JsonNode jsonNode = objectMapper.readTree(apiLog.getDecryptedRequestData()); // 解析推送数据 String startChargeSeq = getJsonTextValue(jsonNode, "StartChargeSeq"); if (startChargeSeq == null || !startChargeSeq.equals(order.getStartChargeSeq())) { log.warn("订单{}的StartChargeSeq不匹配,跳过API日志处理", order.getChargeOrderNo()); return false; } String totalPowerStr = getJsonTextValue(jsonNode, "TotalPower"); if (totalPowerStr == null || new BigDecimal(totalPowerStr).compareTo(BigDecimal.ZERO) <= 0) { log.info("订单{}的API日志充电量为0,跳过处理", order.getChargeOrderNo()); return false; } log.info("开始通过API日志修复订单: {}, 充电量: {}", order.getChargeOrderNo(), totalPowerStr); // 设置充电信息 order.setTotalCharge(new BigDecimal(totalPowerStr)); order.setStopReason(getJsonTextValue(jsonNode, "StopReason")); order.setStartTime(getJsonTextValue(jsonNode, "StartTime")); order.setEndTime(getJsonTextValue(jsonNode, "EndTime")); order.setChargeDetails(jsonNode.toString()); // 第三方费用 String totalMoney = getJsonTextValue(jsonNode, "TotalMoney"); String totalSeviceMoney = getJsonTextValue(jsonNode, "TotalSeviceMoney"); String totalElecMoney = getJsonTextValue(jsonNode, "TotalElecMoney"); order.setThirdPartyTotalCost(totalMoney != null ? new BigDecimal(totalMoney) : BigDecimal.ZERO); order.setThirdPartyServerfee(totalSeviceMoney != null ? new BigDecimal(totalSeviceMoney) : BigDecimal.ZERO); order.setThirdPartyElecfee(totalElecMoney != null ? new BigDecimal(totalElecMoney) : BigDecimal.ZERO); // 获取连接器和站点信息 String connectorId = getJsonTextValue(jsonNode, "ConnectorID"); if (connectorId == null) { connectorId = order.getConnectorId(); } ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyConnectorInfo::getConnectorId, connectorId) .last("LIMIT 1")); if (connectorInfo == null) { log.warn("订单{}找不到充电接口信息", order.getChargeOrderNo()); return false; } ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId()) .last("LIMIT 1")); if (stationInfo == null) { log.warn("订单{}找不到站点信息", order.getChargeOrderNo()); return false; } // 计算平台服务费(从ChargeDetails中解析) BigDecimal serviceFee = calculateServiceFeeFromChargeDetails(order, jsonNode, stationInfo); // 更新订单信息 order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP)); order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost())); // 计算充电时间 if (order.getStartTime() != null && order.getEndTime() != null) { order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime())); } // 设置修复备注 order.setRemark("通过补偿修复处理"); this.updateById(order); // 执行账户余额扣减(仅平台订单和企业订单) if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM) || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) { orderSettlement(order.getId()); log.info("订单{}余额扣减完成", order.getChargeOrderNo()); } // 渠道方订单:推送充电订单信息 + 渠道方账户余额扣减 if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) { compensateChannelOrder(order, apiLog.getDecryptedRequestData()); } log.info("订单{}通过API日志处理成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost()); return true; } catch (Exception e) { log.error("通过API日志处理订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e); return false; } } /** * 判断BigDecimal是否为null或0 * @param value 要判断的值 * @return 如果为null或0返回true,否则返回false */ private boolean isZeroOrNull(BigDecimal value) { return value == null || value.compareTo(BigDecimal.ZERO) == 0; } /** * 补偿未处理的充电订单(定时任务调用) * 查找状态为3(已完成)或5(未成功充电)且充电数据为0的订单 */ @Override @Transactional(rollbackFor = Exception.class) public String compensateUnprocessedOrders() { log.info("开始执行充电订单补偿定时任务..."); // 1. 查询状态为3(已完成)或5(未成功充电)且充电数据为0的订单 List unprocessedOrders = this.list(Wrappers.lambdaQuery() .in(ChargeOrderInfo::getStatus, 3, 5) .isNotNull(ChargeOrderInfo::getStartChargeSeq) // 充电度数为0或null .and(wrapper -> wrapper .isNull(ChargeOrderInfo::getTotalCharge) .or() .eq(ChargeOrderInfo::getTotalCharge, BigDecimal.ZERO)) // 平台实际收取金额为0或null .and(wrapper -> wrapper .isNull(ChargeOrderInfo::getRealCost) .or() .eq(ChargeOrderInfo::getRealCost, BigDecimal.ZERO)) // 平台总服务费为0或null .and(wrapper -> wrapper .isNull(ChargeOrderInfo::getRealServiceCost) .or() .eq(ChargeOrderInfo::getRealServiceCost, BigDecimal.ZERO)) // 三方充电消费总额为0或null .and(wrapper -> wrapper .isNull(ChargeOrderInfo::getThirdPartyTotalCost) .or() .eq(ChargeOrderInfo::getThirdPartyTotalCost, BigDecimal.ZERO)) // 三方充电服务费为0或null .and(wrapper -> wrapper .isNull(ChargeOrderInfo::getThirdPartyServerfee) .or() .eq(ChargeOrderInfo::getThirdPartyServerfee, BigDecimal.ZERO)) // 补偿状态为0或null .and(wrapper -> wrapper .isNull(ChargeOrderInfo::getCompensateStatus) .or() .eq(ChargeOrderInfo::getCompensateStatus, 0)) ); if (unprocessedOrders.isEmpty()) { log.info("充电订单补偿定时任务: 没有找到需要补偿的订单"); return "没有找到需要补偿的订单"; } log.info("充电订单补偿定时任务: 找到{}个需要补偿的订单", unprocessedOrders.size()); int totalCount = 0; int successCount = 0; int skipCount = 0; int apiLogCount = 0; int chargeStatusCount = 0; List failedOrders = new ArrayList<>(); for (ChargeOrderInfo order : unprocessedOrders) { totalCount++; try { // 2. 优先从third_party_api_log表查询推送充电订单信息 ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息") .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq()) .orderByDesc(ThirdPartyApiLog::getCreatedTime) .last("LIMIT 1")); if (apiLog != null && apiLog.getDecryptedRequestData() != null) { // 使用API日志中的推送数据处理订单 boolean success = processOrderFromApiLog(order, apiLog); if (success) { // 设置补偿状态为已补偿 order.setCompensateStatus(1); this.updateById(order); successCount++; apiLogCount++; log.info("补偿任务: 订单{}通过API日志补偿成功", order.getChargeOrderNo()); continue; } } // 3. 备选方案:从third_party_charge_status表查询 ThirdPartyChargeStatus chargeStatus = chargeStatusMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyChargeStatus::getStartChargeSeq, order.getStartChargeSeq())); if (chargeStatus == null || chargeStatus.getTotalPower() == null || chargeStatus.getTotalPower().compareTo(BigDecimal.ZERO) <= 0) { log.info("补偿任务: 订单{}无有效充电数据,设置为异常无须补偿", order.getChargeOrderNo()); // 设置补偿状态为异常无须补偿 order.setCompensateStatus(2); this.updateById(order); skipCount++; continue; } log.info("补偿任务: 开始通过充电状态表补偿订单: {}, 充电量: {}", order.getChargeOrderNo(), chargeStatus.getTotalPower()); // 4. 获取站点信息 ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId()) .last("LIMIT 1")); if (connectorInfo == null) { log.warn("补偿任务: 订单{}找不到充电接口信息", order.getChargeOrderNo()); failedOrders.add(order.getChargeOrderNo()); continue; } ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId()) .last("LIMIT 1")); if (stationInfo == null) { log.warn("补偿任务: 订单{}找不到站点信息", order.getChargeOrderNo()); failedOrders.add(order.getChargeOrderNo()); continue; } // 5. 设置第三方费用信息 order.setTotalCharge(chargeStatus.getTotalPower()); order.setThirdPartyTotalCost(chargeStatus.getTotalMoney() != null ? chargeStatus.getTotalMoney() : BigDecimal.ZERO); order.setThirdPartyServerfee(chargeStatus.getServiceMoney() != null ? chargeStatus.getServiceMoney() : BigDecimal.ZERO); order.setThirdPartyElecfee(chargeStatus.getElecMoney() != null ? chargeStatus.getElecMoney() : BigDecimal.ZERO); if (StrUtil.isNotBlank(chargeStatus.getChargeDetails())) { order.setChargeDetails(chargeStatus.getChargeDetails()); } // 6. 计算平台服务费 BigDecimal serviceFee = calculateServiceFee(order, chargeStatus, stationInfo); // 7. 更新订单信息 order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP)); order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost())); order.setStatus(SystemConstants.STATUS_THREE); // 已完成 // 设置充电时间 if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); order.setStartTime(chargeStatus.getStartTime().format(formatter)); order.setEndTime(chargeStatus.getEndTime().format(formatter)); order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime())); } // 设置修复备注 order.setRemark("通过补偿修复处理"); this.updateById(order); // 8. 执行账户余额扣减(仅平台订单和企业订单) if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM) || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) { orderSettlement(order.getId()); log.info("补偿任务: 订单{}余额扣减完成", order.getChargeOrderNo()); } // 渠道方订单:推送充电订单信息 + 渠道方账户余额扣减 if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) { compensateChannelOrder(order, null); } successCount++; chargeStatusCount++; log.info("补偿任务: 订单{}通过充电状态表补偿成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost()); } catch (Exception e) { log.error("补偿任务: 补偿订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e); failedOrders.add(order.getChargeOrderNo()); } } String result = String.format("充电订单补偿完成!总计: %d, 成功: %d (API日志: %d, 充电状态: %d), 跳过: %d, 失败: %d", totalCount, successCount, apiLogCount, chargeStatusCount, skipCount, failedOrders.size()); if (!failedOrders.isEmpty()) { result += ", 失败订单: " + String.join(",", failedOrders); } log.info(result); return result; } /** * 渠道方订单补偿:推送充电订单信息给渠道方 + 渠道方账户余额扣减 * 推送失败只记日志,不回滚补偿 * * @param order 补偿完成的订单 * @param apiLogData API日志原始数据(可为null,则从订单字段构建推送数据) */ private void compensateChannelOrder(ChargeOrderInfo order, String apiLogData) { try { FirmInfo firmInfo = firmInfoMapper.selectById(order.getFirmId()); if (firmInfo == null) { log.warn("补偿任务: 订单{}找不到渠道方信息,firmId: {}", order.getChargeOrderNo(), order.getFirmId()); return; } // 1. 推送充电订单信息给渠道方 pushChargeOrderInfoToChannel(order, firmInfo, apiLogData); // 2. 渠道方账户余额扣减 deductChannelFirmBalance(order, firmInfo); } catch (Exception e) { log.error("补偿任务: 渠道方订单{}补偿处理异常: {}", order.getChargeOrderNo(), e.getMessage(), e); } } /** * 推送充电订单信息给渠道方(参考 /notification_charge_order_info 接口推送格式) * 失败只记日志,不影响补偿结果 */ private void pushChargeOrderInfoToChannel(ChargeOrderInfo order, FirmInfo firmInfo, String apiLogData) { try { // 构建推送数据 Map pushData; if (apiLogData != null) { // 使用API日志原始数据 pushData = objectMapper.readValue(apiLogData, Map.class); } else { // 从order字段构建推送数据 pushData = buildCompensationPushData(order); } normalizeCompensationPushData(pushData, order); pushData.put("chargeOrderNo", order.getChargeOrderNo()); String url = firmInfo.getChannelUrl() + "/notification_charge_order_info"; String pushJson = objectMapper.writeValueAsString(pushData); int maxRetries = 3; int retryIntervalMs = 5000; for (int attempt = 1; attempt <= maxRetries; attempt++) { try { JsonNode response = okHttpUtil.doPostJson(url, pushJson, null); log.info("补偿任务: 渠道方推送充电订单信息成功 - chargeOrderNo: {}, firmId: {}, response: {}", order.getChargeOrderNo(), order.getFirmId(), response); return; } catch (Exception e) { log.error("补偿任务: 渠道方推送充电订单信息失败(第{}次) - chargeOrderNo: {}, firmId: {}, url: {}, 错误: {}", attempt, order.getChargeOrderNo(), order.getFirmId(), url, e.getMessage(), e); if (attempt < maxRetries) { try { Thread.sleep(retryIntervalMs); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); break; } } } } } catch (Exception e) { log.error("补偿任务: 构建渠道方推送数据失败 - chargeOrderNo: {}, 错误: {}", order.getChargeOrderNo(), e.getMessage(), e); } } /** * 渠道方账户余额扣减 + 记录资金流水 */ private Map buildCompensationPushData(ChargeOrderInfo order) throws JsonProcessingException { Map pushData = new LinkedHashMap<>(); if (StrUtil.isNotBlank(order.getChargeDetails())) { JsonNode chargeDetailsNode = objectMapper.readTree(order.getChargeDetails()); if (chargeDetailsNode.isObject()) { pushData.putAll(objectMapper.convertValue(chargeDetailsNode, LinkedHashMap.class)); } else if (chargeDetailsNode.isArray()) { pushData.put("ChargeDetails", objectMapper.convertValue(chargeDetailsNode, List.class)); } } normalizeCompensationPushData(pushData, order); return pushData; } private void normalizeCompensationPushData(Map pushData, ChargeOrderInfo order) throws JsonProcessingException { List> chargeDetails = resolveChargeDetails(pushData, order); putIfBlank(pushData, "ArrearsAmt", BigDecimal.ZERO); pushData.put("ChargeDetails", chargeDetails); pushData.put("SumPeriod", chargeDetails.size()); putIfBlank(pushData, "ConnectorID", order.getConnectorId()); putIfBlank(pushData, "EndTime", order.getEndTime()); putIfBlank(pushData, "OriginElecMoney", defaultDecimal(order.getThirdPartyElecfee())); putIfBlank(pushData, "OriginMoney", defaultDecimal(order.getThirdPartyTotalCost())); putIfBlank(pushData, "OriginServiceMoney", defaultDecimal(order.getThirdPartyServerfee())); putIfBlank(pushData, "PlatOrderId", order.getId()); putIfBlank(pushData, "ReceiptsAmt", defaultDecimal(order.getThirdPartyTotalCost())); putIfBlank(pushData, "StartChargeSeq", order.getStartChargeSeq()); putIfBlank(pushData, "StartTime", order.getStartTime()); putIfBlank(pushData, "StopReason", parseIntegerOrDefault(order.getStopReason(), 0)); putIfBlank(pushData, "TotalElecMoney", defaultDecimal(order.getThirdPartyElecfee())); putIfBlank(pushData, "TotalMoney", defaultDecimal(order.getThirdPartyTotalCost())); putIfBlank(pushData, "TotalPower", defaultDecimal(order.getTotalCharge())); putIfBlank(pushData, "TotalSeviceMoney", defaultDecimal(order.getThirdPartyServerfee())); } private List> resolveChargeDetails(Map pushData, ChargeOrderInfo order) throws JsonProcessingException { List> chargeDetails = convertChargeDetails(pushData.get("ChargeDetails")); if (!chargeDetails.isEmpty()) { return chargeDetails; } if (StrUtil.isNotBlank(order.getChargeDetails())) { JsonNode chargeDetailsNode = objectMapper.readTree(order.getChargeDetails()); if (chargeDetailsNode.isArray()) { chargeDetails = convertChargeDetails(chargeDetailsNode); } else if (chargeDetailsNode.isObject() && chargeDetailsNode.has("ChargeDetails")) { chargeDetails = convertChargeDetails(chargeDetailsNode.get("ChargeDetails")); } } if (!chargeDetails.isEmpty()) { return chargeDetails; } return buildFallbackChargeDetails(order); } private List> convertChargeDetails(Object rawChargeDetails) { if (rawChargeDetails == null) { return new ArrayList<>(); } List> chargeDetails = new ArrayList<>(); if (rawChargeDetails instanceof JsonNode jsonNode && jsonNode.isArray()) { for (JsonNode detailNode : jsonNode) { chargeDetails.add(objectMapper.convertValue(detailNode, LinkedHashMap.class)); } return chargeDetails; } if (rawChargeDetails instanceof List detailList) { for (Object detail : detailList) { chargeDetails.add(objectMapper.convertValue(detail, LinkedHashMap.class)); } } return chargeDetails; } private List> buildFallbackChargeDetails(ChargeOrderInfo order) { List> chargeDetails = new ArrayList<>(); if (order == null) { return chargeDetails; } Map detail = new LinkedHashMap<>(); BigDecimal totalPower = defaultDecimal(order.getTotalCharge()); BigDecimal totalElecMoney = defaultDecimal(order.getThirdPartyElecfee()); BigDecimal totalServiceMoney = defaultDecimal(order.getThirdPartyServerfee()); detail.put("DetailStartTime", order.getStartTime()); detail.put("DetailEndTime", order.getEndTime()); detail.put("ItemFlag", resolvePeriodFlag(order)); detail.put("ElecPrice", calculateUnitPrice(totalElecMoney, totalPower)); detail.put("SevicePrice", calculateUnitPrice(totalServiceMoney, totalPower)); detail.put("DetailPower", totalPower); detail.put("DetailElecMoney", totalElecMoney); detail.put("DetailSeviceMoney", totalServiceMoney); chargeDetails.add(detail); return chargeDetails; } private Integer resolvePeriodFlag(ChargeOrderInfo order) { if (order == null || StrUtil.isBlank(order.getConnectorId())) { return 3; } ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, order.getConnectorId()) .eq(ThirdPartyEquipmentPricePolicy::getIsDeleted, 0) .last("LIMIT 1")); if (pricePolicy == null) { return 3; } List policyInfos = thirdPartyPolicyInfoMapper.selectList( Wrappers.lambdaQuery() .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId()) .eq(ThirdPartyPolicyInfo::getIsDeleted, 0) .orderByAsc(ThirdPartyPolicyInfo::getStartTime)); if (policyInfos == null || policyInfos.isEmpty()) { return 3; } String chargeStartTime = extractTimePart(order.getStartTime()); if (StrUtil.isBlank(chargeStartTime)) { return policyInfos.get(0).getPeriodFlag() != null ? policyInfos.get(0).getPeriodFlag() : 3; } ThirdPartyPolicyInfo matchedPolicy = null; for (ThirdPartyPolicyInfo policyInfo : policyInfos) { if (policyInfo.getStartTime() == null) { continue; } if (chargeStartTime.compareTo(policyInfo.getStartTime()) >= 0) { matchedPolicy = policyInfo; } else { break; } } if (matchedPolicy == null) { matchedPolicy = policyInfos.get(policyInfos.size() - 1); } return matchedPolicy.getPeriodFlag() != null ? matchedPolicy.getPeriodFlag() : 3; } private String extractTimePart(String dateTime) { if (StrUtil.isBlank(dateTime)) { return null; } try { return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) .format(DateTimeFormatter.ofPattern("HHmmss")); } catch (Exception ex) { return null; } } private BigDecimal calculateUnitPrice(BigDecimal amount, BigDecimal power) { if (power == null || power.compareTo(BigDecimal.ZERO) <= 0) { return BigDecimal.ZERO; } return amount.divide(power, 4, RoundingMode.HALF_UP); } private BigDecimal defaultDecimal(BigDecimal value) { return value != null ? value : BigDecimal.ZERO; } private void putIfBlank(Map data, String key, Object value) { if (!data.containsKey(key) || isNullLikeValue(data.get(key))) { data.put(key, value); } } private boolean isNullLikeValue(Object value) { if (value == null) { return true; } if (value instanceof String str) { return StrUtil.isBlank(str) || "null".equalsIgnoreCase(str.trim()); } return false; } private Integer parseIntegerOrDefault(String value, Integer defaultValue) { if (StrUtil.isBlank(value) || "null".equalsIgnoreCase(value.trim())) { return defaultValue; } try { return Integer.parseInt(value.trim()); } catch (NumberFormatException ex) { return defaultValue; } } private void deductChannelFirmBalance(ChargeOrderInfo order, FirmInfo firmInfo) { BigDecimal cost = order.getRealCost(); if (cost == null || cost.compareTo(BigDecimal.ZERO) <= 0) { log.info("补偿任务: 订单{}实际费用为0,跳过渠道方余额扣减", order.getChargeOrderNo()); return; } // 记录资金流水 FirmAccountLog accountLog = new FirmAccountLog(); accountLog.setFirmId(firmInfo.getId()); accountLog.setFirmType(firmInfo.getFirmType()); accountLog.setEventDesc("渠道方充电订单下账(补偿)"); accountLog.setSerialNo(order.getChargeOrderNo()); accountLog.setIncomeType(2); accountLog.setBeforeChange(firmInfo.getBalance()); accountLog.setAfterChange(firmInfo.getBalance().subtract(cost)); accountLog.setMoneyChange(cost); firmAccountLogMapper.insert(accountLog); // 渠道方账户余额修改 firmInfo.setBalance(firmInfo.getBalance().subtract(cost)); firmInfoMapper.updateById(firmInfo); log.info("补偿任务: 订单{}渠道方余额扣减完成,扣减金额: {}, 扣减后余额: {}", order.getChargeOrderNo(), cost, firmInfo.getBalance()); } @Override @Transactional(rollbackFor = Exception.class) public String compensateChannelOrderPush() { log.info("开始执行渠道方订单推送补偿..."); // 查询已完成(status=3)、备注为"通过补偿修复处理"的渠道方订单 List channelOrders = this.list(Wrappers.lambdaQuery() .eq(ChargeOrderInfo::getStatus, 3) .eq(ChargeOrderInfo::getOrderType, SystemConstants.CHARGE_ORDER_TYPE_CHANNEL) .eq(ChargeOrderInfo::getRemark, "通过补偿修复处理") ); if (channelOrders.isEmpty()) { log.info("渠道方推送补偿: 没有找到需要补偿的渠道方订单"); return "没有找到需要补偿的渠道方订单"; } log.info("渠道方推送补偿: 找到{}个需要补偿的渠道方订单", channelOrders.size()); int totalCount = 0; int successCount = 0; List failedOrders = new ArrayList<>(); for (ChargeOrderInfo order : channelOrders) { totalCount++; try { // 优先从third_party_api_log获取原始推送数据 String apiLogData = null; if (order.getStartChargeSeq() != null) { ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne( Wrappers.lambdaQuery() .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息") .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq()) .orderByDesc(ThirdPartyApiLog::getCreatedTime) .last("LIMIT 1")); if (apiLog != null && apiLog.getDecryptedRequestData() != null) { apiLogData = apiLog.getDecryptedRequestData(); } } compensateChannelOrder(order, apiLogData); successCount++; log.info("渠道方推送补偿: 订单{}补偿成功", order.getChargeOrderNo()); } catch (Exception e) { log.error("渠道方推送补偿: 订单{}补偿失败: {}", order.getChargeOrderNo(), e.getMessage(), e); failedOrders.add(order.getChargeOrderNo()); } } String result = String.format("渠道方推送补偿完成!总计: %d, 成功: %d, 失败: %d", totalCount, successCount, failedOrders.size()); if (!failedOrders.isEmpty()) { result += ", 失败订单: " + String.join(",", failedOrders); } log.info(result); return result; } @Override public void failOrderDispose() { //1.查询所有待启动的订单 List orderInfoList = this.list(Wrappers.lambdaQuery().eq(ChargeOrderInfo::getStatus, 0).last("LIMIT 1000")); //2.遍历判断订单创建时间是否已超过3分钟 for (ChargeOrderInfo chargeOrderInfo : orderInfoList) { LocalDateTime createTime = chargeOrderInfo.getCreateTime(); if (createTime != null && createTime.plusMinutes(3).isBefore(LocalDateTime.now())) { String chargeOrderNo = chargeOrderInfo.getChargeOrderNo(); try { log.info("失败订单处理: 订单{}创建时间已超过3分钟,调用订单修复", chargeOrderNo); this.repairOrderByOrderNo(chargeOrderNo); } catch (Exception e) { log.error("失败订单处理: 订单{}修复失败: {}", chargeOrderNo, e.getMessage(), e); } } } } @Override public void settleOrderFailDispose() { //1.查询所有结算中的订单 List orderInfoList = this.list(Wrappers.lambdaQuery().eq(ChargeOrderInfo::getStatus, 2).last("LIMIT 1000")); //2.遍历判断订单结算时间是否已超过5分钟 for (ChargeOrderInfo chargeOrderInfo : orderInfoList) { LocalDateTime settleTime = chargeOrderInfo.getSettleTime(); if (settleTime != null && settleTime.plusMinutes(5).isBefore(LocalDateTime.now())) { String chargeOrderNo = chargeOrderInfo.getChargeOrderNo(); try { log.info("失败订单处理: 订单{}结算时间已超过5分钟,调用订单修复", chargeOrderNo); this.repairOrderByOrderNo(chargeOrderNo); } catch (Exception e) { log.error("失败订单处理: 订单{}修复失败: {}", chargeOrderNo, e.getMessage(), e); } } } } }