From d7f7ebd981e392cb9dadeecc6a9c4c468e9b3053 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Wed, 21 Oct 2026 10:13:01 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=88=97=E8=A1=A8=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E8=AE=A1=E8=B4=B9=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/price/BillingController.java | 25 ++ .../price/domain/vo/LocalRebillRequest.java | 29 +++ .../bms/price/mapper/PrcOrderBillMapper.java | 6 + .../price/service/IPrcOrderBillService.java | 22 ++ .../service/impl/PrcOrderBillServiceImpl.java | 169 +++++++++++++ .../PrcOrderBillLocalRebillAsyncRunner.java | 222 ++++++++++++++++++ .../mapper/price/PrcOrderBillMapper.xml | 29 ++- bms-front/src/api/price/billing.js | 44 ++++ bms-front/src/views/price/orderBill/index.vue | 154 +++++++++--- 9 files changed, 659 insertions(+), 41 deletions(-) create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/domain/vo/LocalRebillRequest.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java index ead7b505..db955640 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java @@ -43,6 +43,7 @@ import com.bms.price.domain.vo.BatchAddIncomeBillFeeRequest; import com.bms.price.domain.vo.BillingTaskSubmitRequest; 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.UpdateChargeWeightRequest; import com.bms.price.domain.vo.UpdateIncomeBillFeeRequest; @@ -197,6 +198,30 @@ public class BillingController extends BaseController return success(orderBillProductCheckService.listOrdersByProduct(query)); } + /** + * Order-list rebill from local prc_order_bill only (no Oracle TAB_ORDER). + * Poll {@code GET /orderBill/rebill/task/{taskId}}. + */ + @PreAuthorize("@ss.hasPermi('price:orderBill:rebill')") + @Log(title = "Order bill local rebill", businessType = BusinessType.UPDATE) + @PostMapping("/orderBill/rebill") + public AjaxResult submitLocalRebill(@RequestBody LocalRebillRequest request) + { + return successTask(prcOrderBillService.submitLocalRebill(request)); + } + + @PreAuthorize("@ss.hasPermi('price:orderBill:rebill')") + @GetMapping("/orderBill/rebill/task/{taskId}") + public AjaxResult getLocalRebillTask(@PathVariable("taskId") String taskId) + { + BatchAddIncomeBillFeeTask task = prcOrderBillService.getLocalRebillTask(taskId); + if (task == null) + { + return error(MessageUtils.message("price.bill.recalc.taskNotExists")); + } + return success(task); + } + /** * Order no options for remote-search dropdown (bill list multi search). */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/LocalRebillRequest.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/LocalRebillRequest.java new file mode 100644 index 00000000..8b6cdeeb --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/LocalRebillRequest.java @@ -0,0 +1,29 @@ +package com.bms.price.domain.vo; + +import java.io.Serializable; +import java.util.List; +import lombok.Data; + +/** + * Order-list local rebill: use prc_order_bill only (no Oracle TAB_ORDER). + * scopeMode SELECTED = billIds; ALL = filters (orderIdentifiers / customer / bizDate...). + */ +@Data +public class LocalRebillRequest implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** ALL | SELECTED */ + private String scopeMode; + private List billIds; + private List orderNos; + private List trackingNos; + private List transferNos; + private List orderIdentifiers; + private List customerCodes; + private List productCodes; + private String countryCode; + private String creditArrearsStatus; + private String beginBizDate; + private String endBizDate; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java index 60a06584..d7ddc7b7 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java @@ -21,6 +21,12 @@ public interface PrcOrderBillMapper public List selectPrcOrderBillList(PrcOrderBill bill); + /** bill_id only (same filters / data-scope as list), for large local rebill. */ + List selectPrcOrderBillIds(PrcOrderBill bill); + + /** Count matching bills (same filters / data-scope as list). */ + int countPrcOrderBillIds(PrcOrderBill bill); + /** Distinct customer+product groups for product-check (order counts in filter window). */ List> selectDistinctProductGroups(PrcOrderBill bill); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcOrderBillService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcOrderBillService.java index 0dcfacaf..8db91dfa 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcOrderBillService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcOrderBillService.java @@ -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 selectBillIdsForLocalRebill(PrcOrderBill query); + + /** Data-scoped count for local rebill. */ + int countBillIdsForLocalRebill(PrcOrderBill query); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java index 24dda9b7..cb280f2b 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java @@ -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 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 selectBillIdsForLocalRebill(PrcOrderBill query) + { + if (query == null) + { + return new ArrayList(); + } + normalizeLocalRebillQuery(query); + List ids = orderBillMapper.selectPrcOrderBillIds(query); + return ids == null ? new ArrayList() : 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 billIds = normalizeCodeList(request.getBillIds()); + if (billIds == null || billIds.isEmpty()) + { + throw new ServiceException(MessageUtils.message("price.bill.recalc.selectedRequired")); + } + query.setBillIds(billIds); + return query; + } + List orderNos = normalizeCodeList(request.getOrderNos()); + List trackingNos = normalizeCodeList(request.getTrackingNos()); + List transferNos = normalizeCodeList(request.getTransferNos()); + List orderIdentifiers = normalizeCodeList(request.getOrderIdentifiers()); + List customerCodes = normalizeCodeList(request.getCustomerCodes()); + List 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(); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java b/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java new file mode 100644 index 00000000..238608c3 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java @@ -0,0 +1,222 @@ +package com.bms.price.task; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import com.bms.common.core.redis.RedisCache; +import com.bms.common.utils.StringUtils; +import com.bms.price.config.BillingProperties; +import com.bms.price.domain.BatchAddIncomeBillFeeTask; +import com.bms.price.service.IPrcOrderBillService; + +/** + * Async local rebill from prc_order_bill only (no Oracle / no MQ). + * Large jobs: process in pages of {@link #PAGE_SIZE}; each page runs in a short-lived worker pool. + */ +@Component +public class PrcOrderBillLocalRebillAsyncRunner +{ + private static final Logger log = LoggerFactory.getLogger(PrcOrderBillLocalRebillAsyncRunner.class); + + public static final String CACHE_KEY_PREFIX = "price:billing:localRebill:task:"; + public static final int TTL_MINUTES = 120; + /** Bill this many, wait for page done, then next page. */ + public static final int PAGE_SIZE = 500; + public static final int MIN_THREADS = 2; + public static final int MAX_THREADS = 16; + + @Autowired + @Lazy + private IPrcOrderBillService orderBillService; + + @Autowired + private RedisCache redisCache; + + @Autowired + private BillingProperties billingProperties; + + @Async("threadPoolTaskExecutor") + public void run(String taskId, List billIds) + { + int total = billIds == null ? 0 : billIds.size(); + AtomicInteger success = new AtomicInteger(0); + AtomicInteger fail = new AtomicInteger(0); + StringBuilder failTips = new StringBuilder(); + int threads = clampThreads(billingProperties == null ? null : billingProperties.getOrderSourceSyncThreads()); + ExecutorService pool = newWorkerPool(threads, "local-rebill"); + try + { + put(taskId, BatchAddIncomeBillFeeTask.STATUS_RUNNING, 1, "running", null, total, 0, 0); + if (total == 0) + { + put(taskId, BatchAddIncomeBillFeeTask.STATUS_FAIL, 100, "failed", "no bills", 0, 0, 0); + return; + } + int processed = 0; + for (int from = 0; from < total; from += PAGE_SIZE) + { + int to = Math.min(from + PAGE_SIZE, total); + List page = billIds.subList(from, to); + List> jobs = new ArrayList>(page.size()); + for (String billId : page) + { + final String one = billId; + jobs.add(() -> { + try + { + orderBillService.rebillLocalByBillId(one); + success.incrementAndGet(); + return Boolean.TRUE; + } + catch (Exception e) + { + fail.incrementAndGet(); + appendFail(failTips, one, e); + log.warn("local rebill failed taskId={} billId={}", taskId, one, e); + return Boolean.FALSE; + } + }); + } + invokeAllQuiet(pool, jobs); + processed = to; + int percent = Math.min(99, (int) Math.floor(100.0 * processed / total)); + put(taskId, BatchAddIncomeBillFeeTask.STATUS_RUNNING, percent, + "processing " + processed + "/" + total + " page=" + ((from / PAGE_SIZE) + 1), + null, total, success.get(), fail.get()); + } + String msg = "success=" + success.get() + ", fail=" + fail.get(); + put(taskId, BatchAddIncomeBillFeeTask.STATUS_SUCCESS, 100, msg, + fail.get() > 0 && failTips.length() > 0 ? failTips.toString() : null, + total, success.get(), fail.get()); + log.info("local rebill done taskId={} total={} success={} fail={} threads={} pageSize={}", + taskId, total, success.get(), fail.get(), threads, PAGE_SIZE); + } + catch (Exception e) + { + log.error("local rebill aborted taskId={}", taskId, e); + put(taskId, BatchAddIncomeBillFeeTask.STATUS_FAIL, 100, "failed", errorMessage(e), + total, success.get(), fail.get()); + } + finally + { + shutdownPool(pool); + } + } + + public void put(String taskId, String status, int percent, String message, String error, + int total, int success, int fail) + { + BatchAddIncomeBillFeeTask task = new BatchAddIncomeBillFeeTask(); + task.setTaskId(taskId); + task.setStatus(status); + task.setPercent(percent); + task.setMessage(message); + task.setError(error); + task.setTotal(total); + task.setSuccess(success); + task.setFail(fail); + redisCache.setCacheObject(CACHE_KEY_PREFIX + taskId, task, TTL_MINUTES, TimeUnit.MINUTES); + } + + public BatchAddIncomeBillFeeTask get(String taskId) + { + return redisCache.getCacheObject(CACHE_KEY_PREFIX + taskId); + } + + private static void invokeAllQuiet(ExecutorService pool, List> jobs) throws InterruptedException + { + List> futures = pool.invokeAll(jobs); + for (Future f : futures) + { + try + { + f.get(); + } + catch (ExecutionException e) + { + // counted in job body + } + } + } + + private static ExecutorService newWorkerPool(int threads, String namePrefix) + { + AtomicInteger seq = new AtomicInteger(0); + ThreadFactory factory = r -> { + Thread t = new Thread(r); + t.setName(namePrefix + "-" + seq.incrementAndGet()); + t.setDaemon(true); + return t; + }; + return Executors.newFixedThreadPool(Math.max(1, threads), factory); + } + + private static void shutdownPool(ExecutorService pool) + { + if (pool == null) + { + return; + } + pool.shutdown(); + try + { + if (!pool.awaitTermination(60, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + } + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + pool.shutdownNow(); + } + } + + static int clampThreads(Integer n) + { + int v = n == null ? 8 : n.intValue(); + if (v < MIN_THREADS) + { + return MIN_THREADS; + } + if (v > MAX_THREADS) + { + return MAX_THREADS; + } + return v; + } + + private static void appendFail(StringBuilder failTips, String billId, Exception e) + { + synchronized (failTips) + { + if (failTips.length() >= 800) + { + return; + } + if (failTips.length() > 0) + { + failTips.append("; "); + } + failTips.append(billId).append(":").append(errorMessage(e)); + } + } + + private static String errorMessage(Exception e) + { + return StringUtils.isEmpty(e.getMessage()) ? e.getClass().getSimpleName() : e.getMessage(); + } +} diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml index d3b431af..eaae5c9a 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml @@ -409,9 +409,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" limit #{limit} - + + + order by p.create_time desc, p.bill_id desc + + + + + diff --git a/bms-front/src/api/price/billing.js b/bms-front/src/api/price/billing.js index aed6e483..17e42223 100644 --- a/bms-front/src/api/price/billing.js +++ b/bms-front/src/api/price/billing.js @@ -85,6 +85,50 @@ export function listOrderBillsByProduct(data) { }) } +/** Order-list local rebill (prc_order_bill only, no Oracle). Returns { taskId }. */ +export function submitLocalRebill(data) { + return request({ + url: '/price/billing/orderBill/rebill', + method: 'post', + data, + timeout: 120000 + }) +} + +export function getLocalRebillTask(taskId) { + return request({ + url: '/price/billing/orderBill/rebill/task/' + taskId, + method: 'get' + }) +} + +/** + * Async local rebill; poll until done. + * @param {object} data LocalRebillRequest + * @param {(task: object) => void} [onProgress] + */ +export async function localRebillAsync(data, onProgress) { + const submitRes = await submitLocalRebill(data) + const d = submitRes && submitRes.data + const taskId = d && (d.taskId || d) + if (!taskId) throw new Error('missing local rebill taskId') + const started = Date.now() + const maxMs = 30 * 60 * 1000 + while (Date.now() - started < maxMs) { + const res = await getLocalRebillTask(String(taskId)) + const task = res.data || {} + if (typeof onProgress === 'function') onProgress(task) + if (task.status === 'SUCCESS') return { task } + if (task.status === 'FAIL') { + const err = new Error(task.error || task.message || 'local rebill failed') + err.task = task + throw err + } + await new Promise(resolve => setTimeout(resolve, 800)) + } + throw new Error('local rebill timeout') +} + /** Receivable bill detail */ export function getBill(billId) { return request({ diff --git a/bms-front/src/views/price/orderBill/index.vue b/bms-front/src/views/price/orderBill/index.vue index cdf861c7..30265726 100644 --- a/bms-front/src/views/price/orderBill/index.vue +++ b/bms-front/src/views/price/orderBill/index.vue @@ -20,6 +20,7 @@ type="primary" plain icon="Refresh" + :loading="batchLoading" v-hasPermi="['price:orderBill:rebill']" @click="openFilterRebill()" >重新计费 @@ -91,7 +92,35 @@ {{ billingFailLabel(row.billingFailStatus) }} - + + +
{{ rebillProgressMessage }}
+
+ 总数 {{ rebillProgressTotal }} + / 成功 {{ rebillProgressSuccess }} + / 失败 {{ rebillProgressFail }} +
+

请勿关闭页面,大量订单重新计费可能需要一些时间

+ +
@@ -99,18 +128,22 @@