@@ -1,8 +1,16 @@
package com.jsowell.quartz.task ;
import com.alibaba.fastjson2.JSON ;
import com.google.common.collect.Lists ;
import com.jsowell.adapay.common.DivMember ;
import com.jsowell.adapay.common.PaymentConfirmInfo ;
import com.jsowell.adapay.dto.PaymentConfirmParam ;
import com.jsowell.adapay.dto.QueryPaymentConfirmDTO ;
import com.jsowell.adapay.dto.WithdrawDTO ;
import com.jsowell.adapay.response.PaymentConfirmResponse ;
import com.jsowell.adapay.response.QueryPaymentConfirmDetailResponse ;
import com.jsowell.adapay.service.AdapayService ;
import com.jsowell.common.YouDianUtils ;
import com.jsowell.common.constant.CacheConstants ;
import com.jsowell.common.constant.Constants ;
import com.jsowell.common.core.redis.RedisCache ;
@@ -10,16 +18,21 @@ import com.jsowell.common.enums.SoftwareProtocolEnum;
import com.jsowell.common.enums.thirdparty.ThirdPlatformTypeEnum ;
import com.jsowell.common.enums.ykc.PileChannelEntity ;
import com.jsowell.common.util.DateUtils ;
import com.jsowell.common.util.PageUtils ;
import com.jsowell.common.util.StringUtils ;
import com.jsowell.common.util.spring.SpringUtils ;
import com.jsowell.pile.domain.* ;
import com.jsowell.pile.domain.ykcCommond.ProofreadTimeCommand ;
import com.jsowell.pile.domain.AdapayUnsplitRecord ;
import com.jsowell.pile.domain.OrderBasicInfo ;
import com.jsowell.pile.domain.PileBasicInfo ;
import com.jsowell.pile.domain.PileMerchantInfo ;
import com.jsowell.pile.domain.PileStationInfo ;
import com.jsowell.pile.domain.ykcCommond.PublishPileBillingTemplateCommand ;
import com.jsowell.pile.domain.ykcCommond.ProofreadTimeCommand ;
import com.jsowell.pile.domain.ykcCommond.StartChargingCommand ;
import com.jsowell.pile.service.* ;
import com.jsowell.pile.vo.AdapayUnsplitRecordVO ;
import com.jsowell.pile.vo.base.StationInfoVO ;
import com.jsowell.pile.vo.web.BillingTemplateVO ;
import com.jsowell.quartz.service.AdapayUnsplitRecordHandleService ;
import com.jsowell.thirdparty.amap.service.AMapService ;
import com.jsowell.thirdparty.common.NotificationDTO ;
import com.jsowell.thirdparty.common.NotificationService ;
@@ -30,7 +43,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.stereotype.Component ;
import java.io.InputStream ;
import java.math.BigDecimal ;
import java.nio.file.Files ;
import java.nio.file.Path ;
import java.nio.file.Paths ;
import java.time.LocalDate ;
import java.time.LocalDateTime ;
import java.util.* ;
@@ -81,7 +98,7 @@ public class JsowellTask {
// private OrderUnsplitRecordService orderUnsplitRecordService;
@Autowired
private AdapayUnsplitRecordHandle Service adapayUnsplitRecordHandle Service ;
private AdapayUnsplitRecordService adapayUnsplitRecordService ;
private static final long YKC_DAILY_TIMECHECK_INTERVAL_MILLIS = 200L ;
@@ -484,7 +501,11 @@ public class JsowellTask {
* jsowellTask.processUnSettledOrder()
*/
public void processUnSettledOrder ( ) {
adapayUnsplitRecordHandleService . processUnSettledOrder( ) ;
// processUnSettledOrderOld(); // 旧方法
for ( int i = 0 ; i < 35 ; i + + ) {
int batchNum = i + 1 ;
processUnSettledOrderV2 ( batchNum ) ; // 新方法
}
}
/**
@@ -493,7 +514,7 @@ public class JsowellTask {
* jsowellTask.processUnsplitRecordToDefaultMember()
*/
public void processUnsplitRecordToDefaultMember ( ) {
adapayUnsplitRecordHandleService . processUnsplitRecordToDefaultMember( ) ;
processUnsplitRecordToDefaultMember ( Constants . DEFAULT_APP_ID , 500 ) ;
}
/**
@@ -501,7 +522,85 @@ public class JsowellTask {
* jsowellTask.processUnsplitRecordToDefaultMember(wechatAppId, pageSize)
*/
public void processUnsplitRecordToDefaultMember ( String wechatAppId , Integer pageSize ) {
adapayUnsplitRecordHandleService . processUnsplitRecordToDefaultMember ( wechatAppId , pageSize ) ;
int size = pageSize = = null | | pageSize < = 0 ? 500 : pageSize ;
int pageNum = 1 ;
int total = 0 ;
int success = 0 ;
int skipped = 0 ;
int failed = 0 ;
while ( true ) {
PageUtils . startPage ( pageNum , size ) ;
List < AdapayUnsplitRecordVO > list = adapayUnsplitRecordService . queryList ( ) ;
if ( CollectionUtils . isEmpty ( list ) ) {
break ;
}
log . info ( " 处理未分账数据到默认账户, pageNum:{}, pageSize:{}, 当前页:{}条 " , pageNum , size , list . size ( ) ) ;
for ( AdapayUnsplitRecordVO item : list ) {
total + + ;
String paymentId = item . getPaymentId ( ) ;
String orderCode = item . getOrderCode ( ) ;
BigDecimal waitSplitAmount = parseAmount ( item . getWaitSplitAmount ( ) ) ;
if ( StringUtils . isBlank ( paymentId ) | | waitSplitAmount . compareTo ( BigDecimal . ZERO ) < = 0 ) {
skipped + + ;
continue ;
}
BigDecimal confirmAmt = getLatestConfirmAmount ( waitSplitAmount , item . getPayAmount ( ) , paymentId , wechatAppId ) ;
if ( confirmAmt . compareTo ( BigDecimal . ZERO ) < = 0 ) {
skipped + + ;
continue ;
}
PaymentConfirmResponse response ;
try {
DivMember divMember = new DivMember ( ) ;
divMember . setMemberId ( Constants . ZERO ) ;
divMember . setAmount ( confirmAmt . setScale ( 2 , BigDecimal . ROUND_HALF_UP ) . toPlainString ( ) ) ;
divMember . setFeeFlag ( Constants . Y ) ;
PaymentConfirmParam param = PaymentConfirmParam . builder ( )
. paymentId ( paymentId )
. divMemberList ( Lists . newArrayList ( divMember ) )
. confirmAmt ( confirmAmt )
. orderCode ( orderCode )
. wechatAppId ( wechatAppId )
. build ( ) ;
response = adapayService . createPaymentConfirmRequest ( param ) ;
} catch ( Exception e ) {
failed + + ;
log . error ( " 处理未分账数据到默认账户异常, paymentId:{}, orderCode:{}, confirmAmt:{} " ,
paymentId , orderCode , confirmAmt , e ) ;
markSplitResult ( paymentId , " FAILED " ) ;
continue ;
}
if ( response ! = null & & response . isSuccess ( ) ) {
success + + ;
updateConfirmedSplitAmount ( item , confirmAmt , paymentId ) ;
markSplitResult ( paymentId , " SUCCESS " ) ;
log . info ( " 处理未分账数据成功, paymentId:{}, orderCode:{}, confirmAmt:{}, response:{} " ,
paymentId , orderCode , confirmAmt , JSON . toJSONString ( response ) ) ;
} else {
failed + + ;
String errorCode = response = = null ? " response_null " : response . getError_code ( ) ;
String errorMsg = response = = null ? " response_is_null " : response . getError_msg ( ) ;
log . error ( " 处理未分账数据失败, paymentId:{}, orderCode:{}, confirmAmt:{}, errorCode:{}, errorMsg:{} " ,
paymentId , orderCode , confirmAmt , errorCode , errorMsg ) ;
markSplitResult ( paymentId , " FAILED " ) ;
}
}
if ( list . size ( ) < size ) {
break ;
}
pageNum + + ;
}
log . info ( " 处理未分账数据到默认账户结束, total:{}, success:{}, skipped:{}, failed:{} " ,
total , success , skipped , failed ) ;
}
/**
@@ -515,7 +614,7 @@ public class JsowellTask {
* jsowellTask.importAdapayUnsplitRecordAndCompleteFields()
*/
public void importAdapayUnsplitRecordAndCompleteFields ( ) {
adapayUnsplitRecordHandleService . importAdapayUnsplitRecordAndCompleteFields( ) ;
importAdapayUnsplitRecordAndCompleteFields ( " doc/万车充小程序-未分账明细.xlsx " ) ;
}
/**
@@ -525,24 +624,8 @@ public class JsowellTask {
* 示例: jsowellTask.completeAdapayUnsplitRecordFields('2024-01-01 00:00:00', '2025-12-31 23:59:59')
*/
public void completeAdapayUnsplitRecordFields ( String startTime , String endTime ) {
adapayUnsplitRecordHandleService . completeAdapay UnsplitRecordFields( startTime , endTime ) ;
}
/**
* 按 paymentId 刷新 adapay_unsplit_record 的退款/分账处理标识
* jsowellTask.refreshAdapayUnsplitRecordHandleFlag(paymentId, wechatAppId)
*/
public void refreshAdapayUnsplitRecordHandleFlag ( String paymentId , String wechatAppId ) {
adapayUnsplitRecordHandleService . refreshAdapayUnsplitRecordHandleFlag ( paymentId , wechatAppId ) ;
}
/**
* 批量刷新 adapay_unsplit_record 的退款/分账处理标识
* jsowellTask.refreshAdapayUnsplitRecordHandleFlag(startTime, endTime, wechatAppId, pageSize)
* 示例: jsowellTask.refreshAdapayUnsplitRecordHandleFlag('2024-01-01 00:00:00', '2025-12-31 23:59:59', 'app_id', 500)
*/
public void refreshAdapayUnsplitRecordHandleFlag ( String startTime , String endTime , String wechatAppId , Integer pageSize ) {
adapayUnsplitRecordHandleService . refreshAdapayUnsplitRecordHandleFlag ( startTime , endTime , wechatAppId , pageSize ) ;
int updatedCount = completeUnsplitRecordMissing Fields( startTime , endTime , 1000 );
log . info ( " 补齐未分账数据缺失字段完成, startTime:{}, endTime:{}, 更新:{}条 " , startTime , endTime , updatedCount ) ;
}
/**
@@ -555,7 +638,565 @@ public class JsowellTask {
* jsowellTask.importAdapayUnsplitRecordAndCompleteFields(文件路径)
*/
public void importAdapayUnsplitRecordAndCompleteFields ( String filePath ) {
adapayUnsplitRecordHandleService . importAdapayUnsplitRecordAndCompleteFields ( filePath ) ;
// 相对路径转绝对路径(基于 JVM 工作目录)
Path path = Paths . get ( filePath ) ;
if ( ! path . isAbsolute ( ) ) {
path = Paths . get ( System . getProperty ( " user.dir " ) , filePath ) ;
}
if ( ! Files . exists ( path ) ) {
log . error ( " 导入未分账数据失败,文件不存在:{} " , path . toAbsolutePath ( ) ) ;
return ;
}
// 第一步: 读取Excel, 将每行数据 insertOrUpdate 到 adapay_unsplit_record 表
ImportSummary summary = importAdapayUnsplitRecord ( path ) ;
if ( summary . successRows = = 0 ) {
log . info ( " 导入未分账数据结束,未写入任何数据,统计:{} " , summary ) ;
return ;
}
// 第二步:以导入数据中最早/最晚支付时间为范围,补齐已入库记录的缺失字段
// 若 Excel 中无有效支付时间, 则使用兜底时间范围( 2024-01-01 至当前时间)
String startTime = summary . minPayTime = = null
? " 2024-01-01 00:00:00 "
: DateUtils . formatDateTime ( summary . minPayTime ) ;
String endTime = summary . maxPayTime = = null
? DateUtils . getDateTime ( )
: DateUtils . formatDateTime ( summary . maxPayTime ) ;
int updatedCount = completeUnsplitRecordMissingFields ( startTime , endTime , 1000 ) ;
log . info ( " 导入并补齐未分账数据完成, 导入统计:{}, 补齐更新:{}条 " , summary , updatedCount ) ;
}
private static final int IMPORT_BATCH_SIZE = 500 ;
private ImportSummary importAdapayUnsplitRecord ( Path filePath ) {
ImportSummary summary = new ImportSummary ( ) ;
DataFormatter formatter = new DataFormatter ( ) ;
try ( InputStream inputStream = Files . newInputStream ( filePath ) ;
Workbook workbook = WorkbookFactory . create ( inputStream ) ) {
Sheet sheet = workbook . getSheetAt ( 0 ) ;
if ( sheet = = null ) {
log . error ( " 导入未分账数据失败, Excel没有sheet, file:{} " , filePath . toAbsolutePath ( ) ) ;
return summary ;
}
Row headerRow = sheet . getRow ( sheet . getFirstRowNum ( ) ) ;
if ( headerRow = = null ) {
log . error ( " 导入未分账数据失败, Excel没有表头, file:{} " , filePath . toAbsolutePath ( ) ) ;
return summary ;
}
Map < String , Integer > headerIndexMap = buildHeaderIndexMap ( headerRow , formatter ) ;
List < String > requiredHeaders = Lists . newArrayList (
" 商户号 " , " 支付时间 " , " 交易流水号 " , " 交易订单号 " , " 交易订单金额 " , " 已确认分账金额 " , " 已撤销金额 " , " 支付确认撤销金额 " , " 剩余未分账金额 "
) ;
for ( String requiredHeader : requiredHeaders ) {
if ( ! headerIndexMap . containsKey ( normalizeHeader ( requiredHeader ) ) ) {
log . error ( " 导入未分账数据失败,缺少字段:{}, file:{} " , requiredHeader , filePath . toAbsolutePath ( ) ) ;
return summary ;
}
}
int firstDataRow = sheet . getFirstRowNum ( ) + 1 ;
int lastDataRow = sheet . getLastRowNum ( ) ;
// 批量收集记录,每 IMPORT_BATCH_SIZE 条执行一次批量 upsert, 减少数据库交互次数
List < AdapayUnsplitRecord > batch = new ArrayList < > ( IMPORT_BATCH_SIZE ) ;
for ( int rowNum = firstDataRow ; rowNum < = lastDataRow ; rowNum + + ) {
Row row = sheet . getRow ( rowNum ) ;
if ( row = = null | | isRowEmpty ( row ) ) {
continue ;
}
summary . totalRows + + ;
try {
AdapayUnsplitRecord record = convertRowToRecord ( row , headerIndexMap , formatter ) ;
if ( record = = null ) {
summary . skippedRows + + ;
continue ;
}
batch . add ( record ) ;
summary . updatePayTimeRange ( record . getPayTime ( ) ) ;
// 达到批量大小时执行一次批量写入
if ( batch . size ( ) > = IMPORT_BATCH_SIZE ) {
flushImportBatch ( batch ) ;
summary . successRows + = batch . size ( ) ;
batch . clear ( ) ;
}
} catch ( Exception e ) {
summary . failedRows + + ;
log . error ( " 导入未分账数据失败, rowNum:{}, file:{} " , rowNum + 1 , filePath . toAbsolutePath ( ) , e ) ;
}
if ( summary . totalRows % 1000 = = 0 ) {
log . info ( " 导入未分账数据进行中, total:{}, success:{}, skipped:{}, failed:{} " ,
summary . totalRows , summary . successRows , summary . skippedRows , summary . failedRows ) ;
}
}
// 处理剩余不足一批的记录
if ( ! batch . isEmpty ( ) ) {
flushImportBatch ( batch ) ;
summary . successRows + = batch . size ( ) ;
}
} catch ( Exception e ) {
log . error ( " 导入未分账数据失败, file:{} " , filePath . toAbsolutePath ( ) , e ) ;
}
return summary ;
}
/**
* 写入一批导入数据:以 paymentId 为业务唯一键,
* - 数据库已存在相同 paymentId 的记录 -> 沿用其主键 id 走 updateBatchSelective 更新
* - 数据库不存在的 -> 走 batchInsert 新增
* 同一批内若出现重复 paymentId, 仅保留最后一条( 与文件读取顺序一致)
*/
private void flushImportBatch ( List < AdapayUnsplitRecord > batch ) {
if ( CollectionUtils . isEmpty ( batch ) ) {
return ;
}
// 同批内按 paymentId 去重,保留最后一条
Map < String , AdapayUnsplitRecord > uniqueByPaymentId = new LinkedHashMap < > ( ) ;
for ( AdapayUnsplitRecord record : batch ) {
if ( StringUtils . isNotBlank ( record . getPaymentId ( ) ) ) {
uniqueByPaymentId . put ( record . getPaymentId ( ) , record ) ;
}
}
if ( uniqueByPaymentId . isEmpty ( ) ) {
return ;
}
List < String > paymentIds = new ArrayList < > ( uniqueByPaymentId . keySet ( ) ) ;
List < AdapayUnsplitRecord > existingList = adapayUnsplitRecordService . selectByPaymentIds ( paymentIds ) ;
Map < String , Integer > existingIdMap = new HashMap < > ( ) ;
if ( CollectionUtils . isNotEmpty ( existingList ) ) {
for ( AdapayUnsplitRecord existing : existingList ) {
existingIdMap . put ( existing . getPaymentId ( ) , existing . getId ( ) ) ;
}
}
List < AdapayUnsplitRecord > insertList = new ArrayList < > ( ) ;
List < AdapayUnsplitRecord > updateList = new ArrayList < > ( ) ;
for ( AdapayUnsplitRecord record : uniqueByPaymentId . values ( ) ) {
Integer existingId = existingIdMap . get ( record . getPaymentId ( ) ) ;
if ( existingId ! = null ) {
record . setId ( existingId ) ;
updateList . add ( record ) ;
} else {
insertList . add ( record ) ;
}
}
if ( ! insertList . isEmpty ( ) ) {
adapayUnsplitRecordService . batchInsert ( insertList ) ;
}
if ( ! updateList . isEmpty ( ) ) {
adapayUnsplitRecordService . updateBatchSelective ( updateList ) ;
}
}
private int completeUnsplitRecordMissingFields ( String startTime , String endTime , int pageSize ) {
int pageNum = 1 ;
int updatedCount = 0 ;
while ( true ) {
PageUtils . startPage ( pageNum , pageSize ) ;
List < AdapayUnsplitRecord > list = adapayUnsplitRecordService . queryUnsplitOrders ( startTime , endTime ) ;
if ( CollectionUtils . isEmpty ( list ) ) {
break ;
}
Set < String > orderCodeSet = new HashSet < > ( ) ;
for ( AdapayUnsplitRecord record : list ) {
if ( StringUtils . isBlank ( record . getOrderCode ( ) ) ) {
String extractedOrderCode = extractOrderCode ( record . getOrderNo ( ) ) ;
if ( StringUtils . isNotBlank ( extractedOrderCode ) ) {
record . setOrderCode ( extractedOrderCode ) ;
}
}
if ( StringUtils . isNotBlank ( record . getOrderCode ( ) ) ) {
orderCodeSet . add ( record . getOrderCode ( ) ) ;
}
}
Map < String , OrderBasicInfo > orderMap = new HashMap < > ( ) ;
if ( CollectionUtils . isNotEmpty ( orderCodeSet ) ) {
List < OrderBasicInfo > orderList = orderBasicInfoService . selectOrderTemp ( orderCodeSet ) ;
orderMap = orderList . stream ( )
. collect ( Collectors . toMap ( OrderBasicInfo : : getOrderCode , v - > v , ( k1 , k2 ) - > k1 ) ) ;
}
List < AdapayUnsplitRecord > updateList = new ArrayList < > ( ) ;
Date now = DateUtils . getNowDate ( ) ;
for ( AdapayUnsplitRecord record : list ) {
boolean needUpdate = false ;
String orderCode = record . getOrderCode ( ) ;
if ( StringUtils . isBlank ( orderCode ) ) {
orderCode = extractOrderCode ( record . getOrderNo ( ) ) ;
if ( StringUtils . isNotBlank ( orderCode ) ) {
record . setOrderCode ( orderCode ) ;
needUpdate = true ;
}
}
if ( StringUtils . isNotBlank ( orderCode ) ) {
OrderBasicInfo orderBasicInfo = orderMap . get ( orderCode ) ;
if ( orderBasicInfo ! = null ) {
BigDecimal refundAmount = orderBasicInfo . getRefundAmount ( ) ;
if ( ! isSameAmount ( record . getDueRefundAmount ( ) , refundAmount ) ) {
record . setDueRefundAmount ( refundAmount ) ;
needUpdate = true ;
}
BigDecimal settleAmount = orderBasicInfo . getSettleAmount ( ) ;
if ( ! isSameAmount ( record . getSettleAmount ( ) , settleAmount ) ) {
record . setSettleAmount ( settleAmount ) ;
needUpdate = true ;
}
String pileType = YouDianUtils . isEBikePileSn ( orderBasicInfo . getPileSn ( ) ) ? " eBike " : " EV " ;
if ( ! StringUtils . equals ( record . getPileType ( ) , pileType ) ) {
record . setPileType ( pileType ) ;
needUpdate = true ;
}
}
}
if ( needUpdate ) {
record . setUpdateTime ( now ) ;
updateList . add ( record ) ;
}
}
if ( CollectionUtils . isNotEmpty ( updateList ) ) {
adapayUnsplitRecordService . updateBatchSelective ( updateList ) ;
updatedCount + = updateList . size ( ) ;
}
if ( list . size ( ) < pageSize ) {
break ;
}
pageNum + + ;
}
return updatedCount ;
}
private BigDecimal getLatestConfirmAmount ( BigDecimal waitSplitAmount , String payAmount , String paymentId , String wechatAppId ) {
BigDecimal confirmAmt = waitSplitAmount ;
try {
QueryPaymentConfirmDTO dto = new QueryPaymentConfirmDTO ( ) ;
dto . setWechatAppId ( wechatAppId ) ;
dto . setPaymentId ( paymentId ) ;
QueryPaymentConfirmDetailResponse response = adapayService . queryPaymentConfirmList ( dto ) ;
BigDecimal latestRemaining = calculateLatestRemainingAmount ( payAmount , response ) ;
if ( latestRemaining . compareTo ( BigDecimal . ZERO ) > 0 ) {
confirmAmt = waitSplitAmount . min ( latestRemaining ) ;
}
} catch ( Exception e ) {
log . warn ( " 查询汇付确认金额异常,使用数据库待分账金额继续处理, paymentId:{}, waitSplitAmount:{} " ,
paymentId , waitSplitAmount , e ) ;
}
return confirmAmt ;
}
private BigDecimal calculateLatestRemainingAmount ( String payAmount , QueryPaymentConfirmDetailResponse response ) {
BigDecimal payAmt = parseAmount ( payAmount ) ;
if ( payAmt . compareTo ( BigDecimal . ZERO ) < = 0 | | response = = null | | CollectionUtils . isEmpty ( response . getPaymentConfirms ( ) ) ) {
return payAmt ;
}
BigDecimal maxConfirmedAmt = BigDecimal . ZERO ;
BigDecimal maxReservedAmt = BigDecimal . ZERO ;
List < PaymentConfirmInfo > confirms = response . getPaymentConfirms ( ) ;
for ( PaymentConfirmInfo confirm : confirms ) {
BigDecimal confirmedAmt = parseAmount ( confirm . getConfirmedAmt ( ) ) ;
BigDecimal reservedAmt = parseAmount ( confirm . getReservedAmt ( ) ) ;
if ( confirmedAmt . compareTo ( maxConfirmedAmt ) > 0 ) {
maxConfirmedAmt = confirmedAmt ;
}
if ( reservedAmt . compareTo ( maxReservedAmt ) > 0 ) {
maxReservedAmt = reservedAmt ;
}
}
BigDecimal latestRemaining = payAmt . subtract ( maxConfirmedAmt ) . subtract ( maxReservedAmt ) ;
return latestRemaining . compareTo ( BigDecimal . ZERO ) > 0 ? latestRemaining : BigDecimal . ZERO ;
}
private void updateConfirmedSplitAmount ( AdapayUnsplitRecordVO item , BigDecimal confirmAmt , String paymentId ) {
BigDecimal oldConfirmedAmt = parseAmount ( item . getConfirmedSplitAmount ( ) ) ;
BigDecimal newConfirmedAmt = oldConfirmedAmt . add ( confirmAmt ) . setScale ( 2 , BigDecimal . ROUND_HALF_UP ) ;
AdapayUnsplitRecord updateRecord = new AdapayUnsplitRecord ( ) ;
updateRecord . setPaymentId ( paymentId ) ;
updateRecord . setConfirmedSplitAmount ( newConfirmedAmt ) ;
updateRecord . setUpdateTime ( DateUtils . getNowDate ( ) ) ;
adapayUnsplitRecordService . insertOrUpdateSelective ( updateRecord ) ;
}
private void markSplitResult ( String paymentId , String splitFlag ) {
AdapayUnsplitRecord updateRecord = new AdapayUnsplitRecord ( ) ;
updateRecord . setPaymentId ( paymentId ) ;
updateRecord . setSplitFlag ( splitFlag ) ;
updateRecord . setUpdateTime ( DateUtils . getNowDate ( ) ) ;
adapayUnsplitRecordService . insertOrUpdateSelective ( updateRecord ) ;
}
private AdapayUnsplitRecord convertRowToRecord ( Row row , Map < String , Integer > headerIndexMap , DataFormatter formatter ) {
String paymentId = getCellString ( row , headerIndexMap . get ( normalizeHeader ( " 交易流水号 " ) ) , formatter ) ;
if ( StringUtils . isBlank ( paymentId ) ) {
return null ;
}
String orderNo = getCellString ( row , headerIndexMap . get ( normalizeHeader ( " 交易订单号 " ) ) , formatter ) ;
Date payTime = parsePayTime ( getCell ( row , headerIndexMap . get ( normalizeHeader ( " 支付时间 " ) ) ) , formatter ) ;
AdapayUnsplitRecord record = new AdapayUnsplitRecord ( ) ;
record . setMerchantCode ( getCellString ( row , headerIndexMap . get ( normalizeHeader ( " 商户号 " ) ) , formatter ) ) ;
record . setPayTime ( payTime ) ;
record . setPaymentId ( paymentId ) ;
record . setOrderNo ( orderNo ) ;
record . setOrderCode ( extractOrderCode ( orderNo ) ) ;
record . setPayAmount ( getCellDecimal ( row , headerIndexMap . get ( normalizeHeader ( " 交易订单金额 " ) ) , formatter ) ) ;
record . setConfirmedSplitAmount ( getCellDecimal ( row , headerIndexMap . get ( normalizeHeader ( " 已确认分账金额 " ) ) , formatter ) ) ;
record . setRefundAmount ( getCellDecimal ( row , headerIndexMap . get ( normalizeHeader ( " 已撤销金额 " ) ) , formatter ) ) ;
record . setPaymentRevokeAmount ( getCellDecimal ( row , headerIndexMap . get ( normalizeHeader ( " 支付确认撤销金额 " ) ) , formatter ) ) ;
record . setRemainingSplitAmount ( getCellDecimal ( row , headerIndexMap . get ( normalizeHeader ( " 剩余未分账金额 " ) ) , formatter ) ) ;
record . setUpdateTime ( DateUtils . getNowDate ( ) ) ;
return record ;
}
private Map < String , Integer > buildHeaderIndexMap ( Row headerRow , DataFormatter formatter ) {
Map < String , Integer > headerIndexMap = new HashMap < > ( ) ;
short firstCellNum = headerRow . getFirstCellNum ( ) ;
short lastCellNum = headerRow . getLastCellNum ( ) ;
if ( firstCellNum < 0 | | lastCellNum < 0 ) {
return headerIndexMap ;
}
for ( int cellIndex = firstCellNum ; cellIndex < lastCellNum ; cellIndex + + ) {
Cell cell = headerRow . getCell ( cellIndex , Row . MissingCellPolicy . RETURN_BLANK_AS_NULL ) ;
if ( cell = = null ) {
continue ;
}
String header = normalizeHeader ( formatter . formatCellValue ( cell ) ) ;
if ( StringUtils . isNotBlank ( header ) ) {
headerIndexMap . put ( header , cellIndex ) ;
}
}
return headerIndexMap ;
}
private String getCellString ( Row row , Integer columnIndex , DataFormatter formatter ) {
Cell cell = getCell ( row , columnIndex ) ;
if ( cell = = null ) {
return null ;
}
String value = formatter . formatCellValue ( cell ) ;
return StringUtils . isBlank ( value ) ? null : value . trim ( ) ;
}
private Cell getCell ( Row row , Integer columnIndex ) {
if ( row = = null | | columnIndex = = null ) {
return null ;
}
return row . getCell ( columnIndex , Row . MissingCellPolicy . RETURN_BLANK_AS_NULL ) ;
}
private BigDecimal getCellDecimal ( Row row , Integer columnIndex , DataFormatter formatter ) {
String value = getCellString ( row , columnIndex , formatter ) ;
return parseAmount ( value ) ;
}
private Date parsePayTime ( Cell cell , DataFormatter formatter ) {
if ( cell = = null ) {
return null ;
}
if ( cell . getCellType ( ) = = CellType . NUMERIC ) {
return DateUtil . getJavaDate ( cell . getNumericCellValue ( ) ) ;
}
if ( cell . getCellType ( ) = = CellType . FORMULA & & cell . getCachedFormulaResultType ( ) = = CellType . NUMERIC ) {
return DateUtil . getJavaDate ( cell . getNumericCellValue ( ) ) ;
}
String value = formatter . formatCellValue ( cell ) ;
if ( StringUtils . isBlank ( value ) ) {
return null ;
}
Date date = DateUtils . parseDate ( value . trim ( ) ) ;
if ( date ! = null ) {
return date ;
}
try {
return DateUtil . getJavaDate ( Double . parseDouble ( value . trim ( ) ) ) ;
} catch ( Exception e ) {
log . warn ( " 解析支付时间失败, value:{} " , value ) ;
return null ;
}
}
private String extractOrderCode ( String orderNo ) {
if ( StringUtils . isBlank ( orderNo ) ) {
return null ;
}
int index = orderNo . indexOf ( " _ " ) ;
String orderCode = index > 0 ? orderNo . substring ( 0 , index ) : orderNo ;
// order_code 字段长度限制为 16, 超长则无法匹配订单, 返回 null
if ( orderCode . length ( ) > = 16 ) {
log . warn ( " order_code 字段长度超出限制, order_no:{} " , orderNo ) ;
}
return orderCode . length ( ) < = 16 ? orderCode : null ;
}
private String normalizeHeader ( String header ) {
return header = = null ? " " : header . replace ( " " , " " ) . trim ( ) ;
}
private boolean isSameAmount ( BigDecimal left , BigDecimal right ) {
if ( left = = null & & right = = null ) {
return true ;
}
if ( left = = null | | right = = null ) {
return false ;
}
return left . compareTo ( right ) = = 0 ;
}
private BigDecimal parseAmount ( String value ) {
if ( StringUtils . isBlank ( value ) ) {
return BigDecimal . ZERO ;
}
String normalizedValue = value . replace ( " , " , " " ) . trim ( ) ;
try {
return new BigDecimal ( normalizedValue ) ;
} catch ( NumberFormatException e ) {
log . warn ( " 解析数字失败, value:{} " , value ) ;
return BigDecimal . ZERO ;
}
}
private boolean isRowEmpty ( Row row ) {
if ( row = = null ) {
return true ;
}
short firstCellNum = row . getFirstCellNum ( ) ;
short lastCellNum = row . getLastCellNum ( ) ;
if ( firstCellNum < 0 | | lastCellNum < 0 ) {
return true ;
}
for ( int i = firstCellNum ; i < lastCellNum ; i + + ) {
Cell cell = row . getCell ( i , Row . MissingCellPolicy . RETURN_BLANK_AS_NULL ) ;
if ( cell = = null ) {
continue ;
}
if ( cell . getCellType ( ) ! = CellType . BLANK ) {
return false ;
}
}
return true ;
}
private static class ImportSummary {
private int totalRows ;
private int successRows ;
private int skippedRows ;
private int failedRows ;
private Date minPayTime ;
private Date maxPayTime ;
private void updatePayTimeRange ( Date payTime ) {
if ( payTime = = null ) {
return ;
}
if ( minPayTime = = null | | payTime . before ( minPayTime ) ) {
minPayTime = payTime ;
}
if ( maxPayTime = = null | | payTime . after ( maxPayTime ) ) {
maxPayTime = payTime ;
}
}
@Override
public String toString ( ) {
return " { "
+ " \" totalRows \" : " + totalRows
+ " , \" successRows \" : " + successRows
+ " , \" skippedRows \" : " + skippedRows
+ " , \" failedRows \" : " + failedRows
+ " , \" minPayTime \" : \" " + ( minPayTime = = null ? " " : DateUtils . formatDateTime ( minPayTime ) ) + " \" "
+ " , \" maxPayTime \" : \" " + ( maxPayTime = = null ? " " : DateUtils . formatDateTime ( maxPayTime ) ) + " \" "
+ " } " ;
}
}
/**
* V1方法, 获取退款金额与结算金额
* @param batchNum
*/
private void processUnSettledOrderV1 ( int batchNum ) {
String startTime = " 2025-01-01 00:00:00 " ;
String endTime = " 2025-12-31 23:59:59 " ;
// 查询未分帐订单
PageUtils . startPage ( 1 , 1000 ) ;
List < AdapayUnsplitRecord > list = adapayUnsplitRecordService . queryUnsplitOrders ( startTime , endTime ) ;
log . info ( " 第{}批次,共查询到{}条数据 " , batchNum , list . size ( ) ) ;
if ( CollectionUtils . isEmpty ( list ) ) {
return ;
}
// 转为map, key:orderCode, value:AdapayUnsplitRecord
Map < String , AdapayUnsplitRecord > map = list . stream ( ) . collect ( Collectors . toMap ( AdapayUnsplitRecord : : getOrderCode , v - > v , ( k1 , k2 ) - > k1 ) ) ;
// 取keySet
Set < String > orderCodes = map . keySet ( ) ;
// 查询订单的退款金额与结算金额
List < OrderBasicInfo > orderList = orderBasicInfoService . selectOrderTemp ( orderCodes ) ;
// 转为map, key:orderCode, value:OrderBasicInfo
Map < String , OrderBasicInfo > orderMap = orderList . stream ( ) . collect ( Collectors . toMap ( OrderBasicInfo : : getOrderCode , v - > v , ( k1 , k2 ) - > k1 ) ) ;
List < AdapayUnsplitRecord > updateList = new ArrayList < > ( ) ;
//更新map
for ( String orderCode : orderCodes ) {
OrderBasicInfo orderBasicInfo = orderMap . get ( orderCode ) ;
if ( Objects . isNull ( orderBasicInfo ) ) {
// log.error("未查询到订单:{}", orderCode);
continue ;
}
AdapayUnsplitRecord adapayUnsplitRecord = map . get ( orderCode ) ;
adapayUnsplitRecord . setDueRefundAmount ( orderBasicInfo . getRefundAmount ( ) ) ;
adapayUnsplitRecord . setSettleAmount ( orderBasicInfo . getSettleAmount ( ) ) ;
String pileSn = orderBasicInfo . getPileSn ( ) ;
adapayUnsplitRecord . setPileType ( YouDianUtils . isEBikePileSn ( pileSn ) ? " eBike " : " EV " ) ;
updateList . add ( adapayUnsplitRecord ) ;
}
adapayUnsplitRecordService . updateBatchSelective ( updateList ) ;
log . info ( " 第{}批次,共更新{}条数据 " , batchNum , updateList . size ( ) ) ;
}
/**
* 更新adapay_unsplit_record表, 去汇付查询最新的数据
* @param batchNum
*/
private void processUnSettledOrderV2 ( int batchNum ) {
String startTime = " 2025-01-01 00:00:00 " ;
String endTime = " 2025-12-31 23:59:59 " ;
// 查询未分帐订单
PageUtils . startPage ( 1 , 3 ) ;
List < AdapayUnsplitRecord > list = adapayUnsplitRecordService . queryUnsplitOrders ( startTime , endTime ) ;
// 根据paymentId去汇付查询最新数据
for ( AdapayUnsplitRecord adapayUnsplitRecord : list ) {
String paymentId = adapayUnsplitRecord . getPaymentId ( ) ;
QueryPaymentConfirmDTO dto = new QueryPaymentConfirmDTO ( ) ;
dto . setWechatAppId ( Constants . DEFAULT_APP_ID ) ;
dto . setPaymentId ( paymentId ) ;
QueryPaymentConfirmDetailResponse response = adapayService . queryPaymentConfirmList ( dto ) ;
System . out . println ( JSON . toJSONString ( response ) ) ;
}
}
public void updateOrderReview ( ) {
@@ -566,4 +1207,82 @@ public class JsowellTask {
orderBasicInfoService . updateOrderReviewFlagTemp ( start , end , null , null ) ;
}
// private void processUnSettledOrderOld() {
// String startTime = "2023-01-01 00:00:00";
// String endTime = "2024-12-31 23:59:59";
//
// // 使用redis控制请求api
// Boolean setnx = redisCache.setnx(CacheConstants.PROCESS_UNSPLIT_ORDERS, Constants.ONE, 65);
// if (!setnx) {
// return;
// }
//
// // 查询未分帐订单
// PageUtils.startPage(1, 10);
// List<OrderUnsplitRecord> list = orderUnsplitRecordService.queryUnsplitOrders(startTime, endTime);
//
// log.info("处理未分帐订单start, 当前时间:{}, 查询出[{}]条未分账订单, 下面进行处理", DateUtils.getDateTime(), list.size());
// int count = 0;
// for (OrderUnsplitRecord orderUnsplitRecord : list) {
// String paymentId = orderUnsplitRecord.getPaymentId();
// String orderCode = orderUnsplitRecord.getOrderCode();
// BigDecimal confirmAmt = orderUnsplitRecord.getSettleAmount();
//
// DivMember divMember = new DivMember();
// divMember.setMemberId(Constants.ZERO); // 若是商户本身时, 传入0
// divMember.setAmount(AdapayUtil.formatAmount(confirmAmt));
// divMember.setFeeFlag(Constants.Y);
//
// PaymentConfirmParam param = PaymentConfirmParam.builder()
// .paymentId(paymentId)
// .divMemberList(Lists.newArrayList(divMember))
// .confirmAmt(confirmAmt)
// .orderCode(orderCode)
// .wechatAppId(Constants.DEFAULT_APP_ID) // 默认使用万车充的appId
// .build();
// // 延时分账, 使用确认交易API
// PaymentConfirmResponse paymentConfirmResponse = adapayService.createPaymentConfirmRequest(param);
//
// // 如果确认交易成功,则更新订单状态为已分账
// if (paymentConfirmResponse.isSuccess()) {
// count++;
// orderUnsplitRecord.setStatus(AdapayStatusEnum.SUCCEEDED.getValue());
// orderUnsplitRecordService.updateOrderUnsplitRecord(orderUnsplitRecord);
// log.info("processUnsplitOrders, 分账成功, paymentId:{}", paymentId);
// } else {
// // error_type:api_error, error_code:confirm_amt_over_limit, error_msg当前确认金额 > 支付金额 - 已支付确认金额 - 已支付撤销金额
// if (paymentConfirmResponse.getError_code().equals("confirm_amt_over_limit")) {
// // 查询paymentId的总分账金额
// BigDecimal totalSplitAmount;
// try {
// totalSplitAmount = adapayService.getTotalSplitAmountByPaymentId(paymentId);
// } catch (BaseAdaPayException e) {
// throw new RuntimeException(e);
// }
// if (totalSplitAmount.compareTo(confirmAmt) == 0) {
// // 如果总分账金额等于当前分账金额,则更新订单状态为已分账
// orderUnsplitRecord.setStatus(AdapayStatusEnum.SUCCEEDED.getValue());
// orderUnsplitRecordService.updateOrderUnsplitRecord(orderUnsplitRecord);
// log.info("processUnsplitOrders, 分账成功, paymentId:{}", paymentId);
// } else {
// log.info("processUnsplitOrders, 分账失败, paymentId:{}, 错误信息:{}", paymentId, paymentConfirmResponse.getError_msg());
// }
// }
//
// // error_type:invalid_request_error, error_code:payment_over_time_doing, error_msg:数据正在处理中,请稍后再试
// if (paymentConfirmResponse.getError_code().equals("payment_over_time_doing")) {
// log.info("processUnsplitOrders, 分账失败, paymentId:{}, 错误信息:{}", paymentId, paymentConfirmResponse.getError_msg());
// break;
// }
// // error_type:invalid_request_error, error_code:refund_repeate_request, error_msg:请求过于频繁
// if (paymentConfirmResponse.getError_code().equals("refund_repeate_request")) {
// log.info("processUnsplitOrders, 分账失败, paymentId:{}, 错误信息:{}", paymentId, paymentConfirmResponse.getError_msg());
// break;
// }
// }
// }
// log.info("处理未分帐订单end, 当前时间:[{}], 成功分账[{}]条订单", DateUtils.getDateTime(), count);
// }
}