mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-08-10 00:03:44 +08:00
修复余额不足仍可启动充电问题
统一启动金额优先级并在启动前校验会员余额。 同步完成订单创建和余额支付后再返回鉴权成功,并增加支付凭证补偿、异常停机和结算保护。
This commit is contained in:
@@ -6,29 +6,28 @@ import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.enums.ykc.OrderPayModeEnum;
|
||||
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.common.util.id.IdUtils;
|
||||
import com.jsowell.common.util.spring.SpringUtils;
|
||||
import com.jsowell.netty.factory.YKCOperateFactory;
|
||||
import com.jsowell.pile.domain.PileBasicInfo;
|
||||
import com.jsowell.pile.dto.ConfirmStartChargingData;
|
||||
import com.jsowell.pile.dto.GenerateOrderDTO;
|
||||
import com.jsowell.pile.dto.SavePileMsgDTO;
|
||||
import com.jsowell.pile.service.*;
|
||||
import com.jsowell.pile.util.ChargeAmountUtils;
|
||||
import com.jsowell.pile.vo.base.ConfirmStartChargingMemberVO;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 充电桩主动申请启动充电 0x31
|
||||
@@ -42,18 +41,9 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class ConfirmStartChargingRequestHandler extends AbstractYkcHandler {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REQUEST_START_CHARGING_CODE.getBytes());
|
||||
|
||||
// 引入线程池
|
||||
private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");
|
||||
|
||||
@Autowired
|
||||
private PileAuthCardService pileAuthCardService;
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private MemberPlateNumberRelationService memberPlateNumberRelationService;
|
||||
|
||||
@Autowired
|
||||
private PileMsgRecordService pileMsgRecordService;
|
||||
|
||||
@@ -198,8 +188,14 @@ public class ConfirmStartChargingRequestHandler extends AbstractYkcHandler {
|
||||
.vinCode(vinCode)
|
||||
.build();
|
||||
|
||||
// 启动充电
|
||||
Map<String, String> resultMap = this.confirmStartCharging(confirmStartChargingData);
|
||||
Map<String, String> resultMap;
|
||||
try {
|
||||
// 启动充电
|
||||
resultMap = this.confirmStartCharging(confirmStartChargingData);
|
||||
} catch (Exception e) {
|
||||
log.error("充电桩主动申请充电处理失败, param:{}", JSON.toJSONString(confirmStartChargingData), e);
|
||||
resultMap = buildFailureResult("05");
|
||||
}
|
||||
|
||||
byte[] defeatReasonByteArr = Constants.zeroByteArray;
|
||||
/**
|
||||
@@ -279,6 +275,10 @@ public class ConfirmStartChargingRequestHandler extends AbstractYkcHandler {
|
||||
|
||||
// 查询充电桩信息
|
||||
PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn);
|
||||
if (pileBasicInfo == null) {
|
||||
log.info("充电桩主动申请充电未查询到桩信息, pileSn:{}", pileSn);
|
||||
return buildFailureResult("05");
|
||||
}
|
||||
|
||||
// 查询是否有权限
|
||||
ConfirmStartChargingMemberVO memberWalletVO = null;
|
||||
@@ -295,50 +295,38 @@ public class ConfirmStartChargingRequestHandler extends AbstractYkcHandler {
|
||||
log.info("根据vin码:{},查询会员信息:{}", vinCode, JSON.toJSONString(memberWalletVO));
|
||||
}
|
||||
|
||||
String accountBalance = Constants.ZERO;
|
||||
|
||||
// 如果查询后memberWalletVO为null, 则标示无权限
|
||||
if (memberWalletVO == null) {
|
||||
resultMap.put("transactionCode", Constants.ILLEGAL_TRANSACTION_CODE);
|
||||
resultMap.put("accountBalance", accountBalance);
|
||||
resultMap.put("defeatReasonCode", "01");
|
||||
log.info("无此会员权限");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// 如果不是平台测试员, 并且也不是站点白名单, 并且总余额小于等于0, 则提示余额不足
|
||||
if (!StringUtils.equals(Constants.ONE, memberWalletVO.getPlatformTesterFlag())
|
||||
&& !StringUtils.equals(Constants.ONE, memberWalletVO.getStationWhiteListFlag())
|
||||
&& memberWalletVO.getTotalBalance().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
resultMap.put("transactionCode", Constants.ILLEGAL_TRANSACTION_CODE);
|
||||
resultMap.put("accountBalance", accountBalance);
|
||||
resultMap.put("defeatReasonCode", "03");
|
||||
log.info("余额不足");
|
||||
return resultMap;
|
||||
return buildFailureResult("01");
|
||||
}
|
||||
|
||||
/*
|
||||
鉴权通过, 生成订单并支付
|
||||
*/
|
||||
BigDecimal chargeAmount;
|
||||
String payMode;
|
||||
if (StringUtils.equals(Constants.ONE, memberWalletVO.getPlatformTesterFlag())
|
||||
|| StringUtils.equals(Constants.ONE, memberWalletVO.getStationWhiteListFlag())) {
|
||||
// 如果是平台测试员或者是站点白名单
|
||||
accountBalance = Constants.WHITELIST_DEFAULT_AMOUNT.toString();
|
||||
chargeAmount = Constants.WHITELIST_DEFAULT_AMOUNT;
|
||||
payMode = OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue();
|
||||
} else {
|
||||
BigDecimal totalBalance = memberWalletVO.getTotalBalance();
|
||||
accountBalance = totalBalance.toString();
|
||||
if (totalBalance.compareTo(Constants.BALANCE_PAY_MAX_AMOUNT_NEW) > 0) {
|
||||
accountBalance = Constants.BALANCE_PAY_MAX_AMOUNT_NEW.toString();
|
||||
chargeAmount = ChargeAmountUtils.resolveStartupAmount(
|
||||
dto.getChargeAmount(), memberWalletVO.getDefaultChargeAmount());
|
||||
BigDecimal totalBalance = memberWalletVO.getTotalBalance() == null
|
||||
? BigDecimal.ZERO
|
||||
: memberWalletVO.getTotalBalance();
|
||||
if (!ChargeAmountUtils.isPositive(chargeAmount) || totalBalance.compareTo(chargeAmount) < 0) {
|
||||
log.info("余额不足, memberId:{}, chargeAmount:{}, totalBalance:{}",
|
||||
memberWalletVO.getMemberId(), chargeAmount, totalBalance);
|
||||
return buildFailureResult("03");
|
||||
}
|
||||
payMode = OrderPayModeEnum.PAYMENT_OF_PRINCIPAL_BALANCE.getValue();
|
||||
}
|
||||
|
||||
// 生成交易流水号, 传递给订单使用
|
||||
String transactionCode = IdUtils.generateTransactionCode(pileSn, connectorCode);
|
||||
resultMap.put("transactionCode", transactionCode);
|
||||
resultMap.put("accountBalance", accountBalance);
|
||||
|
||||
dto.setPileSn(pileSn);
|
||||
if (StringUtils.isNotBlank(memberWalletVO.getPlateNumber())) {
|
||||
@@ -346,34 +334,40 @@ public class ConfirmStartChargingRequestHandler extends AbstractYkcHandler {
|
||||
}
|
||||
dto.setConnectorCode(connectorCode);
|
||||
dto.setTransactionCode(transactionCode);
|
||||
if (memberWalletVO.getDefaultChargeAmount() != null && memberWalletVO.getDefaultChargeAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
dto.setChargeAmount(memberWalletVO.getDefaultChargeAmount());
|
||||
}else {
|
||||
dto.setChargeAmount(new BigDecimal(accountBalance));
|
||||
}
|
||||
dto.setChargeAmount(chargeAmount);
|
||||
dto.setPayMode(payMode);
|
||||
dto.setStartMode(StringUtils.equals("01", startMode) ? "2" : "5");
|
||||
dto.setMemberId(memberWalletVO.getMemberId());
|
||||
dto.setMerchantId(pileBasicInfo.getMerchantId() + "");
|
||||
|
||||
// 校验充电桩相关的信息
|
||||
try {
|
||||
orderBasicInfoService.checkPileInfoForEV(dto);
|
||||
// 必须等订单创建并完成余额支付后,才能向桩端返回鉴权成功。
|
||||
Map<String, Object> map = orderBasicInfoService.generateOrderByCardV2(dto);
|
||||
if (map == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_GENERATE_ORDER_ERROR);
|
||||
}
|
||||
} catch (BusinessException e) {
|
||||
String defeatReasonCode = StringUtils.equals(
|
||||
ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT.getValue(), e.getCode()) ? "03" : "05";
|
||||
log.error("充电桩主动申请充电生成订单失败, memberId:{}, chargeAmount:{}",
|
||||
memberWalletVO.getMemberId(), chargeAmount, e);
|
||||
return buildFailureResult(defeatReasonCode);
|
||||
} catch (Exception e) {
|
||||
// 校验充电桩报错,返回校验值 05-桩停用
|
||||
resultMap.put("transactionCode", Constants.ILLEGAL_TRANSACTION_CODE);
|
||||
resultMap.put("accountBalance", accountBalance);
|
||||
resultMap.put("defeatReasonCode", "05");
|
||||
log.error("充电桩主动申请充电生成订单失败, memberId:{}, chargeAmount:{}",
|
||||
memberWalletVO.getMemberId(), chargeAmount, e);
|
||||
return buildFailureResult("05");
|
||||
}
|
||||
|
||||
// 异步创建订单
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
Map<String, Object> map = orderBasicInfoService.generateOrderByCardV2(dto);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, executor);
|
||||
resultMap.put("transactionCode", transactionCode);
|
||||
resultMap.put("accountBalance", chargeAmount.toString());
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
private Map<String, String> buildFailureResult(String defeatReasonCode) {
|
||||
Map<String, String> resultMap = new HashMap<>();
|
||||
resultMap.put("transactionCode", Constants.ILLEGAL_TRANSACTION_CODE);
|
||||
resultMap.put("accountBalance", Constants.ZERO);
|
||||
resultMap.put("defeatReasonCode", defeatReasonCode);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ public class PileApplyMergeChargeHandler extends AbstractYkcHandler{
|
||||
log.info("桩号:{}, 并充订单鉴权成功, 结果map:{}", pileSn, map);
|
||||
} catch (Exception e) {
|
||||
log.error("桩号:{}, 并充订单鉴权失败, ", pileSn, e);
|
||||
map.put("transactionCode", Constants.ILLEGAL_TRANSACTION_CODE);
|
||||
map.put("accountBalance", Constants.ZERO);
|
||||
map.put("defeatReasonCode", "05");
|
||||
}
|
||||
String transactionCode = map.get("transactionCode");
|
||||
// 如果交易流水号不等于Constants.ILLEGAL_TRANSACTION_CODE, 表示鉴权通过
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.jsowell.netty.factory.YKCOperateFactory;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import com.jsowell.pile.service.PileBasicInfoService;
|
||||
import com.jsowell.pile.vo.web.OrderPaymentCheckResult;
|
||||
import com.jsowell.thirdparty.common.CommonService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -297,6 +298,22 @@ public class UploadRealTimeMonitorHandler extends AbstractYkcHandler {
|
||||
// 查询数据库中该订单当前信息
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (Objects.nonNull(orderInfo)) {
|
||||
OrderPaymentCheckResult paymentCheck = orderBasicInfoService.checkAndRepairOrderPayment(orderInfo);
|
||||
boolean paymentReady = paymentCheck.isOrderPaymentReady();
|
||||
if (!paymentCheck.isCheckCompleted()) {
|
||||
// 查询依赖暂时异常时只保留实时数据,等待后续补偿重试,不能据此判定订单未支付。
|
||||
saveRedisFlag = true;
|
||||
log.error("实时监控支付凭证校验未完成,保留实时数据且不变更订单状态, orderCode:{}, reason:{}",
|
||||
orderInfo.getOrderCode(), paymentCheck.getReason());
|
||||
} else if (!paymentReady) {
|
||||
// 实时监控只能保存原始数据并隔离订单,不能把待支付订单改成已支付。
|
||||
saveRedisFlag = true;
|
||||
orderBasicInfoService.handleOrderPaymentAnomaly(orderInfo,
|
||||
paymentCheck.getReason(), null, true);
|
||||
log.warn("实时监控发现支付凭证无效, orderCode:{}, reason:{}",
|
||||
orderInfo.getOrderCode(), paymentCheck.getReason());
|
||||
}
|
||||
if (paymentCheck.isCheckCompleted() && paymentReady) {
|
||||
// 保存此订单的soc
|
||||
YKCUtils.saveSOC(transactionCode, realTimeMonitorData.getSOC());
|
||||
|
||||
@@ -315,11 +332,6 @@ public class UploadRealTimeMonitorHandler extends AbstractYkcHandler {
|
||||
orderInfo.setOrderStatus(OrderStatusEnum.IN_THE_CHARGING.getValue());
|
||||
}
|
||||
|
||||
if (StringUtils.equals(orderInfo.getPayStatus(), OrderPayStatusEnum.unpaid.getValue())) {
|
||||
// 如果发现该订单的支付状态为 0-待支付,将该订单支付状态改为 1-支付完成
|
||||
orderInfo.setPayStatus(OrderPayStatusEnum.paid.getValue());
|
||||
}
|
||||
|
||||
// 如果原来没有开始充电时间就保存当前时间为开始充电时间
|
||||
if (orderInfo.getChargeStartTime() == null) {
|
||||
updateFlag = true;
|
||||
@@ -365,6 +377,7 @@ public class UploadRealTimeMonitorHandler extends AbstractYkcHandler {
|
||||
log.error("异步绑定车辆停车优惠券error", e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
}
|
||||
|
||||
// 充电时保存实时数据到redis
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.jsowell.pile.service.PileBasicInfoService;
|
||||
import com.jsowell.pile.service.PileMsgRecordService;
|
||||
import com.jsowell.pile.service.YKCPushCommandService;
|
||||
import com.jsowell.thirdparty.common.CommonService;
|
||||
import com.jsowell.pile.vo.web.OrderPaymentCheckResult;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -358,6 +359,22 @@ public class PileRabbitListener {
|
||||
// 查询数据库中该订单当前信息
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (Objects.nonNull(orderInfo)) {
|
||||
OrderPaymentCheckResult paymentCheck = orderBasicInfoService.checkAndRepairOrderPayment(orderInfo);
|
||||
boolean paymentReady = paymentCheck.isOrderPaymentReady();
|
||||
if (!paymentCheck.isCheckCompleted()) {
|
||||
// 查询依赖暂时异常时只保留实时数据,等待后续补偿重试,不能据此判定订单未支付。
|
||||
saveRedisFlag = true;
|
||||
log.error("实时监控支付凭证校验未完成,保留实时数据且不变更订单状态, orderCode:{}, reason:{}",
|
||||
orderInfo.getOrderCode(), paymentCheck.getReason());
|
||||
} else if (!paymentReady) {
|
||||
// 实时监控不能把待支付订单伪造成已支付,只保留实时数据并执行幂等停机。
|
||||
saveRedisFlag = true;
|
||||
orderBasicInfoService.handleOrderPaymentAnomaly(orderInfo,
|
||||
paymentCheck.getReason(), null, true);
|
||||
log.warn("实时监控发现支付凭证无效, orderCode:{}, reason:{}",
|
||||
orderInfo.getOrderCode(), paymentCheck.getReason());
|
||||
}
|
||||
if (paymentCheck.isCheckCompleted() && paymentReady) {
|
||||
if (StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())
|
||||
|| StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.STAY_SETTLEMENT.getValue())) {
|
||||
// 在订单状态为 订单完成或待结算,不保存
|
||||
@@ -373,11 +390,6 @@ public class PileRabbitListener {
|
||||
orderInfo.setOrderStatus(OrderStatusEnum.IN_THE_CHARGING.getValue());
|
||||
}
|
||||
|
||||
if (StringUtils.equals(orderInfo.getPayStatus(), OrderPayStatusEnum.unpaid.getValue())) {
|
||||
// 如果发现该订单的支付状态为 0-待支付,将该订单支付状态改为 1-支付完成
|
||||
orderInfo.setPayStatus(OrderPayStatusEnum.paid.getValue());
|
||||
}
|
||||
|
||||
// 如果原来没有开始充电时间就保存当前时间为开始充电时间
|
||||
if (orderInfo.getChargeStartTime() == null) {
|
||||
updateFlag = true;
|
||||
@@ -387,6 +399,7 @@ public class PileRabbitListener {
|
||||
if (updateFlag) {
|
||||
orderBasicInfoService.updateOrderBasicInfo(orderInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 充电时保存实时数据到redis
|
||||
|
||||
Reference in New Issue
Block a user