This commit is contained in:
jsowell
2026-05-27 18:31:34 +08:00
parent 8168b3686a
commit 429bc73c3c
4 changed files with 278 additions and 12 deletions

View File

@@ -756,7 +756,7 @@
GREATEST(0, IFNULL(due_refund_amount, 0) - IFNULL(refund_amount, 0)) AS refundPayAmount
FROM adapay_unsplit_record
WHERE
(IFNULL(pay_amount, 0) - IFNULL(due_refund_amount, 0) > IFNULL(confirmed_split_amount, 0))
OR (IFNULL(due_refund_amount, 0) > IFNULL(refund_amount, 0))
(refund_flag IS NULL OR refund_flag != 'SUCCESS')
OR (split_flag IS NULL OR split_flag != 'SUCCESS')
</select>
</mapper>

View File

@@ -12,5 +12,9 @@ public interface AdapayUnsplitRecordHandleService {
int completeAdapayUnsplitRecordFields(String startTime, String endTime);
int refreshAdapayUnsplitRecordHandleFlag(String paymentId, String wechatAppId);
int refreshAdapayUnsplitRecordHandleFlag(String startTime, String endTime, String wechatAppId, Integer pageSize);
void processUnSettledOrder();
}

View File

@@ -58,6 +58,9 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
private static final int IMPORT_BATCH_SIZE = 500;
private static final int REFUND_WAIT_MAX_ATTEMPTS = 12;
private static final long REFUND_WAIT_INTERVAL_MILLIS = 5000L;
private static final String HANDLE_FLAG_SUCCESS = "SUCCESS";
private static final String HANDLE_FLAG_PROCESSING = "PROCESSING";
private static final String HANDLE_FLAG_FAILED = "FAILED";
private final Logger log = LoggerFactory.getLogger(AdapayUnsplitRecordHandleServiceImpl.class);
@@ -104,6 +107,7 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
total++;
String paymentId = item.getPaymentId();
String orderCode = item.getOrderCode();
// queryList 中 refundAmount 映射的是 due_refund_amount即订单应退款金额。
BigDecimal dueRefundAmount = parseAmount(item.getRefundAmount());
BigDecimal waitSplitAmount = parseAmount(item.getWaitSplitAmount());
@@ -112,18 +116,22 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
continue;
}
// 有应退款金额时,必须先确认退款已足额成功;未退足会先发起差额退款并等待,未足额则本轮不分账。
if (dueRefundAmount.compareTo(BigDecimal.ZERO) > 0 && !ensureRefundBeforeSplit(item, wechatAppId)) {
refreshHandleFlagQuietly(paymentId, wechatAppId);
skipped++;
continue;
}
if (waitSplitAmount.compareTo(BigDecimal.ZERO) <= 0) {
refreshHandleFlagQuietly(paymentId, wechatAppId);
skipped++;
continue;
}
BigDecimal confirmAmt = getLatestConfirmAmount(waitSplitAmount, item.getPayAmount(), item.getRefundAmount(), paymentId, wechatAppId);
if (confirmAmt.compareTo(BigDecimal.ZERO) <= 0) {
refreshHandleFlagQuietly(paymentId, wechatAppId);
skipped++;
continue;
}
@@ -147,14 +155,15 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
failed++;
log.error("处理未分账数据到默认账户异常, paymentId:{}, orderCode:{}, confirmAmt:{}",
paymentId, orderCode, confirmAmt, e);
markSplitResult(paymentId, "FAILED");
markSplitResult(paymentId, HANDLE_FLAG_FAILED);
refreshHandleFlagQuietly(paymentId, wechatAppId);
continue;
}
if (response != null && response.isSuccess()) {
success++;
updateConfirmedSplitAmount(item, confirmAmt, paymentId);
markSplitResult(paymentId, "SUCCESS");
refreshHandleFlagQuietly(paymentId, wechatAppId);
log.info("处理未分账数据成功, paymentId:{}, orderCode:{}, confirmAmt:{}, response:{}",
paymentId, orderCode, confirmAmt, JSON.toJSONString(response));
} else {
@@ -163,7 +172,8 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
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");
markSplitResult(paymentId, HANDLE_FLAG_FAILED);
refreshHandleFlagQuietly(paymentId, wechatAppId);
}
}
@@ -217,6 +227,44 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
return updatedCount;
}
@Override
public int refreshAdapayUnsplitRecordHandleFlag(String paymentId, String wechatAppId) {
if (StringUtils.isBlank(paymentId)) {
return 0;
}
List<AdapayUnsplitRecord> list = adapayUnsplitRecordService.selectByPaymentIds(Lists.newArrayList(paymentId));
if (CollectionUtils.isEmpty(list)) {
log.warn("刷新未分账处理标识失败,记录不存在, paymentId:{}", paymentId);
return 0;
}
return refreshUnsplitRecordHandleFlag(list, StringUtils.isBlank(wechatAppId) ? Constants.DEFAULT_APP_ID : wechatAppId);
}
@Override
public int refreshAdapayUnsplitRecordHandleFlag(String startTime, String endTime, String wechatAppId, Integer pageSize) {
int size = pageSize == null || pageSize <= 0 ? 1000 : pageSize;
int pageNum = 1;
int updatedCount = 0;
String appId = StringUtils.isBlank(wechatAppId) ? Constants.DEFAULT_APP_ID : wechatAppId;
while (true) {
PageUtils.startPage(pageNum, size);
List<AdapayUnsplitRecord> list = adapayUnsplitRecordService.queryUnsplitOrders(startTime, endTime);
if (CollectionUtils.isEmpty(list)) {
break;
}
updatedCount += refreshUnsplitRecordHandleFlag(list, appId);
if (list.size() < size) {
break;
}
pageNum++;
}
log.info("刷新未分账处理标识完成, startTime:{}, endTime:{}, 更新:{}条", startTime, endTime, updatedCount);
return updatedCount;
}
/**
* V1方法获取退款金额与结算金额
*/
@@ -481,9 +529,177 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
return updatedCount;
}
private int refreshUnsplitRecordHandleFlag(List<AdapayUnsplitRecord> list, String wechatAppId) {
if (CollectionUtils.isEmpty(list)) {
return 0;
}
Set<String> orderCodeSet = new HashSet<>();
for (AdapayUnsplitRecord record : list) {
String orderCode = record.getOrderCode();
if (StringUtils.isBlank(orderCode)) {
orderCode = extractOrderCode(record.getOrderNo());
}
if (StringUtils.isNotBlank(orderCode)) {
orderCodeSet.add(orderCode);
}
}
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) {
if (record == null || StringUtils.isBlank(record.getPaymentId())) {
continue;
}
boolean needUpdate = false;
String paymentId = record.getPaymentId();
String orderCode = record.getOrderCode();
if (StringUtils.isBlank(orderCode)) {
orderCode = extractOrderCode(record.getOrderNo());
if (StringUtils.isNotBlank(orderCode)) {
record.setOrderCode(orderCode);
needUpdate = true;
}
}
OrderBasicInfo orderBasicInfo = StringUtils.isBlank(orderCode) ? null : orderMap.get(orderCode);
if (orderBasicInfo != null) {
if (!isSameAmount(record.getDueRefundAmount(), orderBasicInfo.getRefundAmount())) {
record.setDueRefundAmount(defaultAmount(orderBasicInfo.getRefundAmount()));
needUpdate = true;
}
if (!isSameAmount(record.getSettleAmount(), orderBasicInfo.getSettleAmount())) {
record.setSettleAmount(orderBasicInfo.getSettleAmount());
needUpdate = true;
}
String pileType = YouDianUtils.isEBikePileSn(orderBasicInfo.getPileSn()) ? "eBike" : "EV";
if (!StringUtils.equals(record.getPileType(), pileType)) {
record.setPileType(pileType);
needUpdate = true;
}
}
BigDecimal dueRefundAmount = defaultAmount(record.getDueRefundAmount());
RefundAmountCheck refundAmountCheck = checkRefundAmount(orderBasicInfo, dueRefundAmount);
if (!isSameAmount(record.getRefundAmount(), refundAmountCheck.refundedAmount)) {
record.setRefundAmount(refundAmountCheck.refundedAmount);
needUpdate = true;
}
String refundFlag = calculateHandleFlag(dueRefundAmount, refundAmountCheck.refundedAmount, refundAmountCheck.acceptedRefundAmount);
if (!StringUtils.equals(record.getRefundFlag(), refundFlag)) {
record.setRefundFlag(refundFlag);
needUpdate = true;
}
BigDecimal expectedSplitAmount = defaultAmount(record.getPayAmount()).subtract(dueRefundAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
if (expectedSplitAmount.compareTo(BigDecimal.ZERO) < 0) {
expectedSplitAmount = BigDecimal.ZERO;
}
SplitAmountCheck splitAmountCheck = checkSplitAmount(paymentId, wechatAppId);
BigDecimal confirmedSplitAmount = defaultAmount(record.getConfirmedSplitAmount()).max(splitAmountCheck.confirmedSplitAmount);
if (!isSameAmount(record.getConfirmedSplitAmount(), confirmedSplitAmount)) {
record.setConfirmedSplitAmount(confirmedSplitAmount);
needUpdate = true;
}
String splitFlag = calculateHandleFlag(expectedSplitAmount, confirmedSplitAmount, confirmedSplitAmount.add(splitAmountCheck.reservedSplitAmount));
if (!StringUtils.equals(record.getSplitFlag(), splitFlag)) {
record.setSplitFlag(splitFlag);
needUpdate = true;
}
if (needUpdate) {
record.setUpdateTime(now);
updateList.add(record);
}
log.info("刷新未分账处理标识, paymentId:{}, orderCode:{}, dueRefundAmount:{}, refundedAmount:{}, expectedSplitAmount:{}, confirmedSplitAmount:{}, refundFlag:{}, splitFlag:{}",
paymentId, orderCode, dueRefundAmount, refundAmountCheck.refundedAmount, expectedSplitAmount, confirmedSplitAmount, refundFlag, splitFlag);
}
if (CollectionUtils.isNotEmpty(updateList)) {
adapayUnsplitRecordService.updateBatchSelective(updateList);
return updateList.size();
}
return 0;
}
private void refreshHandleFlagQuietly(String paymentId, String wechatAppId) {
try {
refreshAdapayUnsplitRecordHandleFlag(paymentId, wechatAppId);
} catch (Exception e) {
log.warn("刷新未分账处理标识异常,本轮继续处理后续记录, paymentId:{}", paymentId, e);
}
}
private RefundAmountCheck checkRefundAmount(OrderBasicInfo orderBasicInfo, BigDecimal dueRefundAmount) {
if (dueRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
return new RefundAmountCheck(BigDecimal.ZERO, BigDecimal.ZERO);
}
if (orderBasicInfo == null) {
return new RefundAmountCheck(BigDecimal.ZERO, BigDecimal.ZERO);
}
BigDecimal refundedAmount = getRefundedAmount(orderBasicInfo, false);
BigDecimal acceptedRefundAmount = getRefundedAmount(orderBasicInfo, true);
return new RefundAmountCheck(refundedAmount, acceptedRefundAmount);
}
private SplitAmountCheck checkSplitAmount(String paymentId, String wechatAppId) {
try {
QueryPaymentConfirmDTO dto = new QueryPaymentConfirmDTO();
dto.setWechatAppId(wechatAppId);
dto.setPaymentId(paymentId);
QueryPaymentConfirmDetailResponse response = adapayService.queryPaymentConfirmList(dto);
if (response == null || CollectionUtils.isEmpty(response.getPaymentConfirms())) {
return new SplitAmountCheck(BigDecimal.ZERO, BigDecimal.ZERO);
}
BigDecimal maxConfirmedAmount = BigDecimal.ZERO;
BigDecimal maxReservedAmount = BigDecimal.ZERO;
for (PaymentConfirmInfo confirm : response.getPaymentConfirms()) {
if (confirm == null) {
continue;
}
BigDecimal confirmedAmount = parseAmount(confirm.getConfirmedAmt());
BigDecimal reservedAmount = parseAmount(confirm.getReservedAmt());
if (confirmedAmount.compareTo(maxConfirmedAmount) > 0) {
maxConfirmedAmount = confirmedAmount;
}
if (reservedAmount.compareTo(maxReservedAmount) > 0) {
maxReservedAmount = reservedAmount;
}
}
return new SplitAmountCheck(maxConfirmedAmount.setScale(2, BigDecimal.ROUND_HALF_UP),
maxReservedAmount.setScale(2, BigDecimal.ROUND_HALF_UP));
} catch (Exception e) {
log.warn("刷新未分账处理标识时查询分账金额失败, paymentId:{}", paymentId, e);
return new SplitAmountCheck(BigDecimal.ZERO, BigDecimal.ZERO);
}
}
private String calculateHandleFlag(BigDecimal expectedAmount, BigDecimal successAmount, BigDecimal acceptedAmount) {
BigDecimal expected = defaultAmount(expectedAmount);
BigDecimal success = defaultAmount(successAmount);
BigDecimal accepted = defaultAmount(acceptedAmount);
if (expected.compareTo(BigDecimal.ZERO) <= 0 || success.compareTo(expected) >= 0) {
return HANDLE_FLAG_SUCCESS;
}
if (accepted.compareTo(expected) >= 0) {
return HANDLE_FLAG_PROCESSING;
}
return HANDLE_FLAG_FAILED;
}
private boolean ensureRefundBeforeSplit(AdapayUnsplitRecordVO item, String wechatAppId) {
String orderCode = item.getOrderCode();
String paymentId = item.getPaymentId();
// VO 中 refundAmount 来源于 adapay_unsplit_record.due_refund_amount表示分账前必须完成的应退款金额。
BigDecimal dueRefundAmount = parseAmount(item.getRefundAmount());
if (dueRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
return true;
@@ -492,23 +708,26 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
if (orderBasicInfo == null) {
log.warn("未分账数据退款前置校验失败,订单不存在, paymentId:{}, orderCode:{}", paymentId, orderCode);
markRefundResult(paymentId, "FAILED");
markRefundResult(paymentId, HANDLE_FLAG_FAILED);
return false;
}
// 先只统计成功退款金额;成功退款已足额时才允许继续后续分账。
BigDecimal refundedAmount = getRefundedAmount(orderBasicInfo, false);
updateRefundAmount(paymentId, refundedAmount);
if (refundedAmount.compareTo(dueRefundAmount) >= 0) {
markRefundResult(paymentId, "SUCCESS");
markRefundResult(paymentId, HANDLE_FLAG_SUCCESS);
return true;
}
// 再统计已受理退款金额(成功 + 处理中),避免处理中退款未回调时重复发起退款。
BigDecimal acceptedRefundAmount = getRefundedAmount(orderBasicInfo, true);
if (acceptedRefundAmount.compareTo(dueRefundAmount) >= 0) {
markRefundResult(paymentId, "PROCESSING");
markRefundResult(paymentId, HANDLE_FLAG_PROCESSING);
return waitRefundFullySucceeded(orderBasicInfo, paymentId, dueRefundAmount);
}
// 已受理退款仍不足时,只补发差额退款;补发后等待成功退款金额达到应退款金额。
BigDecimal refundAmount = dueRefundAmount.subtract(acceptedRefundAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
try {
ApplyRefundDTO dto = new ApplyRefundDTO();
@@ -518,12 +737,12 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
dto.setWechatAppId(wechatAppId);
dto.setMemberId(orderBasicInfo.getMemberId());
orderBasicInfoService.refundOrderWithAdapay(dto);
markRefundResult(paymentId, "PROCESSING");
markRefundResult(paymentId, HANDLE_FLAG_PROCESSING);
log.info("未分账数据先执行退款, paymentId:{}, orderCode:{}, dueRefundAmount:{}, refundedAmount:{}, refundAmount:{}",
paymentId, orderCode, dueRefundAmount, acceptedRefundAmount, refundAmount);
return waitRefundFullySucceeded(orderBasicInfo, paymentId, dueRefundAmount);
} catch (Exception e) {
markRefundResult(paymentId, "FAILED");
markRefundResult(paymentId, HANDLE_FLAG_FAILED);
log.error("未分账数据执行退款失败, paymentId:{}, orderCode:{}, dueRefundAmount:{}, refundedAmount:{}, refundAmount:{}",
paymentId, orderCode, dueRefundAmount, acceptedRefundAmount, refundAmount, e);
}
@@ -540,10 +759,11 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
return false;
}
// 这里仍然只认成功退款金额,处理中退款不满足“退款足额后再分账”的条件。
BigDecimal refundedAmount = getRefundedAmount(orderBasicInfo, false);
updateRefundAmount(paymentId, refundedAmount);
if (refundedAmount.compareTo(dueRefundAmount) >= 0) {
markRefundResult(paymentId, "SUCCESS");
markRefundResult(paymentId, HANDLE_FLAG_SUCCESS);
log.info("未分账数据退款已足额,继续分账, paymentId:{}, orderCode:{}, dueRefundAmount:{}, refundedAmount:{}",
paymentId, orderBasicInfo.getOrderCode(), dueRefundAmount, refundedAmount);
return true;
@@ -552,7 +772,7 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
paymentId, orderBasicInfo.getOrderCode(), i, REFUND_WAIT_MAX_ATTEMPTS, dueRefundAmount, refundedAmount);
}
markRefundResult(paymentId, "PROCESSING");
markRefundResult(paymentId, HANDLE_FLAG_PROCESSING);
log.warn("等待退款足额超时,本轮不分账, paymentId:{}, orderCode:{}, dueRefundAmount:{}",
paymentId, orderBasicInfo.getOrderCode(), dueRefundAmount);
return false;
@@ -569,6 +789,7 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
if (refundInfo == null) {
continue;
}
// includeProcessing=false只累计成功退款includeProcessing=true成功 + 处理中,用于判断是否还需要补发差额退款。
String status = refundInfo.getStatus();
if (StringUtils.isNotBlank(status)
&& !StringUtils.equals(status, AdapayStatusEnum.SUCCEEDED.getValue())
@@ -786,6 +1007,10 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
}
}
private BigDecimal defaultAmount(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value.setScale(2, BigDecimal.ROUND_HALF_UP);
}
private boolean isRowEmpty(Row row) {
if (row == null) {
return true;
@@ -839,4 +1064,24 @@ public class AdapayUnsplitRecordHandleServiceImpl implements AdapayUnsplitRecord
+ "}";
}
}
private static class RefundAmountCheck {
private final BigDecimal refundedAmount;
private final BigDecimal acceptedRefundAmount;
private RefundAmountCheck(BigDecimal refundedAmount, BigDecimal acceptedRefundAmount) {
this.refundedAmount = refundedAmount == null ? BigDecimal.ZERO : refundedAmount;
this.acceptedRefundAmount = acceptedRefundAmount == null ? BigDecimal.ZERO : acceptedRefundAmount;
}
}
private static class SplitAmountCheck {
private final BigDecimal confirmedSplitAmount;
private final BigDecimal reservedSplitAmount;
private SplitAmountCheck(BigDecimal confirmedSplitAmount, BigDecimal reservedSplitAmount) {
this.confirmedSplitAmount = confirmedSplitAmount == null ? BigDecimal.ZERO : confirmedSplitAmount;
this.reservedSplitAmount = reservedSplitAmount == null ? BigDecimal.ZERO : reservedSplitAmount;
}
}
}

View File

@@ -528,6 +528,23 @@ public class JsowellTask {
adapayUnsplitRecordHandleService.completeAdapayUnsplitRecordFields(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);
}
/**
* 从Excel导入adapay_unsplit_record并补齐缺失字段
* 流程: