longz
2026-07-20 5af1194755100bd05556a3d7e8a1455c11986dd7
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -131,6 +131,7 @@
   private static final int WHITELIST_BENEFIT_ELECTRIC_FREE = 2;
   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 = "特殊退款处理";
   @Resource
   private ChargingGunClient chargingGunClient;
@@ -1571,9 +1572,8 @@
      }
      LocalTime start = LocalTime.parse(site.getReservationStartTime(), DateTimeFormatter.ofPattern("HH:mm"));
      LocalTime end = LocalTime.parse(site.getReservationEndTime(), DateTimeFormatter.ofPattern("HH:mm"));
      if (!isTimeInWindow(LocalTime.now(), start, end)
            || !isTimeInWindow(reservationTime.toLocalTime(), start, end)) {
         return AjaxResult.error("当前时间或预约时间不在允许预约时段内");
      if (!isTimeInWindow(reservationTime.toLocalTime(), start, end)) {
         return AjaxResult.error("预约时间不在允许预约时段内");
      }
      return null;
   }
@@ -4663,7 +4663,161 @@
   
   
   @Override
   public R<PayOrderInfoDto> getSpecialRefundOrder(PayOrderRefundDto payOrderQueryDto) {
      TChargingOrder order = getSpecialRefundChargingOrder(payOrderQueryDto);
      if (order == null) {
         return R.fail("未查询到支付成功的充电订单");
      }
      PayOrderInfoDto dto = new PayOrderInfoDto();
      dto.setOrderId(order.getId().toString());
      dto.setCode(order.getCode());
      dto.setTradeNo(order.getRechargeSerialNumber());
      dto.setPayType(order.getRechargePaymentType());
      dto.setPayAmount(money(order.getPaymentAmount()));
      dto.setPayTime(order.getPayTime());
      dto.setRefundAmount(money(order.getRefundAmount()));
      dto.setRechargeAmount(money(order.getRechargeAmount()));
      dto.setFinalAmount(money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP));
      dto.setPayMethod(order.getPayMethod());
      dto.setBalancePayAmount(order.getBalancePayAmount());
      dto.setThirdPayAmount(order.getThirdPayAmount());
      dto.setBalanceRefundAmount(order.getBalanceRefundAmount());
      dto.setThirdRefundAmount(order.getThirdRefundAmount());
      return R.ok(dto);
   }
   @Override
   @Transactional
   public R specialRefund(PayOrderRefundDto payOrderQueryDto) {
      R passwordValid = validateOperationPassword(payOrderQueryDto);
      if (passwordValid.getCode() != 200) {
         return passwordValid;
      }
      if (payOrderQueryDto.getRefundAmount() == null || payOrderQueryDto.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
         return R.fail("请输入正确的退款金额");
      }
      TChargingOrder order = getSpecialRefundChargingOrder(payOrderQueryDto);
      if (order == null) {
         return R.fail("未查询到支付成功的充电订单");
      }
      BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount());
      BigDecimal refundableAmount = money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP);
      if (refundAmount.compareTo(refundableAmount) > 0) {
         return R.fail("退款金额不能超过剩余可退金额");
      }
      BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount());
      BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount);
      if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) {
         refundableThirdAmount = BigDecimal.ZERO;
      }
      BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod())
            ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP)
            : refundAmount;
      BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP);
      if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) {
         balanceRefundAmount = BigDecimal.ZERO;
      }
      TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount);
      if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) {
         refund.setRefundSerialNumber("BALANCE");
         finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
         return R.ok();
      }
      Integer payType = order.getRechargePaymentType();
      boolean success = false;
      if (Integer.valueOf(1).equals(payType) || "ICBC".equalsIgnoreCase(payOrderQueryDto.getRefundChannel())) {
         success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount);
      } else if (Integer.valueOf(2).equals(payType)) {
         RefundReq dto = new RefundReq();
         dto.setOutTradeNo(order.getCode());
         dto.setOutRequestNo(refund.getRefundCode());
         dto.setRefundAmount(thirdRefundAmount.toString());
         dto.setRefundReason(SPECIAL_REFUND_TITLE);
         RefundResp resp = aliPaymentClient.refund(dto).getData();
         if (resp != null) {
            refund.setRefundSerialNumber(resp.getTradeNo());
            refund.setRefundTime(LocalDateTime.now());
            finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
            success = true;
         }
      } else if (Integer.valueOf(3).equals(payType)) {
         RefundReq dto = new RefundReq();
         dto.setOutTradeNo(order.getRechargeSerialNumber());
         dto.setOutRequestNo(refund.getRefundCode());
         dto.setRefundAmount(thirdRefundAmount.toString());
         dto.setRefundReason(SPECIAL_REFUND_TITLE);
         RefundResp resp = h5AliPaymentClient.refund(dto).getData();
         if (resp != null && "10000".equals(resp.getCode())) {
            refund.setRefundSerialNumber(resp.getTradeNo());
            refund.setRefundTime(LocalDateTime.now());
            finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
            success = true;
         }
      }
      return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试");
   }
   private R validateOperationPassword(PayOrderRefundDto payOrderQueryDto) {
      if (payOrderQueryDto == null || StringUtils.isEmpty(payOrderQueryDto.getOperationPassword())) {
         return R.fail("请输入操作密码");
      }
      SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData();
      if (sysUser == null || StringUtils.isEmpty(sysUser.getOperationPassword())) {
         return R.fail("当前账号未配置操作密码");
      }
      if (!SecurityUtils.matchesPassword(payOrderQueryDto.getOperationPassword(), sysUser.getOperationPassword())) {
         return R.fail("操作密码错误");
      }
      return R.ok();
   }
   private TChargingOrder getSpecialRefundChargingOrder(PayOrderRefundDto payOrderQueryDto) {
      if (payOrderQueryDto == null) {
         return null;
      }
      LambdaQueryWrapper<TChargingOrder> wrapper = new LambdaQueryWrapper<TChargingOrder>()
            .eq(TChargingOrder::getRechargePaymentStatus, 2)
            .eq(TChargingOrder::getDelFlag, false);
      if (payOrderQueryDto.getOrderId() != null) {
         wrapper.eq(TChargingOrder::getId, payOrderQueryDto.getOrderId());
      } else if (StringUtils.hasLength(payOrderQueryDto.getCode())) {
         wrapper.eq(TChargingOrder::getCode, payOrderQueryDto.getCode());
      } else if (StringUtils.hasLength(payOrderQueryDto.getSerialNumber())) {
         wrapper.eq(TChargingOrder::getRechargeSerialNumber, payOrderQueryDto.getSerialNumber());
      } else {
         return null;
      }
      return this.getOne(wrapper.last("limit 1"));
   }
   private TChargingOrderRefund buildSpecialChargingRefund(TChargingOrder order, PayOrderRefundDto dto, BigDecimal refundAmount,
                                             BigDecimal balanceRefundAmount, BigDecimal thirdRefundAmount) {
      TChargingOrderRefund refund = new TChargingOrderRefund();
      refund.setChargingOrderId(order.getId());
      SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
      refund.setRefundCode("TSF" + sdf.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue()));
      refund.setRefundAmount(refundAmount);
      refund.setBalanceRefundAmount(balanceRefundAmount);
      refund.setThirdRefundAmount(thirdRefundAmount);
      refund.setRefundStatus(1);
      refund.setPayType(order.getRechargePaymentType());
      refund.setRefundTime(LocalDateTime.now());
      refund.setCode(order.getCode());
      refund.setRefundTitle(SPECIAL_REFUND_TITLE);
      refund.setRefundContent(SPECIAL_REFUND_TITLE);
      refund.setRefundReason(StringUtils.hasLength(dto.getReason()) ? dto.getReason() : SPECIAL_REFUND_TITLE);
      refund.setRefundRemark(dto.getRemark());
      refund.setRefundTotalAmount(money(order.getRefundAmount()).add(refundAmount));
      refund.setPayAmount(money(order.getPaymentAmount()));
      return refund;
   }
   @Override
   public R payRefund(PayOrderRefundDto payOrderQueryDto) {
      R passwordValid = validateOperationPassword(payOrderQueryDto);
      if (passwordValid.getCode() != 200) {
         return passwordValid;
      }
      if (payOrderQueryDto.getType() == 1) {
         TChargingOrder tChargingOrder = this.baseMapper.selectById(payOrderQueryDto.getOrderId());
         if (tChargingOrder.getPaymentAmount().compareTo(payOrderQueryDto.getRefundAmount()) == -1) {