Merge branch 'test' of https://git.lionquexpress.com/wuweihua/BMS
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
This commit is contained in:
@@ -151,7 +151,9 @@ public interface IPrcOrderBillService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Full rebill from local prc_order_bill only (no Oracle upstream).
|
* Full rebill from local prc_order_bill only (no Oracle upstream).
|
||||||
* Rebuilds TmsPriceBillRequest from the bill row then calls {@link #bill}.
|
* Refund-like local status (order_status 9/10 or refund_status=REFUNDED):
|
||||||
|
* skip when prc_income_bill_fee already has fee_type=REFUND; otherwise {@link #refund}.
|
||||||
|
* Else rebuilds request from the bill row then {@link #bill}.
|
||||||
*/
|
*/
|
||||||
TmsPriceQuoteResult rebillLocalByBillId(String billId);
|
TmsPriceQuoteResult rebillLocalByBillId(String billId);
|
||||||
|
|
||||||
|
|||||||
+88
-6
@@ -255,11 +255,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
|||||||
}
|
}
|
||||||
failReq.setTransferNo(bill.getTransferNo());
|
failReq.setTransferNo(bill.getTransferNo());
|
||||||
assertOrderBillEditable(bill);
|
assertOrderBillEditable(bill);
|
||||||
if (BillRefundStatus.REFUNDED.getCode().equalsIgnoreCase(bill.getRefundStatus()))
|
// Duplicate guard is fee_type=REFUND on prc_income_bill_fee (refund_status alone may be stale).
|
||||||
{
|
|
||||||
throw new ServiceException("\u8ba2\u5355\u5df2\u9000\u8d39\uff0c\u4e0d\u53ef\u91cd\u590d\u9000\u8d39");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PrcIncomeBillFee> fees = incomeBillFeeMapper.selectByBillId(bill.getBillId());
|
List<PrcIncomeBillFee> fees = incomeBillFeeMapper.selectByBillId(bill.getBillId());
|
||||||
if (fees == null)
|
if (fees == null)
|
||||||
{
|
{
|
||||||
@@ -752,8 +748,11 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
|||||||
private static final long LOCAL_REBILL_DB_RETRY_BASE_MS = 80L;
|
private static final long LOCAL_REBILL_DB_RETRY_BASE_MS = 80L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local rebill without outer txn: each {@link #bill} starts its own transaction so
|
* Local rebill without outer txn: each {@link #bill}/{@link #refund} starts its own transaction so
|
||||||
* MySQL deadlock can be retried cleanly (same idea as billing-task billWithDbRetry).
|
* MySQL deadlock can be retried cleanly (same idea as billing-task billWithDbRetry).
|
||||||
|
* <p>If local order is refund-like (order_status 9/10 or refund_status=REFUNDED):
|
||||||
|
* skip when prc_income_bill_fee already has fee_type=REFUND; otherwise call {@link #refund}.
|
||||||
|
* Otherwise normal {@link #bill}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TmsPriceQuoteResult rebillLocalByBillId(String billId)
|
public TmsPriceQuoteResult rebillLocalByBillId(String billId)
|
||||||
@@ -775,10 +774,57 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
|||||||
{
|
{
|
||||||
throw new ServiceException(MessageUtils.message("price.bill.bizDateRequired"));
|
throw new ServiceException(MessageUtils.message("price.bill.bizDateRequired"));
|
||||||
}
|
}
|
||||||
|
if (isLocalRefundCandidate(bill))
|
||||||
|
{
|
||||||
|
if (hasRefundFeeLine(bill.getBillId()))
|
||||||
|
{
|
||||||
|
log.info("local rebill skip refund, REFUND fee exists billId={} orderNo={} orderStatus={} refundStatus={}",
|
||||||
|
billId, bill.getOrderNo(), bill.getOrderStatus(), bill.getRefundStatus());
|
||||||
|
return toResult(bill.getBillId());
|
||||||
|
}
|
||||||
|
log.info("local rebill -> refund path billId={} orderNo={} orderStatus={} refundStatus={}",
|
||||||
|
billId, bill.getOrderNo(), bill.getOrderStatus(), bill.getRefundStatus());
|
||||||
|
return refundLocalWithDbRetry(bill.getOrderNo().trim());
|
||||||
|
}
|
||||||
TmsPriceBillRequest req = buildBillRequestFromBill(bill);
|
TmsPriceBillRequest req = buildBillRequestFromBill(bill);
|
||||||
return billLocalWithDbRetry(billId.trim(), req);
|
return billLocalWithDbRetry(billId.trim(), req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** order_status 9/10 or refund_status=REFUNDED. */
|
||||||
|
private static boolean isLocalRefundCandidate(PrcOrderBill bill)
|
||||||
|
{
|
||||||
|
if (bill == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (BillOrderStatus.requiresFeeRefund(bill.getOrderStatus()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return BillRefundStatus.REFUNDED.getCode().equalsIgnoreCase(bill.getRefundStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRefundFeeLine(String billId)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(billId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<PrcIncomeBillFee> fees = incomeBillFeeMapper.selectByBillId(billId);
|
||||||
|
if (fees == null || fees.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (PrcIncomeBillFee row : fees)
|
||||||
|
{
|
||||||
|
if (row != null && FEE_TYPE_REFUND.equalsIgnoreCase(row.getFeeType()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private TmsPriceQuoteResult billLocalWithDbRetry(String billId, TmsPriceBillRequest billReq)
|
private TmsPriceQuoteResult billLocalWithDbRetry(String billId, TmsPriceBillRequest billReq)
|
||||||
{
|
{
|
||||||
RuntimeException last = null;
|
RuntimeException last = null;
|
||||||
@@ -815,6 +861,42 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
|||||||
throw last != null ? last : new ServiceException("local rebill retry exhausted");
|
throw last != null ? last : new ServiceException("local rebill retry exhausted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TmsPriceQuoteResult refundLocalWithDbRetry(String orderNo)
|
||||||
|
{
|
||||||
|
RuntimeException last = null;
|
||||||
|
for (int attempt = 1; attempt <= LOCAL_REBILL_DB_RETRY_MAX; attempt++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return self.refund(orderNo);
|
||||||
|
}
|
||||||
|
catch (RuntimeException e)
|
||||||
|
{
|
||||||
|
last = e;
|
||||||
|
if (attempt < LOCAL_REBILL_DB_RETRY_MAX && isRetryableDbConflict(e))
|
||||||
|
{
|
||||||
|
long sleepMs = LOCAL_REBILL_DB_RETRY_BASE_MS * attempt
|
||||||
|
+ (long) (Math.random() * LOCAL_REBILL_DB_RETRY_BASE_MS);
|
||||||
|
log.warn("local rebill refund db conflict, retry orderNo={} attempt={}/{} sleepMs={} msg={}",
|
||||||
|
orderNo, Integer.valueOf(attempt), Integer.valueOf(LOCAL_REBILL_DB_RETRY_MAX),
|
||||||
|
Long.valueOf(sleepMs), StringUtils.substring(e.getMessage(), 0, 200));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(sleepMs);
|
||||||
|
}
|
||||||
|
catch (InterruptedException ie)
|
||||||
|
{
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw last != null ? last : new ServiceException("local rebill refund retry exhausted");
|
||||||
|
}
|
||||||
|
|
||||||
/** Deadlock / lock wait — safe to retry whole bill txn (parallel local rebill). */
|
/** Deadlock / lock wait — safe to retry whole bill txn (parallel local rebill). */
|
||||||
private static boolean isRetryableDbConflict(Throwable e)
|
private static boolean isRetryableDbConflict(Throwable e)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user