@@ -2,6 +2,7 @@ package com.intc.weixin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper ;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper ;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page ;
import com.intc.common.core.exception.ServiceException ;
import com.intc.common.tenant.helper.TenantHelper ;
import com.intc.fishery.constant.DefineDeviceWarnCode ;
@@ -28,6 +29,7 @@ import org.springframework.transaction.support.TransactionTemplate;
import java.text.SimpleDateFormat ;
import java.util.* ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.concurrent.atomic.AtomicLong ;
import java.util.stream.Collectors ;
/**
@@ -51,6 +53,13 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
* 订单处理锁,防止并发处理同一订单
*/
private static final ConcurrentHashMap < Long , Long > ORDER_PROCESSING_LOCK = new ConcurrentHashMap < > ( ) ;
private static final AtomicLong COMPENSATION_HISTORY_CURSOR_ID = new AtomicLong ( 0L ) ;
private static final AtomicLong REPAIR_HISTORY_CURSOR_ID = new AtomicLong ( 0L ) ;
private static final int RECENT_SCAN_SIZE = 200 ;
private static final int HISTORY_SCAN_SIZE = 200 ;
private static final int ORDER_UPDATE_FAILED = - 1 ;
private static final int ORDER_ALREADY_SUCCESS = 0 ;
private static final int ORDER_UPDATED = 1 ;
@Override
@Transactional ( rollbackFor = Exception . class )
@@ -196,48 +205,45 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
// 批量处理订单:先查微信确认状态,避免错误关闭已支付订单
for ( PayOrder order : unpaidOrders ) {
// 如果订单有prepayId, 先向微信查询实际状态
if ( order . getPrepayId ( ) ! = n ull & & ! order . getPrepayId ( ) . isEmpty ( ) ) {
try {
String queryR esult = wxPayService . queryOrderByOutTradeNo ( order . getOutTradeNumber ( ) ) ;
if ( queryResult ! = null ) {
JSONObject resultJson = JSONUtil . parseObj ( queryResult ) ;
String tradeState = resultJson . getStr ( " trade_state " ) ;
if ( " SUCCESS " . equalsIgnoreCase ( tradeState ) ) {
// 微信侧已支付成功,执行补偿而不是关闭
log . info ( " 关闭订单前查询发现已支付: orderId={}, outTradeNumber={} " , order . getId ( ) , order . getOutTradeNumber ( ) ) ;
compensateSingleOrder ( order , resultJson ) ;
continue ;
}
if ( " CLOSED " . equalsIgnoreCase ( tradeState ) ) {
p ayOrderMapper . update ( null ,
new LambdaUpdateWrapper < PayOrder > ( )
. eq ( PayOrder : : getId , order . getId ( ) )
. set ( PayOrder : : getOr der Status , PayOrderStatus . CLOSED )
. set ( PayOrder : : getTradeState , " CLOSED " )
. set ( PayOrder : : getTradeStateDescription , " 微信侧已关闭 " )
) ;
log . info ( " 旧订单微信侧已关闭,同步本地状态: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
continue ;
}
if ( " USERPAYING " . equalsIgnoreCase ( tradeState ) ) {
log . info ( " 旧订单仍在支付中,跳过关闭: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
continue ;
}
try {
String queryRes ult = wxPayService . queryOrderByOutTradeNo ( order . getOutTradeNumber ( ) ) ;
if ( queryResult ! = null ) {
JSONObject r esultJson = JSONUtil . parseObj ( queryResult ) ;
String tradeState = resultJson . getStr ( " trade_state " ) ;
if ( " SUCCESS " . equalsIgnoreCase ( tradeState ) ) {
// 微信侧已支付成功,执行补偿而不是关闭
log . info ( " 关闭订单前查询发现已支付: orderId={}, outTradeNumber={} " , order . getId ( ) , order . getOutTradeNumber ( ) ) ;
compensateSingleOrder ( order , resultJson ) ;
continue ;
}
if ( " CLOSED " . equalsIgnoreCase ( tradeState ) ) {
payOrderMapper . update ( null ,
new LambdaUpdateWrapper < PayOrder > ( )
. eq ( P ayOrder: : getId , order . getId ( ) )
. set ( PayOrder : : getOrderStatus , PayOrderStatus . CLOSED )
. set ( PayOrder : : getTradeState , " CLOSED " )
. set ( PayOrder : : getTra deStateDescription , " 微信侧已关闭 " )
);
log . info ( " 旧订单微信侧已关闭,同步本地状态: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
continue ;
}
if ( " USERPAYING " . equalsIgnoreCase ( tradeState ) ) {
log . info ( " 旧订单仍在支付中,跳过关闭: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
continue ;
}
} catch ( Exception e ) {
log . warn ( " 关闭订单前查询微信失败,跳过关闭避免误关已支付订单: orderId={}, error={} " , order . getId ( ) , e . getMessage ( ) ) ;
continue ;
}
} catch ( Exception e ) {
log . warn ( " 关闭订单前查询微信失败,跳过关闭避免误关已支付订单: orderId={}, error={} " , order . getId ( ) , e . getMessage ( ) ) ;
continue ;
}
boolean closed = wxPayService . closeOrder ( order . getOutTradeNumber ( ) ) ;
if ( ! closed ) {
log . warn ( " 微信关单失败,跳过本地关闭: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
continue ;
}
boolean closed = wxPayService . closeOrder ( order . getOutTradeNumber ( ) ) ;
if ( ! closed ) {
log . warn ( " 微信关单失败,跳过本地关闭: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
continue ;
}
// 微信侧确认未支付,安全关闭
@@ -254,7 +260,6 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
}
@Override
@Transactional ( rollbackFor = Exception . class )
public boolean handlePaymentSuccess ( String outTradeNumber , String transactionId , Integer payerTotal ,
String payerCurrency , String successTime , String tradeState ,
String tradeStateDescription , String bankType ) {
@@ -267,7 +272,9 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
// 2. 检查订单状态,如果已经是成功状态,直接返回
if ( PayOrderStatus . SUCCESS . equals ( order . getOrderStatus ( ) ) ) {
log . info ( " 订单已处理过: orderId={}, outTradeNumber={} " , order . getId ( ) , outTradeNumb er) ;
int repairCount = repairOrderDeviceDeadTime ( ord er) ;
log . info ( " 订单已处理过: orderId={}, outTradeNumber={}, repairDeviceCount={} " ,
order . getId ( ) , outTradeNumber , repairCount ) ;
return true ;
}
@@ -291,33 +298,55 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
// 7. 更新订单状态
Integer orderStatus = PayOrderStatusUtil . convertTradeStateToOrderStatus ( tradeState ) ;
int updateRows = payOrderMapper . update ( null ,
new LambdaUpdateWrapper < P ayOrder> ( )
. eq ( PayOrder : : getId , order . getId ( ) )
. n e( PayOrder : : getOrderStatus , PayOrderStatus . SUCCESS )
. set ( PayOrder : : getTransactionId , transactionId )
. set ( PayOrder : : getPayerTotal , payerTotal )
. set ( PayOrder : : getPayerCurrency , payerCurrency )
. set ( PayOrder : : getSuccessTime , successDate )
. set ( PayOrder : : getTradeStat e , tradeSt ate)
. set ( PayOrder : : getTradeStateDescription , tradeStateDescription )
. set ( PayOrder : : getBankType , bankType )
. set ( PayOrder : : getOrderStatus , orderStatus )
) ;
Integer updateResult = transactionTemplate . execute ( status - > {
int updateRows = p ayOrderMapper . update ( null ,
new LambdaUpdateWrapper < PayOrder > ( )
. eq ( PayOrder : : getId , order . getId ( ) )
. ne ( PayOrder : : getOrderStatus , PayOrderStatus . SUCCESS )
. set ( PayOrder : : getTransactionId , transactionId )
. set ( PayOrder : : getPayerTotal , payerTotal )
. set ( PayOrder : : getPayerCurrency , payerCurrency )
. set ( PayOrder : : getSuccessTim e , successD ate)
. set ( PayOrder : : getTradeState , tradeState )
. set ( PayOrder : : getTradeStateDescription , tradeStateDescription )
. set ( PayOrder : : getBankType , bankType )
. set ( PayOrder : : getOrderStatus , orderStatus )
) ;
if ( updateRows < = 0 ) {
PayOrder latestOrder = payOrderMapper . selectById ( order . getId ( ) ) ;
if ( latestOrder ! = null & & PayOrderStatus . SUCCESS . equals ( latestOrder . getOrderStatus ( ) ) ) {
log . info ( " 订单已由其他回调处理成功: orderId={}, outTradeNumber={} " , order . getId ( ) , outTradeNumber ) ;
return true ;
if ( updateRows < = 0 ) {
PayOrder latestOrder = payOrderMapper . selectById ( order . getId ( ) ) ;
if ( latestOrder ! = null & & PayOrderStatus . SUCCESS . equals ( latestOrder . getOrderStatus ( ) ) ) {
log . info ( " 订单已由其他回调处理成功: orderId={}, outTradeNumber={} " , order . getId ( ) , outTradeNumber ) ;
return ORDER_ALREADY_SUCCESS ;
}
log . warn ( " 订单状态更新失败: orderId={}, outTradeNumber={} " , order . getId ( ) , outTradeNumber ) ;
return ORDER_UPDATE_FAILED ;
}
log . warn ( " 订单状态更新失败: orderId={}, outTradeNumber={} " , order . getId ( ) , outTradeNumber ) ;
return ORDER_UPDATED ;
} ) ;
if ( updateResult = = null | | ORDER_UPDATE_FAILED = = updateResult ) {
return false ;
}
// 8. 如果支付成功,更新设备到期时间
if ( PayOrderStatus . SUCCESS . equals ( orderStatus ) ) {
updateDeviceDeadTime ( order . getId ( ) ) ;
if ( ORDER_UPDATED = = updateResult ) {
try {
int updatedDevices = updateDeviceDeadTime ( order . getId ( ) ) ;
if ( updatedDevices < = 0 ) {
log . warn ( " 订单已支付成功,但本次未更新任何设备有效期,等待修复任务兜底: orderId={}, outTradeNumber={} " ,
order . getId ( ) , outTradeNumber ) ;
}
} catch ( Exception e ) {
log . error ( " 订单已支付成功,但更新设备有效期异常,等待修复任务兜底: orderId={}, outTradeNumber={}, error={} " ,
order . getId ( ) , outTradeNumber , e . getMessage ( ) , e ) ;
}
} else {
int repairCount = repairOrderDeviceDeadTime ( order ) ;
log . info ( " 订单已由其他实例处理成功,本次只做设备有效期修复: orderId={}, outTradeNumber={}, repairDeviceCount={} " ,
order . getId ( ) , outTradeNumber , repairCount ) ;
}
}
log . info ( " 订单支付处理完成: orderId={}, outTradeNumber={}, status={} " ,
@@ -386,58 +415,101 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
*
* @param orderId 订单ID
*/
private void updateDeviceDeadTime ( Long orderId ) {
private int updateDeviceDeadTime ( Long orderId ) {
// 查询订单关联的设备充值记录
List < PayDevice > payDevices = queryPayDevicesByOrderId ( orderId ) ;
if ( payDevices = = null | | payDevices . isEmpty ( ) ) {
log . warn ( " 订单没有关联的设备充值记录: orderId={} " , orderId ) ;
return ;
return 0 ;
}
int updatedCount = 0 ;
// 批量更新设备到期时间( 忽略租户过滤, 因为设备可能由IOT模块写入, 租户ID可能不一致)
for ( PayDevice payDevice : payDevices ) {
// 仅通过 serialNum 查找设备,不限制 userId, 因为允许代充( 支付者可能与设备所有者不同)
Device device = TenantHelper . ignore ( ( ) - >
deviceMapper . selectOne (
try {
if ( payDevice . getAddMonth ( ) = = null ) {
log . error ( " 设备充值记录缺少续费月数,跳过设备有效期更新: payDeviceId={}, orderId={}, serialNum={} " ,
payDevice . getId ( ) , orderId , payDevice . getSerialNum ( ) ) ;
continue ;
}
Device device = queryDeviceByPayDevice ( payDevice ) ;
if ( device = = null ) {
log . error ( " 设备不存在,订单成功状态已保留,等待人工核对设备编号: orderId={}, payDeviceId={}, serialNum={} " ,
orderId , payDevice . getId ( ) , payDevice . getSerialNum ( ) ) ;
continue ;
}
// 计算新的到期时间
Date newDeadTime = calculateNewDeadTime ( device . getDeadTime ( ) , payDevice . getAddMonth ( ) ) ;
payDeviceMapper . update ( null ,
new LambdaUpdateWrapper < PayDevice > ( )
. eq ( PayDevice : : getId , payDevice . getId ( ) )
. set ( PayDevice : : getDeadTime , newDeadTime )
) ;
// 清除 warnCode 中的「设备到期」标志位(续费后恢复正常状态)
Integer oldWarnCode = device . getWarnCode ( ) ;
Integer newWarnCode = oldWarnCode ! = null
? ( oldWarnCode & ~ DefineDeviceWarnCode . DeviceTimeDead )
: 0 ;
// 更新设备到期时间,并同步清除到期告警位
int updateRows = TenantHelper . ignore ( ( ) - > deviceMapper . update ( null ,
new LambdaUpdateWrapper < Device > ( )
. eq ( Device : : getId , device . getId ( ) )
. set ( Device : : getDeadTime , newDeadTime )
. set ( Device : : getWarnCode , newWarnCode )
) ) ;
if ( updateRows < = 0 ) {
log . error ( " 更新设备有效期失败,订单成功状态已保留,等待修复任务兜底: orderId={}, payDeviceId={}, serialNum={}, deviceId={} " ,
orderId , payDevice . getId ( ) , payDevice . getSerialNum ( ) , device . getId ( ) ) ;
continue ;
}
updatedCount + + ;
log . info ( " 续费成功,更新设备到期时间并清除到期告警: deviceId={}, serialNum={}, newDeadTime={}, oldWarnCode={}, newWarnCode={} " ,
device . getId ( ) , device . getSerialNum ( ) , newDeadTime , oldWarnCode , newWarnCode ) ;
} catch ( Exception e ) {
log . error ( " 更新单台设备有效期异常,订单成功状态已保留: orderId={}, payDeviceId={}, serialNum={}, error={} " ,
orderId , payDevice . getId ( ) , payDevice . getSerialNum ( ) , e . getMessage ( ) , e ) ;
}
}
return updatedCount ;
}
private Device queryDeviceByPayDevice ( PayDevice payDevice ) {
return TenantHelper . ignore ( ( ) - > {
if ( payDevice . getTenantId ( ) ! = null & & ! payDevice . getTenantId ( ) . isEmpty ( ) ) {
List < Device > sameTenantDevices = deviceMapper . selectList (
new LambdaQueryWrapper < Device > ( )
. eq ( Device : : getSerialNum , payDevice . getSerialNum ( ) )
)
);
if ( device = = null ) {
throw new ServiceException ( " 设备不存在: " + pay Device. getSerialNum ( ) ) ;
. eq ( Device : : getTenantId , payDevice . getTenantId ( ) )
. orderByDesc ( Device : : getUpdateTime )
. orderByDesc ( Device : : getCreateTime )
) ;
if ( sameTenantDevices ! = null & & ! sameTenant Devices . isEmpty ( ) ) {
return sameTenantDevices . get ( 0 ) ;
}
}
// 计算新的到期时间
Date newDeadTime = calculateNewDeadTime ( device . getDeadTime ( ) , payDevice . getAddMonth ( ) ) ;
// 清除 warnCode 中的「设备到期」标志位(续费后恢复正常状态)
Integer oldWarnCode = d evice. getWarnCode ( ) ;
Integer newWarnCode = oldWarnCode ! = null
? ( oldWarnCode & ~ DefineDeviceWarnCode . DeviceTimeDead )
: 0 ;
// 更新设备到期时间,并同步清除到期告警位
int updateRows = TenantHelper . ignore ( ( ) - > deviceMapper . update ( null ,
new LambdaUpdateWrapper < Device > ( )
. eq ( Device : : getId , device . getId ( ) )
. set ( Device : : getDeadTime , newDeadTime )
. set ( Device : : getWarnCode , newWarnCode )
) ) ;
if ( updateRows < = 0 ) {
throw new ServiceException ( " 更新设备有效期失败: " + payDevice . getSerialNum ( ) ) ;
}
payDeviceMapper . update ( null ,
new LambdaUpdateWrapper < PayDevice > ( )
. eq ( PayDevice : : getId , payDevice . getId ( ) )
. set ( PayDevice : : getDeadTime , newDeadTime )
List < Device > devices = deviceMapper . selectList (
new LambdaQueryWrapper < Device > ( )
. eq ( Device : : getSerialNum , payDevice . getSerialNum ( ) )
. orderByDesc ( Device : : getUpdateTime )
. orderByDesc ( D evice: : getCreateTime )
) ;
log . info ( " 续费成功,更新设备到期时间并清除到期告警: deviceId={}, serialNum={}, newDeadTime={}, oldWarnCode={}, newWarnCode={} " ,
device . getId ( ) , device . getSerialNum ( ) , newDeadTime , oldWarnCode , newWarnCode ) ;
}
if ( devices = = null | | devices . isEmpty ( ) ) {
return null ;
}
if ( devices . size ( ) > 1 ) {
log . warn ( " 根据设备编号查到多台设备,已取最近更新的一台: serialNum={}, selectedDeviceId={}, matchedCount={} " ,
payDevice . getSerialNum ( ) , devices . get ( 0 ) . getId ( ) , devices . size ( ) ) ;
}
return devices . get ( 0 ) ;
} ) ;
}
/**
@@ -464,76 +536,143 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
@Override
public int compensateUnpaidOrders ( ) {
// 查询状态为未支付/支付中/支付失败/已关闭, 已有prepayId, 且创建时间在1分钟前到48小时内的订单
// 包含CLOSED是因为closeUnpaidOrders可能在微信支付成功前就关闭了订单( 网络延迟导致查单返回NOTPAY)
// 最新订单优先处理,历史订单用游标慢慢补扫,避免老异常数据被固定时间窗口或分页上限漏掉。
Date now = new Date ( ) ;
Date startTime = new Date ( now . getTime ( ) - 48L * 60 * 60 * 1000 ) ; // 48小时前
Date endTime = new Date ( now . getTime ( ) - 1L * 60 * 1000 ) ; // 1分钟前
List < PayOrder > pe ndingOrders = payOrderMapper . selectList (
new LambdaQueryWrapper < PayOrder > ( )
. in ( PayO rder: : getOrderStatus , Arrays . asList (
PayOrderStatus . NOTPAY ,
PayOrderStatus . USERPAYING ,
PayOrderStatus . PAYERROR ,
PayOrderStatus . CLOSED
) )
. isNotNull ( PayOrder : : getPrepayId )
. ne ( PayOrder : : getPrepayId , " " )
. ge ( PayOrder : : getCreateTime , startTime )
. le ( PayOrder : : getCreateTime , endTime )
) ;
Map < Long , PayOrder > ca ndidateMap = new LinkedHashMap < > ( ) ;
scanRecentCompensationOrders ( endTime ) . forEach ( order - > candidateMap . put ( order . getId ( ) , order ) ) ;
scanHistoryCompensationOrders ( endTime ) . forEach ( o rder - > candidateMap . putIfAbsent ( order . getId ( ) , order ) ) ;
if ( pe ndingOrders = = null | | pendingOrders . isEmpty ( ) ) {
if ( ca ndidateMap . isEmpty ( ) ) {
return 0 ;
}
log . info ( " [支付补偿] 扫描到 {} 笔待补偿订单 " , pendingOrders . size ( ) ) ;
int compensatedCount = 0 ;
for ( PayOrder order : pe ndingOrders ) {
try {
String queryResult = wxPayService . queryOrderByOutTradeNo ( order . getOutTradeNumber ( ) ) ;
if ( queryResult = = null ) {
log . warn ( " [支付补偿] 查询微信订单失败: outTradeNumber={} " , order . getOutTradeNumber ( ) ) ;
continue ;
}
JSONObject resultJson = JSONUtil . parseObj ( queryResult ) ;
String tradeState = resultJson . getStr ( " trade_state " ) ;
if ( " SUCCESS " . equalsIgnoreCase ( tradeState ) ) {
// 微信侧已支付成功,执行补偿
boolean success = compensateSingleOrder ( order , resultJson ) ;
if ( success ) {
compensatedCount + + ;
log . info ( " [支付补偿] 补偿成功: orderId={}, outTradeNumber={} " , order . getId ( ) , order . getOutTradeNumber ( ) ) ;
}
} else if ( " CLOSED " . equalsIgnoreCase ( tradeState ) ) {
// 微信侧已关闭,同步关闭本地订单
payOrderMapper . update ( null ,
new LambdaUpdateWrapper < PayOrder > ( )
. eq ( PayOrder : : getId , order . getId ( ) )
. set ( PayOrder : : getOrderStatus , PayOrderStatus . CLOSED )
. set ( PayOrder : : getTradeState , " CLOSED " )
. set ( PayOrder : : getTradeStateDescription , " 微信侧已关闭 " )
) ;
log . info ( " [支付补偿] 订单微信侧已关闭,同步关闭: orderId={} " , order . getId ( ) ) ;
}
// NOTPAY/USERPAYING 状态不处理,等待下次扫描
} catch ( Exception e ) {
log . error ( " [支付补偿] 处理订单异常: orderId={}, outTradeNumber={}, error={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) , e . getMessage ( ) , e ) ;
for ( PayOrder order : ca ndidateMap . values ( ) ) {
if ( compensateOrderFromWechat ( order ) ) {
compensatedCount + + ;
}
}
log . info ( " [支付补偿] 本次扫描 {} 笔待补偿订单 " , candidateMap . size ( ) ) ;
if ( compensatedCount > 0 ) {
log . info ( " [支付补偿] 本次补偿完成 {} 笔订单 " , compensatedCount ) ;
}
return compensatedCount ;
}
private List < PayOrder > scanRecentCompensationOrders ( Date endTime ) {
Page < PayOrder > page = payOrderMapper . selectPage (
new Page < > ( 1 , RECENT_SCAN_SIZE , false ) ,
buildCompensationOrderQuery ( endTime )
. orderByDesc ( PayOrder : : getCreateTime )
. orderByDesc ( PayOrder : : getId )
) ;
return page . getRecords ( ) ! = null ? page . getRecords ( ) : Collections . emptyList ( ) ;
}
private List < PayOrder > scanHistoryCompensationOrders ( Date endTime ) {
long cursorId = COMPENSATION_HISTORY_CURSOR_ID . get ( ) ;
LambdaQueryWrapper < PayOrder > queryWrapper = buildCompensationOrderQuery ( endTime ) ;
if ( cursorId > 0 ) {
queryWrapper . gt ( PayOrder : : getId , cursorId ) ;
}
Page < PayOrder > page = payOrderMapper . selectPage (
new Page < > ( 1 , HISTORY_SCAN_SIZE , false ) ,
queryWrapper . orderByAsc ( PayOrder : : getId )
) ;
List < PayOrder > records = page . getRecords ( ) ;
if ( records = = null | | records . isEmpty ( ) ) {
if ( cursorId > 0 ) {
COMPENSATION_HISTORY_CURSOR_ID . set ( 0L ) ;
log . info ( " [支付补偿] 历史扫描游标已到末尾,下次从最早订单重新扫描 " ) ;
}
return Collections . emptyList ( ) ;
}
COMPENSATION_HISTORY_CURSOR_ID . set ( records . get ( records . size ( ) - 1 ) . getId ( ) ) ;
return records ;
}
private LambdaQueryWrapper < PayOrder > buildCompensationOrderQuery ( Date endTime ) {
return new LambdaQueryWrapper < PayOrder > ( )
. and ( wrapper - > wrapper
. in ( PayOrder : : getOrderStatus , Arrays . asList (
PayOrderStatus . NOTPAY ,
PayOrderStatus . USERPAYING ,
PayOrderStatus . PAYERROR
) )
. or ( )
. and ( closed - > closed
. eq ( PayOrder : : getOrderStatus , PayOrderStatus . CLOSED )
. and ( unconfirmedClosed - > unconfirmedClosed
. isNull ( PayOrder : : getTradeStateDescription )
. or ( )
. ne ( PayOrder : : getTradeStateDescription , " 微信侧已关闭 " )
)
)
. or ( )
. isNull ( PayOrder : : getOrderStatus )
)
. isNotNull ( PayOrder : : getOutTradeNumber )
. ne ( PayOrder : : getOutTradeNumber , " " )
. and ( time - > time
. isNull ( PayOrder : : getCreateTime )
. or ( )
. le ( PayOrder : : getCreateTime , endTime )
) ;
}
private boolean compensateOrderFromWechat ( PayOrder order ) {
try {
String queryResult = wxPayService . queryOrderByOutTradeNo ( order . getOutTradeNumber ( ) ) ;
if ( queryResult = = null ) {
log . warn ( " [支付补偿] 查询微信订单失败: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
return false ;
}
JSONObject resultJson = JSONUtil . parseObj ( queryResult ) ;
String tradeState = resultJson . getStr ( " trade_state " ) ;
log . info ( " [支付补偿] 微信查单结果: orderId={}, outTradeNumber={}, localStatus={}, tradeState={}, prepayId={}, createTime={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) , order . getOrderStatus ( ) , tradeState , order . getPrepayId ( ) , order . getCreateTime ( ) ) ;
if ( " SUCCESS " . equalsIgnoreCase ( tradeState ) ) {
boolean success = compensateSingleOrder ( order , resultJson ) ;
if ( success ) {
log . info ( " [支付补偿] 补偿成功: orderId={}, outTradeNumber={} " , order . getId ( ) , order . getOutTradeNumber ( ) ) ;
} else {
log . warn ( " [支付补偿] 微信已支付但本地补偿失败: orderId={}, outTradeNumber={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) ) ;
}
return success ;
}
if ( " CLOSED " . equalsIgnoreCase ( tradeState ) ) {
syncLocalClosedOrder ( order . getId ( ) , " 微信侧已关闭 " ) ;
}
return false ;
} catch ( Exception e ) {
log . error ( " [支付补偿] 处理订单异常: orderId={}, outTradeNumber={}, error={} " ,
order . getId ( ) , order . getOutTradeNumber ( ) , e . getMessage ( ) , e ) ;
return false ;
}
}
private void syncLocalClosedOrder ( Long orderId , String description ) {
payOrderMapper . update ( null ,
new LambdaUpdateWrapper < PayOrder > ( )
. eq ( PayOrder : : getId , orderId )
. ne ( PayOrder : : getOrderStatus , PayOrderStatus . SUCCESS )
. set ( PayOrder : : getOrderStatus , PayOrderStatus . CLOSED )
. set ( PayOrder : : getTradeState , " CLOSED " )
. set ( PayOrder : : getTradeStateDescription , description )
) ;
log . info ( " [支付补偿] 订单微信侧已关闭,同步关闭: orderId={} " , orderId ) ;
}
/**
* 对单笔订单执行支付补偿
*
@@ -557,7 +696,7 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
return false ;
}
Boolean success = transactionTemplate . execute ( status - > handlePaymentSuccess (
return handlePaymentSuccess (
order . getOutTradeNumber ( ) ,
transactionId ,
payerTotal ,
@@ -566,8 +705,7 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
tradeState ,
tradeStateDesc ,
bankType
) ) ;
return Boolean . TRUE . equals ( success ) ;
) ;
}
@Override
@@ -582,7 +720,9 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
return false ;
}
if ( PayOrderStatus . SUCCESS . equals ( order . getOrderStatus ( ) ) ) {
log . info ( " [支付确认] 订单已成功: orderId={}, outTradeNumber={} " , order . getId ( ) , outTradeNumb er) ;
int repairCount = repairOrderDeviceDeadTime ( ord er) ;
log . info ( " [支付确认] 订单已成功: orderId={}, outTradeNumber={}, repairDeviceCount={} " ,
order . getId ( ) , outTradeNumber , repairCount ) ;
return true ;
}
@@ -616,41 +756,83 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
@Override
@Transactional ( rollbackFor = Exception . class )
public int repairPaidOrderDeviceDeadTime ( ) {
Date now = new Date ( ) ;
Date startTime = new Date ( now . getTime ( ) - 30L * 24 * 60 * 60 * 1000 ) ; // 30天内
Map < Long , PayOrder > paidOrderMap = new LinkedHashMap < > ( ) ;
scanRecentPaidOrdersForRepair ( ) . forEach ( order - > paidOrderMap . put ( order . getId ( ) , order ) ) ;
scanHistoryPaidOrdersForRepair ( ) . forEach ( order - > paidOrderMap . putIfAbsent ( order . getId ( ) , order ) ) ;
List < PayOrder > paidOrders = payOrderMapper . selectList (
new LambdaQueryWrapper < PayOrder > ( )
. eq ( PayOrder : : getOrderStatus , PayOrderStatus . SUCCESS )
. ge ( PayOrder : : getCreateTime , startTime )
) ;
if ( paidOrders = = null | | paidOrders . isEmpty ( ) ) {
if ( paidOrderMap . isEmpty ( ) ) {
return 0 ;
}
int repairCount = 0 ;
for ( PayOrder order : paidOrders ) {
List < PayDevice > payDevices = queryPayDevicesByOrderId ( order . getId ( ) ) ;
if ( payDevices = = null | | payDevices . isEmpty ( ) ) {
log . warn ( " [支付有效期修复] 支付成功订单没有设备充值记录: orderId={} " , order . getId ( ) ) ;
continue ;
}
for ( PayOrder order : paidOrderMap . values ( ) ) {
repairCount + = repairOrderDeviceDeadTime ( order ) ;
}
for ( PayDevice payDevice : payDevices ) {
if ( repairCount > 0 ) {
log . warn ( " [支付有效期修复] 本次修复 {} 台设备 " , repairCount ) ;
} else {
log . info ( " [支付有效期修复] 本次扫描 {} 笔支付成功订单,无需修复 " , paidOrderMap . size ( ) ) ;
}
return repairCount ;
}
private List < PayOrder > scanRecentPaidOrdersForRepair ( ) {
Page < PayOrder > page = payOrderMapper . selectPage (
new Page < > ( 1 , RECENT_SCAN_SIZE , false ) ,
buildPaidOrderRepairQuery ( )
. orderByDesc ( PayOrder : : getCreateTime )
. orderByDesc ( PayOrder : : getId )
) ;
return page . getRecords ( ) ! = null ? page . getRecords ( ) : Collections . emptyList ( ) ;
}
private List < PayOrder > scanHistoryPaidOrdersForRepair ( ) {
long cursorId = REPAIR_HISTORY_CURSOR_ID . get ( ) ;
LambdaQueryWrapper < PayOrder > queryWrapper = buildPaidOrderRepairQuery ( ) ;
if ( cursorId > 0 ) {
queryWrapper . gt ( PayOrder : : getId , cursorId ) ;
}
Page < PayOrder > page = payOrderMapper . selectPage (
new Page < > ( 1 , HISTORY_SCAN_SIZE , false ) ,
queryWrapper . orderByAsc ( PayOrder : : getId )
) ;
List < PayOrder > records = page . getRecords ( ) ;
if ( records = = null | | records . isEmpty ( ) ) {
if ( cursorId > 0 ) {
REPAIR_HISTORY_CURSOR_ID . set ( 0L ) ;
log . info ( " [支付有效期修复] 历史扫描游标已到末尾,下次从最早成功订单重新扫描 " ) ;
}
return Collections . emptyList ( ) ;
}
REPAIR_HISTORY_CURSOR_ID . set ( records . get ( records . size ( ) - 1 ) . getId ( ) ) ;
return records ;
}
private LambdaQueryWrapper < PayOrder > buildPaidOrderRepairQuery ( ) {
return new LambdaQueryWrapper < PayOrder > ( )
. eq ( PayOrder : : getOrderStatus , PayOrderStatus . SUCCESS ) ;
}
private int repairOrderDeviceDeadTime ( PayOrder order ) {
List < PayDevice > payDevices = queryPayDevicesByOrderId ( order . getId ( ) ) ;
if ( payDevices = = null | | payDevices . isEmpty ( ) ) {
log . warn ( " [支付有效期修复] 支付成功订单没有设备充值记录: orderId={} " , order . getId ( ) ) ;
return 0 ;
}
int repairCount = 0 ;
for ( PayDevice payDevice : payDevices ) {
try {
if ( payDevice . getDeadTime ( ) = = null ) {
log . warn ( " [支付有效期修复] 设备充值记录没有目标到期时间: payDeviceId={}, orderId={} " ,
payDevice . getId ( ) , order . getId ( ) ) ;
continue ;
}
Device device = TenantHelper . ignore ( ( ) - >
deviceMapper . selectOne (
new LambdaQueryWrapper < Device > ( )
. eq ( Device : : getSerialNum , payDevice . getSerialNum ( ) )
)
) ;
Device device = queryDeviceByPayDevice ( payDevice ) ;
if ( device = = null ) {
log . warn ( " [支付有效期修复] 设备不存在: orderId={}, serialNum={} " ,
order . getId ( ) , payDevice . getSerialNum ( ) ) ;
@@ -678,12 +860,11 @@ public class PayOrderBusinessServiceImpl implements PayOrderBusinessService {
log . warn ( " [支付有效期修复] 已修复设备有效期: orderId={}, deviceId={}, serialNum={}, oldDeadTime={}, targetDeadTime={} " ,
order . getId ( ) , device . getId ( ) , device . getSerialNum ( ) , device . getDeadTime ( ) , payDevice . getDeadTime ( ) ) ;
}
} catch ( Exception e ) {
log . error ( " [支付有效期修复] 修复单台设备异常: orderId={}, payDeviceId={}, serialNum={}, error={} " ,
order . getId ( ) , payDevice . getId ( ) , payDevice . getSerialNum ( ) , e . getMessage ( ) , e ) ;
}
}
if ( repairCount > 0 ) {
log . warn ( " [支付有效期修复] 本次修复 {} 台设备 " , repairCount ) ;
}
return repairCount ;
}
}