From 6fc77feef6337bc478423efad5de91fa520b4990 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 13 七月 2026 21:01:40 +0800
Subject: [PATCH] 支付分账测试相关

---
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java |  443 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 420 insertions(+), 23 deletions(-)

diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
index c72ad45..f1b0083 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -5,6 +5,7 @@
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.account.api.feignClient.*;
@@ -61,6 +62,8 @@
 import com.ruoyi.order.util.mongodb.service.TransactionRecordService;
 import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService;
 import com.ruoyi.order.vo.EndOfChargePageInfo;
+import com.ruoyi.order.vo.ChargingSimulationVO;
+import com.ruoyi.order.vo.IcbcAdminSubOrderVO;
 import com.ruoyi.order.vo.OrderCountByDate;
 import com.ruoyi.order.vo.StatisticsOfBatteryVO;
 import com.ruoyi.other.api.domain.*;
@@ -77,6 +80,7 @@
 import com.ruoyi.payment.api.vo.*;
 import com.ruoyi.system.api.domain.SysUser;
 import com.ruoyi.system.api.feignClient.SysUserClient;
+import org.redisson.api.RBucket;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
@@ -155,6 +159,8 @@
 	private IcbcPaymentClient icbcPaymentClient;
 	@Resource
 	private TChargingOrderShareRecordMapper chargingOrderShareRecordMapper;
+	@Resource
+	private ChargingOrderSettlementExecutor chargingOrderSettlementExecutor;
 	
 	@Resource
 	private AppCouponClient appCouponClient;
@@ -817,7 +823,7 @@
 		chargingOrder.setWhitelistServiceRefundAmount(BigDecimal.ZERO);
 		chargingOrder.setVipDiscountAmount(BigDecimal.ZERO);
 		chargingOrder.setOrderSource(1);
-		if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0) {
+		if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0 && Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) {
 			chargingOrder.setSourceName("ICBC");
 		}
 		chargingOrder.setTitle("【充电桩充电】" + chargingPile.getNumber() + "号桩/" + tChargingGun.getCode() + "号枪");
@@ -880,21 +886,371 @@
 		}
 		//会员优惠折扣将其计入增加充电时长(增加总充电金额)
 		//如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额)
-		IcbcPaymentReq req = new IcbcPaymentReq();
-		req.setOutTradeNo(chargingOrder.getCode());
-		req.setAmount(thirdPayAmount);
-		req.setSubject("充电充值");
-		req.setBody("充电充值");
-		req.setPaymentType(addChargingOrder.getPaymentType());
-		req.setNotifyUrl("/payment/icbc/callBack");
-		req.setReturnUrl(addChargingOrder.getReturnUrl());
-		req.setOpenId(Integer.valueOf(1).equals(addChargingOrder.getPaymentType()) ? appUser.getWxOpenid() : appUser.getAliOpenid());
-		req.setProfitSharing(buildIcbcProfitSharing(site));
-		IcbcPaymentResp data = icbcPaymentClient.payment(req).getData();
-		if (isIcbcPaymentCreated(data)) {
-			return AjaxResult.success(data);
+		if (Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) {
+			IcbcPaymentReq req = new IcbcPaymentReq();
+			req.setOutTradeNo(chargingOrder.getCode());
+			req.setAmount(thirdPayAmount);
+			req.setSubject("充电充值");
+			req.setBody("充电充值");
+			req.setPaymentType(1);
+			req.setNotifyUrl("/payment/icbc/callBack");
+			req.setReturnUrl(addChargingOrder.getReturnUrl());
+			req.setOpenId(appUser.getWxOpenid());
+			req.setProfitSharing(buildIcbcProfitSharing(site));
+			IcbcPaymentResp data = icbcPaymentClient.payment(req).getData();
+			if (isIcbcPaymentCreated(data)) {
+				if (StringUtils.hasLength(data.getTradeNo())) {
+					chargingOrder.setRechargeSerialNumber(data.getTradeNo());
+					this.updateById(chargingOrder);
+				}
+				return AjaxResult.success(data);
+			}
+			throw new RuntimeException("调起工行支付失败");
 		}
-		throw new RuntimeException("调起工行支付失败");
+		if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) {
+			AliPaymentReq req = new AliPaymentReq();
+			req.setOutTradeNo(chargingOrder.getCode());
+			req.setTotalAmount(thirdPayAmount.toString());
+			req.setSubject("充电充值");
+			req.setBuyerOpenId(appUser.getAliOpenid());
+			req.setBody("充电充值");
+			req.setNotifyUrl("/payment/ali/callBack");
+			AliPaymentResp data = aliPaymentClient.payment(req).getData();
+			if (data != null) {
+				return AjaxResult.success(data);
+			}
+			throw new RuntimeException("调起支付宝支付失败");
+		}
+		if (Integer.valueOf(3).equals(addChargingOrder.getPaymentType())) {
+			AliPaymentReq req = new AliPaymentReq();
+			req.setOutTradeNo(chargingOrder.getCode());
+			req.setTotalAmount(thirdPayAmount.toString());
+			req.setSubject("充电充值");
+			req.setBuyerOpenId(appUser.getAliOpenid());
+			req.setBody("充电充值");
+			req.setNotifyUrl("/payment/aliH5/callBack");
+			req.setReturnUrl(addChargingOrder.getReturnUrl());
+			String data = h5AliPaymentClient.payment(req).getData();
+			if (StringUtils.hasLength(data)) {
+				return AjaxResult.success("操作成功", data);
+			}
+		}
+		throw new RuntimeException("无效的支付方式");
+	}
+
+	@Override
+	public ChargingSimulationVO getCurrentSimulationOrder(Integer gunId) {
+		if (gunId == null) {
+			throw new ServiceException("充电枪不能为空");
+		}
+		Long userId = tokenService.getLoginUserApplet().getUserId();
+		return buildSimulationVO(findSimulationOrder(userId, gunId));
+	}
+
+	@Override
+	public ChargingSimulationVO completeSimulation(ChargingSimulationReq request) {
+		if (request == null || request.getGunId() == null) {
+			throw new ServiceException("充电枪不能为空");
+		}
+		Long userId = tokenService.getLoginUserApplet().getUserId();
+		TChargingOrder order = findSimulationOrder(userId, request.getGunId());
+		List<UploadRealTimeMonitoringData> monitoringData = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode());
+		if (monitoringData != null && !monitoringData.isEmpty()) {
+			throw new ServiceException("订单已存在真实充电监控数据,禁止模拟结算");
+		}
+		BigDecimal electricityFee = money(request.getElectricityFee());
+		BigDecimal serviceFee = money(request.getServiceFee());
+		BigDecimal simulationTotal = electricityFee.add(serviceFee).setScale(2, RoundingMode.HALF_UP);
+		BigDecimal availableAmount = money(order.getChargeAmount()).compareTo(BigDecimal.ZERO) > 0
+				? money(order.getChargeAmount()) : money(order.getRechargeAmount());
+		if (simulationTotal.compareTo(availableAmount) > 0) {
+			throw new ServiceException("模拟消费金额不能超过订单可消费金额" + availableAmount.toPlainString() + "元");
+		}
+
+		List<AccountingStrategyDetailOrder> tariffs = accountingStrategyDetailOrderService.list(
+				new LambdaQueryWrapper<AccountingStrategyDetailOrder>()
+						.eq(AccountingStrategyDetailOrder::getChargingOrderId, order.getId())
+						.orderByAsc(AccountingStrategyDetailOrder::getStartTime));
+		ChargingSimulationSupport.PreparedSimulation prepared;
+		try {
+			prepared = ChargingSimulationSupport.prepare(order.getCode(), tariffs, electricityFee, serviceFee, LocalDateTime.now());
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		accountingStrategyDetailOrderService.updateById(prepared.getTariff());
+
+		TChargingGun gun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData();
+		TChargingPile pile = chargingPileClient.getChargingPileById(order.getChargingPileId()).getData();
+		TransactionRecordMessageVO record = prepared.getRecord();
+		record.setCharging_gun_code(gun == null ? null : gun.getCode());
+		record.setCharging_pile_code(pile == null ? null : pile.getCode());
+		if (Integer.valueOf(2).equals(order.getStatus())) {
+			TChargingOrder charging = new TChargingOrder();
+			charging.setId(order.getId());
+			charging.setStatus(3);
+			charging.setStartTime(LocalDateTime.now().minusSeconds(1));
+			this.updateById(charging);
+		}
+		R settlement = chargingOrderSettlementExecutor.settle(record);
+		if (R.isError(settlement)) {
+			throw new ServiceException(StringUtils.hasLength(settlement.getMsg()) ? settlement.getMsg() : "模拟结算失败");
+		}
+		return buildSimulationVO(this.getById(order.getId()));
+	}
+
+	private TChargingOrder findSimulationOrder(Long userId, Integer gunId) {
+		TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>()
+				.eq(TChargingOrder::getAppUserId, userId)
+				.eq(TChargingOrder::getChargingGunId, gunId)
+				.eq(TChargingOrder::getSourceName, "ICBC")
+				.eq(TChargingOrder::getRechargePaymentStatus, 2)
+				.in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4))
+				.eq(TChargingOrder::getDelFlag, 0)
+				.isNull(TChargingOrder::getRefundStatus)
+				.orderByDesc(TChargingOrder::getId)
+				.last("limit 1"));
+		if (order == null) {
+			throw new ServiceException("当前枪没有属于你的可模拟ICBC已支付订单");
+		}
+		return order;
+	}
+
+	private ChargingSimulationVO buildSimulationVO(TChargingOrder order) {
+		ChargingSimulationVO result = new ChargingSimulationVO();
+		result.setOrderId(order.getId());
+		result.setOrderCode(order.getCode());
+		result.setOrderStatus(order.getStatus());
+		result.setRechargeAmount(order.getRechargeAmount());
+		result.setChargeAmount(order.getChargeAmount());
+		result.setElectricityFee(order.getElectrovalence());
+		result.setServiceFee(order.getServiceCharge());
+		result.setPaymentAmount(order.getPaymentAmount());
+		result.setRefundAmount(order.getRefundAmount());
+		result.setRefundStatus(order.getRefundStatus());
+		result.setIcbcOrderId(order.getRechargeSerialNumber());
+		TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectOne(
+				new LambdaQueryWrapper<TChargingOrderShareRecord>()
+						.eq(TChargingOrderShareRecord::getChargingOrderId, order.getId())
+						.eq(TChargingOrderShareRecord::getPayChannel, "ICBC")
+						.eq(TChargingOrderShareRecord::getDelFlag, false)
+						.orderByDesc(TChargingOrderShareRecord::getId)
+						.last("limit 1"));
+		if (shareRecord != null) {
+			result.setShareAmount(shareRecord.getShareAmount());
+			result.setShareStatus(shareRecord.getOnlineShareStatus());
+			result.setSubOrderId(shareRecord.getSubOrderId());
+			result.setSubOrderSeq(shareRecord.getSubOrderSeq());
+			result.setSubOrderTrxid(shareRecord.getSubOrderTrxid());
+		}
+		return result;
+	}
+
+	@Override
+	public IcbcPaymentResp queryIcbcAdminPayment(String orderCode) {
+		TChargingOrder order = loadIcbcAdminOrder(orderCode);
+		return requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败");
+	}
+
+	@Override
+	public IcbcRefundResp refundIcbcAdminPayment(IcbcAdminFundOperationReq request) {
+		if (request == null) {
+			throw new ServiceException("请求参数不能为空");
+		}
+		TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode());
+		BigDecimal amount;
+		try {
+			amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount());
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		BigDecimal paidAmount = money(order.getThirdPayAmount()).compareTo(BigDecimal.ZERO) > 0
+				? money(order.getThirdPayAmount()) : money(order.getRechargeAmount());
+		if (amount.compareTo(paidAmount) > 0) {
+			throw new ServiceException("退款金额不能超过订单第三方支付金额" + paidAmount.toPlainString() + "元");
+		}
+		RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode());
+		if (!lock.tryLock()) {
+			throw new ServiceException("该订单正在执行工行资金操作,请勿重复提交");
+		}
+		try {
+			IcbcPaymentResp payment = requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败");
+			String orderId = extractIcbcOrderId(payment);
+			order.setRechargeSerialNumber(orderId);
+			RBucket<String> pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:main:" + order.getCode());
+			String generatedRefundNo = "ICBCADMINREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+			String pendingValue = pendingRefund.get();
+			boolean reusingPending = StringUtils.hasLength(pendingValue);
+			String refundNo;
+			try {
+				refundNo = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedRefundNo);
+			} catch (IllegalArgumentException e) {
+				throw new ServiceException(e.getMessage());
+			}
+			IcbcRefundReq refundRequest = IcbcAdminFundOperationSupport.buildRefundRequest(order, amount, refundNo);
+			if (!reusingPending) {
+				pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(refundNo, amount));
+			}
+			IcbcRefundResp response = requireFeignData(icbcPaymentClient.refund(refundRequest), "工行退款调用失败");
+			if (IcbcAdminFundOperationSupport.shouldClearPendingRefund(
+					reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) {
+				pendingRefund.delete();
+			}
+			return response;
+		} finally {
+			if (lock.isHeldByCurrentThread()) {
+				lock.unlock();
+			}
+		}
+	}
+
+	@Override
+	public List<IcbcAdminSubOrderVO> queryIcbcAdminSubOrders(String orderCode) {
+		TChargingOrder order = loadIcbcAdminOrder(orderCode);
+		List<TChargingOrderShareRecord> records = chargingOrderShareRecordMapper.selectList(
+				new LambdaQueryWrapper<TChargingOrderShareRecord>()
+						.eq(TChargingOrderShareRecord::getChargingOrderId, order.getId())
+						.eq(TChargingOrderShareRecord::getPayChannel, "ICBC")
+						.eq(TChargingOrderShareRecord::getDelFlag, false)
+						.orderByAsc(TChargingOrderShareRecord::getId));
+		List<IcbcAdminSubOrderVO> result = new ArrayList<>();
+		for (TChargingOrderShareRecord record : records) {
+			IcbcAdminSubOrderVO item = buildIcbcAdminSubOrderVO(record);
+			try {
+				IcbcSubOrderQueryReq query = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, record);
+				item.applyQuery(requireFeignData(icbcPaymentClient.querySubOrder(query), "工行子订单查询失败"));
+			} catch (Exception e) {
+				item.setQueryStatus("FAIL");
+				item.setErrorMessage(e.getMessage());
+			}
+			result.add(item);
+		}
+		return result;
+	}
+
+	@Override
+	public IcbcSubOrderRefundResp refundIcbcAdminSubOrder(IcbcAdminFundOperationReq request) {
+		if (request == null || request.getShareRecordId() == null) {
+			throw new ServiceException("请选择需要退款的分账子订单");
+		}
+		TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode());
+		TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectById(request.getShareRecordId());
+		if (shareRecord == null || !order.getId().equals(shareRecord.getChargingOrderId())) {
+			throw new ServiceException("分账记录不属于当前订单");
+		}
+		BigDecimal amount;
+		try {
+			amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount());
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		if (shareRecord.getShareAmount() != null && amount.compareTo(shareRecord.getShareAmount()) > 0) {
+			throw new ServiceException("分账退款金额不能超过分账金额" + shareRecord.getShareAmount().toPlainString() + "元");
+		}
+		RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode());
+		if (!lock.tryLock()) {
+			throw new ServiceException("该分账子订单正在退款,请勿重复提交");
+		}
+		try {
+			String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+			RBucket<String> pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:sub:" + shareRecord.getId());
+			String generatedToken = "ICBCSUBREFUND" + suffix + ":ICBCSUBREFUNDNO" + suffix;
+			String pendingValue = pendingRefund.get();
+			boolean reusingPending = StringUtils.hasLength(pendingValue);
+			String operationToken;
+			try {
+				operationToken = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedToken);
+			} catch (IllegalArgumentException e) {
+				throw new ServiceException(e.getMessage());
+			}
+			String[] refundNumbers = operationToken.split(":", 2);
+			if (refundNumbers.length != 2) {
+				throw new ServiceException("工行分账退款幂等记录格式错误");
+			}
+			IcbcSubOrderRefundReq refundRequest;
+			try {
+				refundRequest = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
+						order, shareRecord, amount, refundNumbers[0], refundNumbers[1]);
+			} catch (IllegalArgumentException e) {
+				throw new ServiceException(e.getMessage());
+			}
+			if (!reusingPending) {
+				pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(operationToken, amount));
+			}
+			IcbcSubOrderRefundResp response = requireFeignData(
+					icbcPaymentClient.refundSubOrder(refundRequest), "工行分账退款调用失败");
+			if (IcbcAdminFundOperationSupport.shouldClearPendingRefund(
+					reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) {
+				pendingRefund.delete();
+			}
+			return response;
+		} finally {
+			if (lock.isHeldByCurrentThread()) {
+				lock.unlock();
+			}
+		}
+	}
+
+	private TChargingOrder loadIcbcAdminOrder(String orderCode) {
+		if (!StringUtils.hasLength(orderCode)) {
+			throw new ServiceException("订单号不能为空");
+		}
+		TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>()
+				.eq(TChargingOrder::getCode, orderCode)
+				.eq(TChargingOrder::getDelFlag, 0)
+				.last("limit 1"));
+		try {
+			IcbcAdminFundOperationSupport.validateOrder(order);
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		return order;
+	}
+
+	private String extractIcbcOrderId(IcbcPaymentResp payment) {
+		try {
+			JSONObject response = JSON.parseObject(payment.getRawResponse());
+			String orderId = response == null ? null : response.getString("order_id");
+			if (StringUtils.hasLength(orderId)) {
+				return orderId;
+			}
+		} catch (Exception ignored) {
+		}
+		throw new ServiceException("工行支付查询未返回真实order_id,禁止退款");
+	}
+
+	private boolean isDefinitiveIcbcResponse(String rawResponse) {
+		if (!StringUtils.hasLength(rawResponse)) {
+			return false;
+		}
+		try {
+			JSONObject response = JSON.parseObject(rawResponse);
+			return response != null && (response.containsKey("returnCode") || response.containsKey("return_code"));
+		} catch (Exception ignored) {
+			return false;
+		}
+	}
+
+	private IcbcAdminSubOrderVO buildIcbcAdminSubOrderVO(TChargingOrderShareRecord record) {
+		IcbcAdminSubOrderVO item = new IcbcAdminSubOrderVO();
+		item.setShareRecordId(record.getId());
+		item.setOrderCode(record.getChargingOrderCode());
+		item.setShareAmount(record.getShareAmount());
+		item.setOnlineShareStatus(record.getOnlineShareStatus());
+		item.setSubOrderId(record.getSubOrderId());
+		item.setSubMerId(record.getSubMerId());
+		item.setSubMerPrtclNo(record.getSubMerPrtclNo());
+		item.setOriTrxDate(record.getOriTrxDate());
+		item.setBankOrderId(record.getIcbcOrderId());
+		item.setSubOrderSeq(record.getSubOrderSeq());
+		item.setSubOrderTrxid(record.getSubOrderTrxid());
+		return item;
+	}
+
+	private <T> T requireFeignData(R<T> result, String message) {
+		if (result == null || R.isError(result) || result.getData() == null) {
+			throw new ServiceException(result != null && StringUtils.hasLength(result.getMsg()) ? result.getMsg() : message);
+		}
+		return result.getData();
 	}
 	
 	
@@ -911,7 +1267,26 @@
 	@Override
 	public AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach) {
 		TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, out_trade_no));
+		if (chargingOrder == null) {
+			return AjaxResult.error("支付回调订单不存在");
+		}
 		if (chargingOrder.getRechargePaymentStatus() == 2) {
+			if (isIcbcOrder(chargingOrder) && StringUtils.hasLength(transaction_id)
+					&& !transaction_id.equals(chargingOrder.getRechargeSerialNumber())) {
+				chargingOrder.setRechargeSerialNumber(transaction_id);
+				this.updateById(chargingOrder);
+			}
+			return AjaxResult.success();
+		}
+		LocalDateTime callbackPayTime = LocalDateTime.now();
+		int claimed = this.baseMapper.update(null, new LambdaUpdateWrapper<TChargingOrder>()
+				.eq(TChargingOrder::getId, chargingOrder.getId())
+				.eq(TChargingOrder::getRechargePaymentStatus, 1)
+				.set(TChargingOrder::getRechargePaymentStatus, 2)
+				.set(TChargingOrder::getRechargeSerialNumber, transaction_id)
+				.set(TChargingOrder::getStatus, 2)
+				.set(TChargingOrder::getPayTime, callbackPayTime));
+		if (claimed == 0) {
 			return AjaxResult.success();
 		}
 		if (isBalancePayMode(chargingOrder.getPayMethod())
@@ -927,7 +1302,7 @@
 		chargingOrder.setRechargePaymentStatus(2);
 		chargingOrder.setRechargeSerialNumber(transaction_id);
 		chargingOrder.setStatus(2);
-		chargingOrder.setPayTime(LocalDateTime.now());
+		chargingOrder.setPayTime(callbackPayTime);
 		
 		//添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据
 		PreChargeCheck preChargeCheck = new PreChargeCheck();
@@ -3183,15 +3558,19 @@
     @Transactional
 	public R endChargeBillingCharge(TransactionRecordMessageVO vo) {
 		RLock lock = redissonClient.getLock("endOrder:" + vo.getTransaction_serial_number());
+		boolean locked = false;
 		try {
-            boolean tryLock = lock.tryLock(60, 60, TimeUnit.SECONDS);
+            locked = lock.tryLock(60, 60, TimeUnit.SECONDS);
+            if (!locked) {
+                return R.fail("订单正在结算,请勿重复操作");
+            }
             //添加redis缓存数据,过期时间,用于防止处理缓慢且硬件重复发送报文导致的重复退款
             Boolean hasKey = redisService.hasKey("END_ORDER:" + vo.getTransaction_serial_number());
             if(hasKey){
                 return R.fail("不能重复退款");
             }
             redisService.setCacheObject("END_ORDER:" + vo.getTransaction_serial_number(), System.currentTimeMillis() + "", 10L, TimeUnit.MINUTES);
-            if(tryLock){
+            if(locked){
                 TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, vo.getTransaction_serial_number()));
                 if (null == chargingOrder) {
                     log.error("订单不存在,订单号:{}", vo.getTransaction_serial_number());
@@ -3683,8 +4062,15 @@
                             this.updateById(chargingOrder);
                             triggerChargingOrderShare(chargingOrder);
                         } else if (isIcbcOrder(chargingOrder)) {
-                            refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电完成退款", false,
-                                    refundAmount, balanceRefundAmount);
+                            boolean refundSuccess = refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount,
+                                    "充电完成退款", false, refundAmount, balanceRefundAmount);
+                            if (!refundSuccess) {
+                                chargingOrderRefund.setRefundStatus(1);
+                                chargingOrderRefundService.save(chargingOrderRefund);
+                                chargingOrder.setRefundStatus(1);
+                                this.updateById(chargingOrder);
+                                throw new ServiceException("工行退款失败,结算未完成,请检查支付流水后重试退款");
+                            }
                         } else if (1 == rechargePaymentType) {
                             WxPaymentRefundModel model = new WxPaymentRefundModel();
                             model.setOut_trade_no(chargingOrder.getCode());
@@ -3827,9 +4213,12 @@
                 chargingMessageClient.pushOrderStatus(chargingOrderVo);
             }
 		} catch (Exception e) {
-			e.printStackTrace();
+			log.error("充电订单结算失败,订单号:{}", vo == null ? null : vo.getTransaction_serial_number(), e);
+			return R.fail("充电订单结算失败:" + firstNotBlank(e.getMessage(), "未知异常"));
 		} finally {
-			lock.unlock();
+			if (locked && lock.isHeldByCurrentThread()) {
+				lock.unlock();
+			}
 		}
 		return R.ok();
 	}
@@ -6131,7 +6520,15 @@
 		if (resp == null || !StringUtils.hasLength(resp.getStatus())) {
 			return false;
 		}
-		return "SUCCESS".equals(resp.getStatus());
+		if (!"SUCCESS".equals(resp.getStatus()) || !StringUtils.hasLength(resp.getPayData())) {
+			return false;
+		}
+		try {
+			JSONObject payData = JSON.parseObject(resp.getPayData());
+			return StringUtils.hasLength(payData.getString("wxDataPackage")) || StringUtils.hasLength(payData.getString("package"));
+		} catch (Exception e) {
+			return false;
+		}
 	}
 
 	private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) {

--
Gitblit v1.7.1