longz
2026-07-20 faaf70b3fc0cff90d25fa30b37f225f679a0d5a5
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -966,7 +966,13 @@
         req.setReturnUrl(addChargingOrder.getReturnUrl());
         req.setOpenId(appUser.getWxOpenid());
         req.setProfitSharing(buildIcbcProfitSharing(site));
         IcbcPaymentResp data = icbcPaymentClient.payment(req).getData();
         R<IcbcPaymentResp> result = icbcPaymentClient.payment(req);
         if (result == null || R.isError(result)) {
            String reason = result == null ? "工行支付服务无响应" : firstNotBlank(result.getMsg(), "工行支付服务调用失败");
            log.error("调起工行支付失败,订单号:{},原因:{}", chargingOrder.getCode(), reason);
            throw new RuntimeException("调起工行支付失败:" + reason);
         }
         IcbcPaymentResp data = result.getData();
         if (isIcbcPaymentCreated(data)) {
            if (StringUtils.hasLength(data.getTradeNo())) {
               chargingOrder.setRechargeSerialNumber(data.getTradeNo());
@@ -974,7 +980,9 @@
            }
            return AjaxResult.success(data);
         }
         throw new RuntimeException("调起工行支付失败");
         String reason = buildIcbcPaymentFailReason(data);
         log.error("调起工行支付失败,订单号:{},工行返回:{}", chargingOrder.getCode(), reason);
         throw new RuntimeException("调起工行支付失败:" + reason);
      }
      if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) {
         AliPaymentReq req = new AliPaymentReq();
@@ -2385,12 +2393,23 @@
    * @param chargingOrder
    */
   public void editUserTag(TChargingOrder chargingOrder) {
      if (chargingOrder == null || chargingOrder.getAppUserId() == null) {
         return;
      }
      //处理用户标签数据
      List<TUserTag> data = userTagClient.getAllUserTag().getData();
      R<List<TUserTag>> tagResult = userTagClient.getAllUserTag();
      List<TUserTag> data = tagResult == null ? null : tagResult.getData();
      if (data == null || data.isEmpty()) {
         return;
      }
      //累计充电次数
      List<TUserTag> userTagList1 = data.stream().filter(s -> s.getStandardCondition() == 1).collect(Collectors.toList());
      List<TUserTag> userTagList1 = data.stream().filter(s -> Integer.valueOf(1).equals(s.getStandardCondition())).collect(Collectors.toList());
      for (TUserTag tUserTag : userTagList1) {
         Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times");
         JSONObject conditions = JSON.parseObject(tUserTag.getConditions());
         Integer times = conditions == null ? null : conditions.getInteger("times");
         if (times == null || times <= 0 || tUserTag.getCreateTime() == null) {
            continue;
         }
         TAppUserTag appUserTag = new TAppUserTag();
         appUserTag.setAppUserId(chargingOrder.getAppUserId());
         appUserTag.setUserTagId(tUserTag.getId());
@@ -2409,18 +2428,21 @@
         }
      }
      //充电频率
      List<TUserTag> userTagList2 = data.stream().filter(s -> s.getStandardCondition() == 2).collect(Collectors.toList());
      List<TUserTag> userTagList2 = data.stream().filter(s -> Integer.valueOf(2).equals(s.getStandardCondition())).collect(Collectors.toList());
      for (TUserTag tUserTag : userTagList2) {
         TAppUserTag appUserTag = new TAppUserTag();
         appUserTag.setAppUserId(chargingOrder.getAppUserId());
         appUserTag.setUserTagId(tUserTag.getId());
         TAppUserTag data1 = appUserTagClient.getUserTag(appUserTag).getData();
         
         Integer day = JSON.parseObject(tUserTag.getConditions()).getInteger("day");
         Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times");
         JSONObject conditions = JSON.parseObject(tUserTag.getConditions());
         Integer day = conditions == null ? null : conditions.getInteger("day");
         Integer times = conditions == null ? null : conditions.getInteger("times");
         if (day == null || day <= 0 || times == null || times <= 0) {
            continue;
         }
         LocalDateTime endTime = LocalDateTime.now();
         endTime.plusDays(day);
         String start = tUserTag.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
         String start = endTime.minusDays(day).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
         String end = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
         long count1 = this.count(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId())
               .eq(TChargingOrder::getRechargePaymentStatus, 2).between(TChargingOrder::getStartTime, start, end).eq(TChargingOrder::getDelFlag, 0));
@@ -2934,13 +2956,23 @@
   @Override
   public R<PageInfo<PayOrderDto>> payOrderQuery(PayOrderQueryDto payOrderQueryDto) {
      if (payOrderQueryDto == null) {
         payOrderQueryDto = new PayOrderQueryDto();
      }
      PageInfo<PayOrderDto> pageInfo = new PageInfo<>(payOrderQueryDto.getPageCurr(), payOrderQueryDto.getPageSize());
      List<PayOrderDto> list = this.baseMapper.payOrderQuery(pageInfo, payOrderQueryDto);
      if (list == null || list.isEmpty()) {
         pageInfo.setRecords(Collections.emptyList());
         return R.ok(pageInfo);
      }
      for (PayOrderDto payOrderDto : list) {
         if (payOrderDto.getType() == 2 || payOrderDto.getType() == 3) {
         if (payOrderDto == null) {
            continue;
         }
         if (Integer.valueOf(2).equals(payOrderDto.getType()) || Integer.valueOf(3).equals(payOrderDto.getType())) {
            payOrderDto.setFinalAmount(payOrderDto.getOrderAmount());
         }
         if (payOrderDto.getType() == 1 && payOrderDto.getStatus() == 3) {
         if (Integer.valueOf(1).equals(payOrderDto.getType()) && Integer.valueOf(3).equals(payOrderDto.getStatus())) {
            payOrderDto.setFinalAmount(payOrderDto.getOrderAmount());
            payOrderDto.setOrderAmount(payOrderDto.getOrderAmount());
            payOrderDto.setPaymentAmount(payOrderDto.getOrderAmount());
@@ -4700,61 +4732,189 @@
      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("退款金额不能超过剩余可退金额");
      RLock lock = redissonClient.getLock("SPECIAL_REFUND_ORDER_LOCK:" + order.getId());
      if (!lock.tryLock()) {
         return R.fail("该订单正在退款处理中,请勿重复提交");
      }
      BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount());
      BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount);
      if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) {
         refundableThirdAmount = BigDecimal.ZERO;
      try {
         order = this.getById(order.getId());
         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)) {
            R shareRefund = refundIcbcShareBeforeMainRefund(order, refund, thirdRefundAmount);
            if (shareRefund.getCode() != 200) {
               return shareRefund;
            }
            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("退款失败,请检查支付渠道或稍后重试");
      } finally {
         if (lock.isHeldByCurrentThread()) {
            lock.unlock();
         }
      }
      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);
   }
   private R refundIcbcShareBeforeMainRefund(TChargingOrder order, TChargingOrderRefund refund, BigDecimal thirdRefundAmount) {
      if (order == null || refund == null || thirdRefundAmount == null || thirdRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
         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;
      if (!"ICBC".equals(order.getSourceName())) {
         return R.ok();
      }
      List<TChargingOrderShareRecord> shareRecords = chargingOrderShareRecordMapper.selectList(
            new LambdaQueryWrapper<TChargingOrderShareRecord>()
                  .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId())
                  .eq(TChargingOrderShareRecord::getPayChannel, "ICBC")
                  .eq(TChargingOrderShareRecord::getNeedOnlineShare, true)
                  .eq(TChargingOrderShareRecord::getOnlineShareStatus, "SUCCESS")
                  .eq(TChargingOrderShareRecord::getDelFlag, false)
                  .orderByAsc(TChargingOrderShareRecord::getId));
      if (shareRecords == null || shareRecords.isEmpty()) {
         return R.ok();
      }
      BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order);
      if (thirdPayAmount.compareTo(BigDecimal.ZERO) <= 0) {
         return R.fail("订单已分账但第三方支付金额异常,禁止退款");
      }
      BigDecimal cumulativeThirdRefund = money(order.getThirdRefundAmount()).add(thirdRefundAmount).min(thirdPayAmount).setScale(2, RoundingMode.HALF_UP);
      for (TChargingOrderShareRecord shareRecord : shareRecords) {
         BigDecimal shareAmount = money(shareRecord.getShareAmount());
         if (shareAmount.compareTo(BigDecimal.ZERO) <= 0) {
            continue;
         }
      } 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;
         BigDecimal targetRefundAmount = shareAmount.multiply(cumulativeThirdRefund).divide(thirdPayAmount, 2, RoundingMode.HALF_UP).min(shareAmount).setScale(2, RoundingMode.HALF_UP);
         BigDecimal refundedShareAmount;
         try {
            refundedShareAmount = queryIcbcShareRefundAmount(order, shareRecord);
         } catch (ServiceException e) {
            return R.fail(e.getMessage());
         }
         BigDecimal shareRefundAmount = targetRefundAmount.subtract(refundedShareAmount).setScale(2, RoundingMode.HALF_UP);
         if (shareRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
            continue;
         }
         R result = refundIcbcShareRecord(order, shareRecord, refund, shareRefundAmount);
         if (result.getCode() != 200) {
            return result;
         }
      }
      return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试");
      return R.ok();
   }
   private BigDecimal queryIcbcShareRefundAmount(TChargingOrder order, TChargingOrderShareRecord shareRecord) {
      try {
         IcbcSubOrderQueryReq queryReq = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, shareRecord);
         R<IcbcSubOrderQueryResp> queryResult = icbcPaymentClient.querySubOrder(queryReq);
         IcbcSubOrderQueryResp queryResp = queryResult == null ? null : queryResult.getData();
         if (queryResult == null || queryResult.getCode() != 200 || queryResp == null || !"SUCCESS".equals(queryResp.getStatus())) {
            throw new ServiceException(queryResult == null ? "工行子订单查询无响应" : firstNotBlank(queryResult.getMsg(), "工行子订单查询失败"));
         }
         return parseIcbcAmount(queryResp.getOrderRefundAmnt());
      } catch (IllegalArgumentException e) {
         throw new ServiceException(e.getMessage());
      } catch (ServiceException e) {
         throw e;
      } catch (Exception e) {
         throw new ServiceException("工行子订单查询异常:" + firstNotBlank(e.getMessage(), "未知异常"));
      }
   }
   private R refundIcbcShareRecord(TChargingOrder order, TChargingOrderShareRecord shareRecord, TChargingOrderRefund refund, BigDecimal shareRefundAmount) {
      String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
      String retSubOrderId = ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).substring(0, Math.min(50, ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).length()));
      String outtrxSerialNo = ("SPSUBREF" + suffix + shareRecord.getId()).substring(0, Math.min(50, ("SPSUBREF" + suffix + shareRecord.getId()).length()));
      try {
         IcbcSubOrderRefundReq refundReq = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
               order, shareRecord, shareRefundAmount, retSubOrderId, outtrxSerialNo);
         refundReq.setMerSpInfor("特殊退款分账退回:" + order.getCode());
         R<IcbcSubOrderRefundResp> refundResult = icbcPaymentClient.refundSubOrder(refundReq);
         IcbcSubOrderRefundResp refundResp = refundResult == null ? null : refundResult.getData();
         if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) {
            String message = refundResult == null ? "工行分账退款无响应" : firstNotBlank(refundResult.getMsg(), refundResp == null ? null : refundResp.getRawResponse(), "工行分账退款失败");
            log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{},原因:{}",
                  order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, message);
            return R.fail("分账退回失败:" + message);
         }
         log.info("特殊退款分账退回成功,订单号:{},退款单号:{},分账记录:{},金额:{}",
               order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount);
         return R.ok();
      } catch (IllegalArgumentException | ServiceException e) {
         log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{}",
               order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e);
         return R.fail("分账退回失败:" + e.getMessage());
      } catch (Exception e) {
         log.error("特殊退款分账退回异常,订单号:{},退款单号:{},分账记录:{},金额:{}",
               order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e);
         return R.fail("分账退回异常:" + firstNotBlank(e.getMessage(), "未知异常"));
      }
   }
   private BigDecimal parseIcbcAmount(String amount) {
      if (!StringUtils.hasLength(amount)) {
         return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
      }
      String value = amount.trim();
      try {
         if (value.contains(".")) {
            return money(new BigDecimal(value));
         }
         return new BigDecimal(value).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
      } catch (NumberFormatException e) {
         throw new ServiceException("工行金额格式异常:" + amount);
      }
   }
   private R validateOperationPassword(PayOrderRefundDto payOrderQueryDto) {
@@ -7038,6 +7198,20 @@
      }
   }
   private String buildIcbcPaymentFailReason(IcbcPaymentResp resp) {
      if (resp == null) {
         return "工行支付服务未返回支付数据";
      }
      String reason = firstNotBlank(resp.getRawResponse(), resp.getStatus());
      if (!StringUtils.hasLength(reason)) {
         return "工行支付服务返回数据不完整";
      }
      if (reason.length() > 500) {
         return reason.substring(0, 500);
      }
      return reason;
   }
   private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) {
      if (!isNeedChargingOrderShare(chargingOrder)) {
         return;