订单列表重新计费优化

This commit is contained in:
wuweihua
2026-10-21 10:13:01 +08:00
parent be3856a59c
commit d7f7ebd981
9 changed files with 659 additions and 41 deletions
@@ -10,6 +10,7 @@ import com.bms.price.domain.vo.BatchAddIncomeBillFeePreview;
import com.bms.price.domain.vo.BatchAddIncomeBillFeeRequest;
import com.bms.price.domain.vo.ChargeWeightImportRow;
import com.bms.price.domain.vo.ChargeWeightTemplateRequest;
import com.bms.price.domain.vo.LocalRebillRequest;
import com.bms.price.domain.vo.RecalcBillPriceRequest;
import com.bms.price.domain.vo.UpdateIncomeBillFeeRequest;
import com.bms.price.domain.vo.TmsPriceBillRequest;
@@ -135,4 +136,25 @@ public interface IPrcOrderBillService
* Requires existing prc_order_bill; skips AR editable guard.
*/
void recalcBillPriceByBillId(String billId, String mode);
/**
* Full rebill from local prc_order_bill only (no Oracle upstream).
* Rebuilds TmsPriceBillRequest from the bill row then calls {@link #bill}.
*/
TmsPriceQuoteResult rebillLocalByBillId(String billId);
/**
* Async local rebill by selected billIds or list filters. Returns taskId for progress poll.
* Large sets: only bill_id loaded, then processed in pages of 500 with parallel workers.
*/
String submitLocalRebill(LocalRebillRequest request);
/** Poll Redis progress for local rebill task. */
BatchAddIncomeBillFeeTask getLocalRebillTask(String taskId);
/** Data-scoped bill_id list for local rebill (same filters as order-bill list). */
List<String> selectBillIdsForLocalRebill(PrcOrderBill query);
/** Data-scoped count for local rebill. */
int countBillIdsForLocalRebill(PrcOrderBill query);
}
@@ -50,11 +50,13 @@ import com.bms.price.domain.vo.BatchAddIncomeBillFeePreview;
import com.bms.price.domain.vo.BatchAddIncomeBillFeeRequest;
import com.bms.price.domain.vo.ChargeWeightImportRow;
import com.bms.price.domain.vo.ChargeWeightTemplateRequest;
import com.bms.price.domain.vo.LocalRebillRequest;
import com.bms.price.domain.vo.RecalcBillPriceRequest;
import com.bms.price.domain.vo.UpdateIncomeBillFeeRequest;
import com.bms.price.task.PrcBatchAddIncomeFeeAsyncRunner;
import com.bms.price.task.PrcChargeWeightImportAsyncRunner;
import com.bms.price.task.PrcChargeWeightImportAsyncRunner.ChargeWeightImportWorkItem;
import com.bms.price.task.PrcOrderBillLocalRebillAsyncRunner;
import com.bms.price.task.PrcRecalcBillPriceAsyncRunner;
import com.bms.price.domain.vo.TmsPriceBillRequest;
import com.bms.price.domain.vo.TmsOrderBillStatusResult;
@@ -143,6 +145,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
@Autowired
private PrcRecalcBillPriceAsyncRunner recalcBillPriceAsyncRunner;
@Autowired
private PrcOrderBillLocalRebillAsyncRunner localRebillAsyncRunner;
@Autowired
private IBmsBizRuleService bizRuleService;
@@ -150,6 +155,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
private static final int BATCH_ADD_FEE_PREVIEW_LIMIT = 20;
private static final int CHARGE_WEIGHT_IMPORT_MAX = 5000;
private static final int RECALC_PRICE_MAX = 5000;
/** Soft max for one local-rebill job (id-only load + paged parallel). */
private static final int LOCAL_REBILL_MAX = 100000;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -694,6 +701,168 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
persistProductOrRedlineOnly(bill, result, productMode);
}
@Override
@Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult rebillLocalByBillId(String billId)
{
if (StringUtils.isEmpty(billId))
{
throw new ServiceException(MessageUtils.message("price.bill.billIdRequired"));
}
PrcOrderBill bill = orderBillMapper.selectPrcOrderBillById(billId.trim());
if (bill == null)
{
throw new ServiceException(MessageUtils.message("price.bill.notFound"));
}
if (StringUtils.isEmpty(bill.getOrderNo()))
{
throw new ServiceException(MessageUtils.message("price.bill.orderNoRequired"));
}
if (bill.getBizDate() == null)
{
throw new ServiceException(MessageUtils.message("price.bill.bizDateRequired"));
}
TmsPriceBillRequest req = buildBillRequestFromBill(bill);
// Full bill() path: AR guard, weight rules, quote, persist (local snapshot only).
return self.bill(req);
}
@Override
public String submitLocalRebill(LocalRebillRequest request)
{
if (request == null)
{
throw new ServiceException(MessageUtils.message("price.bill.recalc.filterRequired"));
}
PrcOrderBill query = buildLocalRebillQuery(request);
int total = self.countBillIdsForLocalRebill(query);
if (total <= 0)
{
throw new ServiceException(MessageUtils.message("price.bill.recalc.noMatch"));
}
if (total > LOCAL_REBILL_MAX)
{
throw new ServiceException(MessageUtils.message("price.bill.recalc.tooMany",
Integer.valueOf(LOCAL_REBILL_MAX)));
}
// Id-only: avoid loading full prc_order_bill rows for large jobs.
List<String> billIds = self.selectBillIdsForLocalRebill(query);
if (billIds == null || billIds.isEmpty())
{
throw new ServiceException(MessageUtils.message("price.bill.recalc.noMatch"));
}
String taskId = IdUtils.fastSimpleUUID();
localRebillAsyncRunner.put(taskId, BatchAddIncomeBillFeeTask.STATUS_PENDING, 0, "queued", null,
billIds.size(), 0, 0);
localRebillAsyncRunner.run(taskId, billIds);
return taskId;
}
@Override
public BatchAddIncomeBillFeeTask getLocalRebillTask(String taskId)
{
if (StringUtils.isEmpty(taskId) || localRebillAsyncRunner == null)
{
return null;
}
return localRebillAsyncRunner.get(taskId);
}
@Override
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "price:billing:bill")
public List<String> selectBillIdsForLocalRebill(PrcOrderBill query)
{
if (query == null)
{
return new ArrayList<String>();
}
normalizeLocalRebillQuery(query);
List<String> ids = orderBillMapper.selectPrcOrderBillIds(query);
return ids == null ? new ArrayList<String>() : ids;
}
@Override
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "price:billing:bill")
public int countBillIdsForLocalRebill(PrcOrderBill query)
{
if (query == null)
{
return 0;
}
normalizeLocalRebillQuery(query);
return orderBillMapper.countPrcOrderBillIds(query);
}
private PrcOrderBill buildLocalRebillQuery(LocalRebillRequest request)
{
String scopeMode = StringUtils.nvl(request.getScopeMode(), "").trim().toUpperCase();
PrcOrderBill query = new PrcOrderBill();
if ("SELECTED".equals(scopeMode))
{
List<String> billIds = normalizeCodeList(request.getBillIds());
if (billIds == null || billIds.isEmpty())
{
throw new ServiceException(MessageUtils.message("price.bill.recalc.selectedRequired"));
}
query.setBillIds(billIds);
return query;
}
List<String> orderNos = normalizeCodeList(request.getOrderNos());
List<String> trackingNos = normalizeCodeList(request.getTrackingNos());
List<String> transferNos = normalizeCodeList(request.getTransferNos());
List<String> orderIdentifiers = normalizeCodeList(request.getOrderIdentifiers());
List<String> customerCodes = normalizeCodeList(request.getCustomerCodes());
List<String> productCodes = normalizeCodeList(request.getProductCodes());
String creditArrearsStatus = StringUtils.isEmpty(request.getCreditArrearsStatus()) ? null
: request.getCreditArrearsStatus().trim();
String countryCode = StringUtils.isEmpty(request.getCountryCode()) ? null
: request.getCountryCode().trim().toUpperCase();
String beginBizDate = StringUtils.isEmpty(request.getBeginBizDate()) ? null : request.getBeginBizDate().trim();
String endBizDate = StringUtils.isEmpty(request.getEndBizDate()) ? null : request.getEndBizDate().trim();
boolean hasFilter = (orderNos != null && !orderNos.isEmpty())
|| (trackingNos != null && !trackingNos.isEmpty())
|| (transferNos != null && !transferNos.isEmpty())
|| (orderIdentifiers != null && !orderIdentifiers.isEmpty())
|| (customerCodes != null && !customerCodes.isEmpty())
|| (productCodes != null && !productCodes.isEmpty())
|| StringUtils.isNotEmpty(creditArrearsStatus)
|| StringUtils.isNotEmpty(countryCode)
|| StringUtils.isNotEmpty(beginBizDate)
|| StringUtils.isNotEmpty(endBizDate);
if (!hasFilter)
{
throw new ServiceException(MessageUtils.message("price.bill.recalc.filterRequired"));
}
query.setOrderNos(orderNos);
query.setTrackingNos(trackingNos);
query.setTransferNos(transferNos);
query.setOrderIdentifiers(orderIdentifiers);
query.setCustomerCodes(customerCodes);
query.setProductCodes(productCodes);
query.setCreditArrearsStatus(creditArrearsStatus);
query.setCountryCode(countryCode);
if (beginBizDate != null)
{
query.getParams().put("beginBizDate", beginBizDate);
}
if (endBizDate != null)
{
query.getParams().put("endBizDate", endBizDate);
}
return query;
}
private void normalizeLocalRebillQuery(PrcOrderBill query)
{
query.setOrderNos(normalizeCodeList(query.getOrderNos()));
query.setCustomerCodes(normalizeCodeList(query.getCustomerCodes()));
query.setProductCodes(normalizeCodeList(query.getProductCodes()));
query.setTrackingNos(normalizeCodeList(query.getTrackingNos()));
query.setTransferNos(normalizeCodeList(query.getTransferNos()));
query.setOrderIdentifiers(normalizeCodeList(query.getOrderIdentifiers()));
query.setBillIds(normalizeCodeList(query.getBillIds()));
}
private static String normalizeRecalcMode(String mode)
{
String m = StringUtils.nvl(mode, "").trim().toUpperCase();