longz
2026-07-28 79f1a19bb3344de47dff029cf5498d787e223782
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -17,6 +17,7 @@
import com.ruoyi.chargingPile.api.dto.GetSiteListDTO;
import com.ruoyi.chargingPile.api.feignClient.*;
import com.ruoyi.chargingPile.api.model.*;
import com.ruoyi.chargingPile.api.support.ReservationTimeRangeSupport;
import com.ruoyi.chargingPile.api.vo.GetChargingGunByCode;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.dto.ChargingOrderGroup;
@@ -59,6 +60,8 @@
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TOrderEvaluateService;
import com.ruoyi.order.service.*;
import com.ruoyi.order.support.ReservationOrderSupport;
import com.ruoyi.order.support.ReservationRecommendationCalculator;
import com.ruoyi.order.util.mongodb.service.PlatformStartChargingReplyService;
import com.ruoyi.order.util.mongodb.service.TransactionRecordService;
import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService;
@@ -68,6 +71,7 @@
import com.ruoyi.order.vo.OrderCountByDate;
import com.ruoyi.order.vo.StatisticsOfBatteryVO;
import com.ruoyi.order.vo.LargeChargingPowerVo;
import com.ruoyi.order.vo.ReservationRecommendationVO;
import com.ruoyi.other.api.domain.*;
import com.ruoyi.order.vo.ChargingOrderListInfoVO;
import com.ruoyi.other.api.domain.TVip;
@@ -135,6 +139,48 @@
   private static final int WHITELIST_BENEFIT_SERVICE_FREE = 3;
   private static final String SHARE_AFTER_COMMIT_RESOURCE_PREFIX = "chargingOrderShareAfterCommit:";
   private static final String SPECIAL_REFUND_TITLE = "特殊退款处理";
   private static final int RESERVATION_RECOMMENDATION_HISTORY_DAYS = 30;
   @Override
   public ReservationRecommendationVO getReservationRecommendation(Integer siteId) {
      if (siteId == null) {
         throw new ServiceException("站点不能为空");
      }
      R<List<Site>> siteResult = siteClient.getSiteByIds(Collections.singletonList(siteId));
      List<Site> sites = siteResult == null ? null : siteResult.getData();
      if (sites == null || sites.isEmpty()) {
         throw new ServiceException("站点不存在");
      }
      Site site = sites.get(0);
      if (site.getAccountingStrategyId() == null) {
         throw new ServiceException("该站点尚未配置充电计费模板");
      }
      R<List<TAccountingStrategyDetail>> detailResult = accountingStrategyDetailClient
            .getListByAccountingStrategyId(site.getAccountingStrategyId());
      List<TAccountingStrategyDetail> details = detailResult == null ? null : detailResult.getData();
      if (details == null) {
         throw new ServiceException("计费模板服务暂不可用");
      }
      LocalDateTime historyEnd = LocalDate.now().atStartOfDay();
      LocalDateTime historyStart = historyEnd.minusDays(RESERVATION_RECOMMENDATION_HISTORY_DAYS);
      List<TChargingOrder> orders = list(Wrappers.<TChargingOrder>lambdaQuery()
            .select(TChargingOrder::getStartTime, TChargingOrder::getEndTime)
            .eq(TChargingOrder::getSiteId, siteId)
            .eq(TChargingOrder::getStatus, 5)
            .eq(TChargingOrder::getDelFlag, 0)
            .isNotNull(TChargingOrder::getStartTime)
            .isNotNull(TChargingOrder::getEndTime)
            .ge(TChargingOrder::getEndTime, historyStart)
            .lt(TChargingOrder::getStartTime, historyEnd));
      ReservationRecommendationCalculator.Recommendation recommendation =
            ReservationRecommendationCalculator.recommend(details, orders, historyStart, historyEnd);
      if (recommendation == null) {
         throw new ServiceException("该站点计费模板未配置谷时段");
      }
      return new ReservationRecommendationVO(recommendation.getStartTime(), recommendation.getEndTime(),
            recommendation.getUtilizationRate());
   }
   @Resource
   private ChargingGunClient chargingGunClient;
@@ -1603,44 +1649,21 @@
            && reservationTime.isAfter(LocalDateTime.now().plusMinutes(site.getReservationAheadMinute()))) {
         return AjaxResult.error("预约时间不能超过最早可提前预约时限");
      }
      if (!StringUtils.hasLength(site.getReservationStartTime())
            || !StringUtils.hasLength(site.getReservationEndTime())) {
         return AjaxResult.error("电站尚未配置预约时段");
      }
      LocalTime start = parseReservationClock(site.getReservationStartTime());
      LocalTime end = parseReservationClock(site.getReservationEndTime());
      if (start == null || end == null) {
      List<ReservationTimeRange> ranges;
      try {
         ranges = ReservationTimeRangeSupport.parse(site.getReservationTimeRanges(),
               site.getReservationStartTime(), site.getReservationEndTime());
      } catch (IllegalArgumentException e) {
         return AjaxResult.error("电站预约时段配置不正确");
      }
      if (!isTimeInWindow(reservationTime.toLocalTime(), start, end)) {
         return AjaxResult.error("预约时间不在允许预约时段内");
      if (ranges.isEmpty()) {
         return AjaxResult.error("电站尚未配置预约时段");
      }
      if (!ReservationTimeRangeSupport.contains(ranges, reservationTime.toLocalTime())) {
         return AjaxResult.error("预约时间不在允许预约时段内("
               + ReservationTimeRangeSupport.describe(ranges) + ")");
      }
      return null;
   }
   private LocalTime parseReservationClock(String value) {
      if (!StringUtils.hasLength(value)) {
         return null;
      }
      String[] parts = value.trim().split(":");
      if (parts.length < 2) {
         return null;
      }
      try {
         return LocalTime.of(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
      } catch (RuntimeException e) {
         return null;
      }
   }
   private boolean isTimeInWindow(LocalTime value, LocalTime start, LocalTime end) {
      if (start.equals(end)) {
         return true;
      }
      if (start.isBefore(end)) {
         return !value.isBefore(start) && !value.isAfter(end);
      }
      return !value.isBefore(start) || !value.isAfter(end);
   }
   private boolean isSafetyCheckPassed(PreChargeCheck check) {
@@ -1697,7 +1720,7 @@
         if (latest == null || latest.getStatus() != 2 || Boolean.TRUE.equals(check.getStartCommandSent())) {
            return;
         }
         if (latest.getReservationTime() != null && latest.getReservationTime().isAfter(LocalDateTime.now())) {
         if (!ReservationOrderSupport.isStartDue(latest.getReservationTime(), LocalDateTime.now())) {
            return;
         }
         TChargingGun gun = chargingGunClient.getChargingGunById(latest.getChargingGunId()).getData();
@@ -2348,9 +2371,8 @@
      chargingDetails.setName(site.getName() + "-" + chargingPile.getName());
      chargingDetails.setCode(one.getCode());
      chargingDetails.setReservationTime(one.getReservationTime());
      boolean reservationPending = one.getReservationTime() != null
            && one.getReservationTime().isAfter(LocalDateTime.now())
            && Integer.valueOf(2).equals(one.getStatus());
      boolean reservationPending = ReservationOrderSupport.isPending(
            one.getStatus(), one.getReservationTime(), LocalDateTime.now());
      chargingDetails.setStatus(reservationPending ? 1 : one.getStatus());
      chargingDetails.setChargingCost(one.getResidualAmount());
      UploadRealTimeMonitoringData data = uploadRealTimeMonitoringDataService.getLastDataById(one.getCode());
@@ -2388,17 +2410,23 @@
   @Override
   public AjaxResult stopCharging(String id) {
      TChargingOrder order = this.getById(id);
      Integer status = order.getStatus();
      if (status != 3) {
         return AjaxResult.error("还未开始充电");
      }
      if (status == 4 || status == 5) {
         return AjaxResult.error("不能重复操作");
      if (order == null) {
         return AjaxResult.error("充电订单不存在");
      }
      Long userId = tokenService.getLoginUserApplet().getUserId();
      if (!order.getAppUserId().equals(userId)) {
         log.info("【" + order.getCode() + "】主动停止充电失败,用户权限不足。当前登录用户id:{},订单用户id:{}", userId, order.getAppUserId());
         log.info("【" + order.getCode() + "】停止或取消充电失败,用户权限不足。当前登录用户id:{},订单用户id:{}", userId, order.getAppUserId());
         return AjaxResult.error("权限不足");
      }
      if (ReservationOrderSupport.isPending(order.getStatus(), order.getReservationTime(), LocalDateTime.now())) {
         return cancelPendingReservation(order);
      }
      Integer status = order.getStatus();
      if (status == 4 || status == 5) {
         return AjaxResult.error("不能重复操作");
      }
      if (status != 3) {
         return AjaxResult.error("还未开始充电");
      }
      order.setEndTime(LocalDateTime.now());
      order.setStatus(4);
@@ -2434,6 +2462,40 @@
         chengDuJianGuanClient.notificationConnectorChargeStatus(vo);
      });
      return AjaxResult.success();
   }
   private AjaxResult cancelPendingReservation(TChargingOrder order) {
      RLock lock = redissonClient.getLock("START_AFTER_SAFETY:" + order.getId());
      boolean locked = false;
      try {
         locked = lock.tryLock(1, 120, TimeUnit.SECONDS);
         if (!locked) {
            return AjaxResult.error("预约订单正在处理中,请稍后重试");
         }
         TChargingOrder latest = this.getById(order.getId());
         if (latest == null || !ReservationOrderSupport.isPending(
               latest.getStatus(), latest.getReservationTime(), LocalDateTime.now())) {
            return AjaxResult.error("预约已到时间或订单状态已变化,无法取消");
         }
         refund(latest.getCode());
         TChargingOrder cancelled = this.getById(latest.getId());
         if (cancelled != null) {
            cancelled.setStatus(-1);
            cancelled.setEndMode(1);
            cancelled.setEndTime(LocalDateTime.now());
            this.updateById(cancelled);
         }
         redisService.deleteObject("AQJC_" + latest.getChargingGunId());
         log.info("【{}】用户取消预约充电,预约时间:{}", latest.getCode(), latest.getReservationTime());
         return AjaxResult.success("预约充电已取消,退款将原路退回");
      } catch (InterruptedException e) {
         Thread.currentThread().interrupt();
         return AjaxResult.error("取消预约被中断,请稍后重试");
      } finally {
         if (locked && lock.isHeldByCurrentThread()) {
            lock.unlock();
         }
      }
   }
   
   
@@ -2996,6 +3058,12 @@
         payOrderQueryDto = new PayOrderQueryDto();
      }
      PageInfo<PayOrderDto> pageInfo = new PageInfo<>(payOrderQueryDto.getPageCurr(), payOrderQueryDto.getPageSize());
      // 开票申请没有关联明细时必须返回空结果,不能退化成支付订单全量查询。
      if (payOrderQueryDto.getInvoiceId() != null
            && (payOrderQueryDto.getIds() == null || payOrderQueryDto.getIds().isEmpty())) {
         pageInfo.setRecords(Collections.emptyList());
         return R.ok(pageInfo);
      }
      List<PayOrderDto> list = this.baseMapper.payOrderQuery(pageInfo, payOrderQueryDto);
      if (list == null || list.isEmpty()) {
         pageInfo.setRecords(Collections.emptyList());
@@ -3139,7 +3207,23 @@
   
   @Override
   public List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView) {
      List<SixChargingDto> result = this.baseMapper.charge(sixBefore, siteIds, partnerView);
      String sixBeforeValue = sixBefore == null ? null : sixBefore.toString();
      List<SixChargingDto> result = new ArrayList<>();
      if (Boolean.TRUE.equals(partnerView)) {
         List<SixChargingDto> partnerIncome = this.baseMapper.chargePartner(sixBeforeValue, siteIds);
         if (partnerIncome != null) {
            result.addAll(partnerIncome);
         }
      } else {
         List<SixChargingDto> settlementIncome = this.baseMapper.chargeSettlement(sixBeforeValue, siteIds);
         if (settlementIncome != null) {
            result.addAll(settlementIncome);
         }
         List<SixChargingDto> currentMonthIncome = this.baseMapper.chargeCurrentMonth(siteIds);
         if (currentMonthIncome != null) {
            result.addAll(currentMonthIncome);
         }
      }
      if (result != null) {
         for (SixChargingDto item : result) {
            if (item == null) {