update 后管取消订单支持退款与结算逻辑

This commit is contained in:
jsowell
2026-08-05 14:08:02 +08:00
parent 45f9eb3061
commit 323b43be53
2 changed files with 191 additions and 3 deletions

View File

@@ -375,6 +375,193 @@ public class OrderService {
log.info("主动停止充电, orderCode:{}, transactionCode:{}, 发送停机指令成功", dto.getOrderCode(), orderInfo.getTransactionCode());
}
/**
* 后管取消订单
* 除最终状态(订单完成/订单关闭)外,其他状态均可取消:
* - 待支付:直接关闭订单
* - 已支付未启动充电:关闭订单并退款
* - 充电中:发送停机指令,由充电桩返回交易记录后自动结算(结算含退款)
* - 待结算/待补缴/异常/可疑:按最近一次充电数据直接结算(结算含退款)
*
* @param orderCode 订单编号
*/
public void cancelOrderForWeb(String orderCode) {
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
if (orderBasicInfo == null) {
throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_INFO_IS_NULL);
}
String orderStatus = orderBasicInfo.getOrderStatus();
String payStatus = orderBasicInfo.getPayStatus();
// 最终状态不可取消
if (StringUtils.equals(orderStatus, OrderStatusEnum.ORDER_COMPLETE.getValue())
|| StringUtils.equals(orderStatus, OrderStatusEnum.ORDER_CLOSE_TIMEOUT.getValue())) {
throw new BusinessException(ReturnCodeEnum.CODE_ORDER_INFO_ERROR);
}
// 1. 待支付:直接关闭订单
if (StringUtils.equals(payStatus, OrderPayStatusEnum.unpaid.getValue())) {
closeOrderForCancel(orderBasicInfo);
log.info("取消订单-待支付订单直接关闭, orderCode:{}", orderCode);
return;
}
// 2. 已支付未启动充电:关闭订单并退款
if (StringUtils.equals(orderStatus, OrderStatusEnum.NOT_START.getValue())) {
closeOrderForCancel(orderBasicInfo);
refundOrderForCancel(orderBasicInfo);
log.info("取消订单-已支付未启动订单关闭并退款, orderCode:{}", orderCode);
return;
}
// 3. 充电中:发送停机指令,结算由桩返回交易记录后自动完成
if (StringUtils.equals(orderStatus, OrderStatusEnum.IN_THE_CHARGING.getValue())) {
sendStopChargingCommand(orderBasicInfo);
log.info("取消订单-充电中订单发送停机指令, orderCode:{}", orderCode);
return;
}
// 4. 待结算/待补缴/异常/可疑:直接触发结算(含退款)
settleOrderForCancel(orderBasicInfo);
log.info("取消订单-未结算订单执行结算, orderCode:{}", orderCode);
}
/**
* 关闭订单(状态改为 7-订单关闭)
*/
private void closeOrderForCancel(OrderBasicInfo orderBasicInfo) {
orderBasicInfo.setOrderStatus(OrderStatusEnum.ORDER_CLOSE_TIMEOUT.getValue());
orderBasicInfoService.updateOrderBasicInfo(orderBasicInfo);
}
/**
* 已支付未启动订单退款:按实际支付金额全额退款
*/
private void refundOrderForCancel(OrderBasicInfo orderBasicInfo) {
String orderCode = orderBasicInfo.getOrderCode();
String payMode = orderBasicInfo.getPayMode();
// 白名单支付无需退款
if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())) {
log.info("取消订单退款-白名单支付不退款, orderCode:{}", orderCode);
return;
}
// 已存在退款记录,说明已经退过,跳过
List<OrderDetailInfoVO.OrderRefundInfo> refundInfoList = orderBasicInfoService.getOrderRefundInfoList(orderBasicInfo);
if (CollectionUtils.isNotEmpty(refundInfoList)) {
log.info("取消订单退款-订单已存在退款记录, 跳过退款, orderCode:{}", orderCode);
return;
}
// 退款金额取实际支付金额
BigDecimal refundAmount = orderBasicInfo.getPayAmount() != null
? orderBasicInfo.getPayAmount()
: orderBasicInfo.getOrderAmount();
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
log.info("取消订单退款-退款金额小于等于0, 跳过退款, orderCode:{}", orderCode);
return;
}
orderBasicInfo.setRefundAmount(refundAmount);
orderBasicInfoService.updateOrderBasicInfo(orderBasicInfo);
// 余额支付:退到会员余额
if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_PRINCIPAL_BALANCE.getValue())) {
orderBasicInfoService.refundBalancePaymentOrder(orderBasicInfo);
return;
}
// 在线支付:根据支付机构退款
MemberTransactionRecord transactionRecord = memberTransactionRecordService.selectByOrderCode(orderCode, ActionTypeEnum.FORWARD.getValue());
if (transactionRecord == null) {
log.warn("取消订单退款-未查到正向支付流水, 无法自动退款, orderCode:{}", orderCode);
return;
}
String wechatAppId = pileMerchantInfoService.queryAppIdByMerchantId(orderBasicInfo.getMerchantId());
ApplyRefundDTO applyRefundDTO = new ApplyRefundDTO();
applyRefundDTO.setOrderCode(orderCode);
applyRefundDTO.setRefundType(Constants.ONE);
applyRefundDTO.setRefundAmount(refundAmount);
applyRefundDTO.setWechatAppId(wechatAppId);
applyRefundDTO.setMemberId(orderBasicInfo.getMemberId());
if (StringUtils.equals(transactionRecord.getPaymentInstitutions(), PaymentInstitutionsEnum.ADAPAY.getValue())) {
// 汇付支付:撤销/退款
orderBasicInfoService.refundOrderWithAdapay(applyRefundDTO);
} else if (StringUtils.equals(transactionRecord.getPaymentInstitutions(), PaymentInstitutionsEnum.WECHAT_PAY.getValue())) {
// 微信支付:退款
orderBasicInfoService.refundOrderWithWechatPay(applyRefundDTO);
} else {
log.info("取消订单退款-支付机构:{}不支持自动退款, orderCode:{}", transactionRecord.getPaymentInstitutions(), orderCode);
}
}
/**
* 充电中订单发送停机指令(含第三方桩统一停止充电)
*/
private void sendStopChargingCommand(OrderBasicInfo orderBasicInfo) {
List<ThirdPartySnRelationVO> list = snRelationService.selectSnRelationListByParams(
orderBasicInfo.getStationId(), orderBasicInfo.getPileSn(), null);
if (CollectionUtils.isNotEmpty(list)) {
for (ThirdPartySnRelationVO vo : list) {
ThirdPartyCommonStopChargeDTO commonStopChargeDTO = new ThirdPartyCommonStopChargeDTO();
commonStopChargeDTO.setThirdPartyType(vo.getThirdPartyType());
commonStopChargeDTO.setOrderBasicInfo(orderBasicInfo);
String result = commonService.commonStopCharge(commonStopChargeDTO);
log.info("取消订单-订单:{} 调用统一停止充电接口, result:{}", orderBasicInfo.getOrderCode(), result);
}
} else {
pileRemoteService.remoteStopCharging(orderBasicInfo.getPileSn(),
orderBasicInfo.getConnectorCode(), orderBasicInfo.getTransactionCode());
}
log.info("取消订单-订单:{} 发送停机指令成功", orderBasicInfo.getOrderCode());
}
/**
* 待结算/待补缴/异常/可疑订单:按最近一次充电数据执行结算(结算含退款)
*/
private void settleOrderForCancel(OrderBasicInfo orderBasicInfo) {
TransactionRecordsData data = null;
// 优先取桩返回的交易记录
List<PileMsgRecord> pileFeedList = pileMsgRecordService.queryPileFeedList(
orderBasicInfo.getPileSn(), "0x3B", orderBasicInfo.getTransactionCode());
for (PileMsgRecord pileMsgRecord : pileFeedList) {
TransactionRecordsData parse = JSONObject.parseObject(pileMsgRecord.getJsonMsg(), TransactionRecordsData.class);
if (StringUtils.equals(parse.getTransactionCode(), orderBasicInfo.getTransactionCode())) {
data = parse;
data.setVinCode("");
}
}
// 没有交易记录时,用最后一次实时数据兜底
if (data == null) {
data = new TransactionRecordsData();
List<RealTimeMonitorData> chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(orderBasicInfo.getTransactionCode());
if (CollectionUtils.isNotEmpty(chargingRealTimeData)) {
RealTimeMonitorData realTimeMonitorData = chargingRealTimeData.get(0);
data.setConsumptionAmount(realTimeMonitorData.getChargingAmount());
data.setTotalElectricity(realTimeMonitorData.getChargingDegree());
} else {
data.setConsumptionAmount(Constants.ZERO);
data.setTotalElectricity(Constants.ZERO);
}
data.setStopReasonMsg("后管取消订单,操作人:" + SecurityUtils.getUsername());
}
String mode = pileMerchantInfoService.getDelayModeByMerchantId(orderBasicInfo.getMerchantId());
AbstractProgramLogic orderLogic = ProgramLogicFactory.getProgramLogic(mode);
if (YouDianUtils.isEBikePileSn(orderBasicInfo.getPileSn())) {
// 电单车桩
BigDecimal chargingDegree = StringUtils.isNotBlank(data.getTotalElectricity())
? new BigDecimal(data.getTotalElectricity()) : BigDecimal.ZERO;
EBikeMessageCmd03 message = new EBikeMessageCmd03(chargingDegree, EBikeStopReasonEnum.EXTERNAL_OPERATION_STOPPED.getCode());
orderLogic.settleOrderForEBike(message, orderBasicInfo);
} else {
// 汽车桩
orderLogic.settleOrder(data, orderBasicInfo);
}
log.info("取消订单-订单:{} 结算完成", orderBasicInfo.getOrderCode());
}
/**
* 微信退款
*

View File

@@ -179,15 +179,16 @@ public class OrderBasicInfoController extends BaseController {
}
/**
* 取消订单/关闭待支付订单
* 仅待支付状态的订单可取消,取消后订单状态变为 7-订单关闭
* 取消订单
* 除订单完成/订单关闭外均可取消:
* 待支付直接关闭;已支付未启动关闭并退款;充电中发送停机指令并结算;未结算状态直接结算
* http://localhost:8080/order/closeOrderByOrderCode/{orderCode}
*/
@PreAuthorize("@ss.hasPermi('order:order:edit')")
@Log(title = "订单", businessType = BusinessType.UPDATE)
@PostMapping("/closeOrderByOrderCode/{orderCode}")
public AjaxResult closeOrderByOrderCode(@PathVariable("orderCode") String orderCode) {
orderBasicInfoService.closeOrderByOrderCode(orderCode);
orderService.cancelOrderForWeb(orderCode);
return AjaxResult.success();
}