From 44df6b1a1ba00445ca9bf042fe426559d6157f37 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Thu, 5 Nov 2026 16:58:30 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AE=A1=E8=B4=B9?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-local.yml | 4 +- .../src/main/resources/application-prod.yml | 4 +- .../src/main/resources/application.yml | 2 + .../bms/price/config/BillingProperties.java | 15 ++++ .../price/service/IPrcOrderBillService.java | 2 +- .../service/impl/PrcOrderBillServiceImpl.java | 4 +- .../PrcOrderBillLocalRebillAsyncRunner.java | 70 ++++++++----------- 7 files changed, 52 insertions(+), 49 deletions(-) diff --git a/bms-back/bms-admin/src/main/resources/application-local.yml b/bms-back/bms-admin/src/main/resources/application-local.yml index d9e07474..f9a2f83b 100644 --- a/bms-back/bms-admin/src/main/resources/application-local.yml +++ b/bms-back/bms-admin/src/main/resources/application-local.yml @@ -38,8 +38,8 @@ spring: initialSize: 5 # 最小连接池数量 minIdle: 10 - # 最大连接池数量 - maxActive: 20 + # 最大连接池数量(local rebill 16 workers + UI/MQ headroom) + maxActive: 40 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置连接超时时间 diff --git a/bms-back/bms-admin/src/main/resources/application-prod.yml b/bms-back/bms-admin/src/main/resources/application-prod.yml index 1ea85a50..ccf6cb81 100644 --- a/bms-back/bms-admin/src/main/resources/application-prod.yml +++ b/bms-back/bms-admin/src/main/resources/application-prod.yml @@ -51,8 +51,8 @@ spring: initialSize: 5 # 最小连接池数量 minIdle: 10 - # 最大连接池数量 - maxActive: 20 + # 最大连接池数量(local rebill 16 workers + UI/MQ headroom) + maxActive: 40 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置连接超时时间 diff --git a/bms-back/bms-admin/src/main/resources/application.yml b/bms-back/bms-admin/src/main/resources/application.yml index a61fcc06..29427a10 100644 --- a/bms-back/bms-admin/src/main/resources/application.yml +++ b/bms-back/bms-admin/src/main/resources/application.yml @@ -40,6 +40,8 @@ bms: # 同步已直接复用订单行(跳过每单 Oracle 公网查询),瓶颈在 MySQL 计费写入, # 同客户行锁冲突由 billWithDbRetry 3 次重试兜底,16 路实测吞吐最高。 order-source-sync-threads: 16 + # Order-list local rebill workers (2~24). Pair with Druid maxActive >= threads + 8. + local-rebill-threads: 16 # Night auto-rebill: hours 0-8, pull 500 (all statuses) -> bill -> next 500 until empty / 09:00. # Empty page => no billing. night-retry: diff --git a/bms-back/bms-system/src/main/java/com/bms/price/config/BillingProperties.java b/bms-back/bms-system/src/main/java/com/bms/price/config/BillingProperties.java index fd8ecb2e..7dd9c9c0 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/config/BillingProperties.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/config/BillingProperties.java @@ -20,6 +20,11 @@ public class BillingProperties private int consumerThreads = 8; /** Concurrent threads for monitor order-source sync */ private int orderSourceSyncThreads = 8; + /** + * Concurrent threads for order-list local rebill (2~24). + * Keep below Druid maxActive with headroom for other traffic. + */ + private int localRebillThreads = 16; /** Night window auto-retry of prc_billing_task (all statuses) */ private NightRetry nightRetry = new NightRetry(); private Oracle oracle = new Oracle(); @@ -84,6 +89,16 @@ public class BillingProperties this.orderSourceSyncThreads = orderSourceSyncThreads; } + public int getLocalRebillThreads() + { + return localRebillThreads; + } + + public void setLocalRebillThreads(int localRebillThreads) + { + this.localRebillThreads = localRebillThreads; + } + public NightRetry getNightRetry() { return nightRetry; 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 b5db8a2a..f8139c2e 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 @@ -159,7 +159,7 @@ public interface IPrcOrderBillService /** * 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. + * Large sets: only bill_id loaded, then processed by continuous parallel workers. */ String submitLocalRebill(LocalRebillRequest request); 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 5b38b12f..f1617014 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 @@ -183,8 +183,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; + /** Soft max for one local-rebill job (id-only load + continuous workers). */ + private static final int LOCAL_REBILL_MAX = 200000; /** Soft max for one order/billing list Excel export. */ private static final int ORDER_BILL_EXPORT_MAX = 50000; 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 index 4ca52106..c2905a14 100644 --- 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 @@ -36,8 +36,7 @@ import com.bms.price.service.IPrcOrderBillService; /** * Async local rebill from prc_order_bill only (no Oracle / MQ). - * Large jobs page by {@link #PAGE_SIZE}; each page runs in a short-lived pool. - * Cancel is cooperative: current in-flight bills finish, remaining bills are skipped. + * Continuous worker pull (no page barrier); cancel skips remaining billIds. */ @Component public class PrcOrderBillLocalRebillAsyncRunner @@ -46,13 +45,11 @@ public class PrcOrderBillLocalRebillAsyncRunner 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; + public static final int MAX_THREADS = 24; private static final int FAIL_DETAIL_MAX = 200; - /** Publish Redis progress at most this often while a page is running. */ - private static final int PROGRESS_EVERY_N = 20; + /** Publish Redis progress at most this often while workers run. */ + private static final int PROGRESS_EVERY_N = 50; private static final long PROGRESS_MIN_INTERVAL_MS = 500L; private final ConcurrentHashMap cancelFlags = new ConcurrentHashMap(); @@ -86,10 +83,12 @@ public class PrcOrderBillLocalRebillAsyncRunner AtomicInteger success = new AtomicInteger(0); AtomicInteger fail = new AtomicInteger(0); AtomicInteger done = new AtomicInteger(0); + AtomicInteger nextIndex = new AtomicInteger(0); AtomicLong lastProgressAt = new AtomicLong(0L); StringBuilder failTips = new StringBuilder(); List> failDetails = Collections.synchronizedList(new ArrayList>()); - int threads = clampThreads(billingProperties == null ? null : billingProperties.getOrderSourceSyncThreads()); + int configured = billingProperties == null ? 16 : billingProperties.getLocalRebillThreads(); + int threads = clampThreads(Integer.valueOf(configured)); cancelFlags.computeIfAbsent(taskId, k -> new AtomicBoolean(false)); ExecutorService pool = newWorkerPool(threads, "local-rebill"); try @@ -107,30 +106,24 @@ public class PrcOrderBillLocalRebillAsyncRunner finishCancelled(taskId, request, total, success.get(), fail.get(), threads, failDetails); return; } - boolean cancelled = false; - for (int from = 0; from < total; from += PAGE_SIZE) + // Continuous pull: N workers grab next billId — no page barrier idle wait. + final List ids = billIds; + List> workers = new ArrayList>(threads); + for (int w = 0; w < threads; w++) { - if (isCancelRequested(taskId)) - { - cancelled = true; - break; - } - 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(() -> { - if (isCancelRequested(taskId)) + workers.add(() -> { + while (!isCancelRequested(taskId)) + { + int i = nextIndex.getAndIncrement(); + if (i >= total) { - return Boolean.FALSE; + return Boolean.TRUE; } + String one = ids.get(i); try { orderBillService.rebillLocalByBillId(one); success.incrementAndGet(); - return Boolean.TRUE; } catch (Exception e) { @@ -139,26 +132,20 @@ public class PrcOrderBillLocalRebillAsyncRunner collectFailDetail(failDetails, one, e); log.warn("local rebill failed taskId={} billId={} msg={}", taskId, one, e.getMessage()); - return Boolean.FALSE; } finally { int finished = done.incrementAndGet(); - maybePublishProgress(taskId, total, finished, success.get(), fail.get(), lastProgressAt); + maybePublishProgress(taskId, total, finished, success.get(), fail.get(), + lastProgressAt); } - }); - } - invokeAllQuiet(pool, jobs); - if (isCancelRequested(taskId)) - { - cancelled = true; - break; - } - // Page boundary: always flush latest counts (even if throttle skipped mid-page). - publishProgress(taskId, total, done.get(), success.get(), fail.get()); - lastProgressAt.set(System.currentTimeMillis()); + } + return Boolean.FALSE; + }); } - if (cancelled || isCancelRequested(taskId)) + invokeAllQuiet(pool, workers); + publishProgress(taskId, total, done.get(), success.get(), fail.get()); + if (isCancelRequested(taskId)) { finishCancelled(taskId, request, total, success.get(), fail.get(), threads, failDetails); return; @@ -170,8 +157,8 @@ public class PrcOrderBillLocalRebillAsyncRunner String doneStatus = fail.get() > 0 ? EsBizLogSupport.STATUS_FAIL : EsBizLogSupport.STATUS_SUCCESS; String doneErr = fail.get() > 0 ? ("fail=" + fail.get() + ", ok=" + success.get()) : null; saveDoneLog(taskId, request, total, success.get(), fail.get(), threads, failDetails, doneStatus, doneErr); - log.info("local rebill done taskId={} total={} success={} fail={} threads={} pageSize={}", - taskId, total, success.get(), fail.get(), threads, PAGE_SIZE); + log.info("local rebill done taskId={} total={} success={} fail={} threads={}", + taskId, total, success.get(), fail.get(), threads); } catch (Exception e) { @@ -255,7 +242,6 @@ public class PrcOrderBillLocalRebillAsyncRunner resp.put("fail", Integer.valueOf(fail)); resp.put("success", Integer.valueOf(ok)); resp.put("threads", Integer.valueOf(threads)); - resp.put("pageSize", Integer.valueOf(PAGE_SIZE)); if (failDetails != null && !failDetails.isEmpty()) { resp.put("failDetails", new ArrayList>(failDetails)); From 87c75922aa483afd09e0815ea402ae6be536a8bb Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Thu, 5 Nov 2026 18:28:57 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9F=A5=E7=9C=8B?= =?UTF-8?q?=E4=BB=B7=E6=A0=BC=E5=A5=97=E9=A4=90=E5=8E=86=E5=8F=B2=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitor/PriceLogController.java | 32 +- .../bms/price/search/EsBillSearchStore.java | 268 ++++++++++- .../bms/price/search/IBillSearchStore.java | 33 ++ .../bms/price/search/PriceChangeEsLog.java | 21 + .../bms/price/search/PriceChangeGroup.java | 74 +++ .../price/search/PriceChangeGroupPage.java | 31 ++ .../service/impl/PrcPriceSetServiceImpl.java | 17 + bms-front/src/api/monitor/priceLog.js | 9 + .../detail/SetVersionPriceLogDrawer.vue | 450 ++++++++++++++++++ .../set/components/detail/SetVersionTable.vue | 10 +- bms-front/src/views/price/set/detail.vue | 17 + 11 files changed, 956 insertions(+), 6 deletions(-) create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroup.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroupPage.java create mode 100644 bms-front/src/views/price/set/components/detail/SetVersionPriceLogDrawer.vue diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/PriceLogController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/PriceLogController.java index 50d20834..5f20894b 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/PriceLogController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/PriceLogController.java @@ -16,6 +16,7 @@ import com.bms.common.core.page.TableSupport; import com.bms.price.search.BillSearchPage; import com.bms.price.search.EsLogType; import com.bms.price.search.IBillSearchStore; +import com.bms.price.search.PriceChangeGroupPage; /** * 来自 ES 的价格变更前后日志(销售价 / 产品价 / 红线价)。 @@ -31,12 +32,39 @@ public class PriceLogController extends BaseController @GetMapping("/list") public TableDataInfo list( @RequestParam(value = "bizId", required = false) String bizId, - @RequestParam(value = "status", required = false) String status) + @RequestParam(value = "status", required = false) String status, + @RequestParam(value = "psvId", required = false) Long psvId, + @RequestParam(value = "priceKind", required = false) String priceKind, + @RequestParam(value = "productCode", required = false) String productCode, + @RequestParam(value = "updateTime", required = false) String updateTime, + @RequestParam(value = "updateTimePrefix", required = false) String updateTimePrefix) { PageDomain pageDomain = TableSupport.buildPageRequest(); int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue(); int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue(); - BillSearchPage page = billSearchStore.search(EsLogType.PRICE_CHANGE, bizId, status, pageNum, pageSize); + BillSearchPage page = billSearchStore.search(EsLogType.PRICE_CHANGE, bizId, status, psvId, priceKind, + productCode, updateTime, updateTimePrefix, pageNum, pageSize); + TableDataInfo rsp = new TableDataInfo(); + rsp.setCode(HttpStatus.SUCCESS); + rsp.setMsg("查询成功"); + rsp.setRows(page.getRows()); + rsp.setTotal(page.getTotal()); + return rsp; + } + + /** Group by productCode + write time for package-version drill-down. */ + @PreAuthorize("@ss.hasPermi('monitor:priceLog:list')") + @GetMapping("/groups") + public TableDataInfo groups( + @RequestParam(value = "psvId", required = true) Long psvId, + @RequestParam(value = "status", required = false) String status, + @RequestParam(value = "priceKind", required = false) String priceKind) + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue(); + int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue(); + PriceChangeGroupPage page = billSearchStore.searchPriceChangeGroups(psvId, priceKind, status, pageNum, + pageSize); TableDataInfo rsp = new TableDataInfo(); rsp.setCode(HttpStatus.SUCCESS); rsp.setMsg("查询成功"); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java b/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java index 2e0ec3c6..85338de6 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java @@ -3,7 +3,11 @@ package com.bms.price.search; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; @@ -148,6 +152,27 @@ public class EsBillSearchStore implements IBillSearchStore @Override public BillSearchPage search(String logType, String bizId, String status, int pageNum, int pageSize) + { + return search(logType, bizId, status, null, null, pageNum, pageSize); + } + + @Override + public BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind, + int pageNum, int pageSize) + { + return search(logType, bizId, status, psvId, priceKind, null, null, pageNum, pageSize); + } + + @Override + public BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind, + String productCode, String updateTime, int pageNum, int pageSize) + { + return search(logType, bizId, status, psvId, priceKind, productCode, updateTime, null, pageNum, pageSize); + } + + @Override + public BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind, + String productCode, String updateTime, String updateTimePrefix, int pageNum, int pageSize) { BillSearchPage page = new BillSearchPage(); if (pageNum < 1) @@ -171,7 +196,8 @@ public class EsBillSearchStore implements IBillSearchStore sortItem.put("updatedAt.keyword", sortOpt); sort.add(sortItem); body.put("sort", sort); - body.put("query", buildQuery(logType, bizId, status)); + body.put("query", buildQuery(logType, bizId, status, psvId, priceKind, productCode, updateTime, + updateTimePrefix)); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity(body.toJSONString(), headers); @@ -217,6 +243,108 @@ public class EsBillSearchStore implements IBillSearchStore return page; } + @Override + public PriceChangeGroupPage searchPriceChangeGroups(Long psvId, String priceKind, String status, + int pageNum, int pageSize) + { + PriceChangeGroupPage page = new PriceChangeGroupPage(); + if (psvId == null) + { + return page; + } + if (pageNum < 1) + { + pageNum = 1; + } + if (pageSize < 1) + { + pageSize = 10; + } + List all = loadAllPriceChanges(psvId, priceKind, status, 10000); + Map grouped = new LinkedHashMap(); + for (BillSyncSnapshot snap : all) + { + if (snap == null) + { + continue; + } + JSONObject req = parseRequestObject(snap.getRequestJson()); + JSONObject keys = req == null ? null : req.getJSONObject("bizKeys"); + String productCode = keys == null ? "" : StringUtils.nvl(keys.getString("productCode"), ""); + String time = StringUtils.nvl(snap.getUpdateTime(), ""); + String key = productCode + "|" + time; + PriceChangeGroup group = grouped.get(key); + if (group == null) + { + group = new PriceChangeGroup(); + group.setProductCode(StringUtils.isEmpty(productCode) ? "-" : productCode); + group.setUpdateTime(time); + group.setAction(req == null ? null : req.getString("action")); + group.setOperator(req == null ? null : req.getString("operator")); + group.setStatus(snap.getStatus()); + group.setChangeCount(0); + grouped.put(key, group); + } + group.setChangeCount(group.getChangeCount() + 1); + if (EsBizLogSupport.STATUS_FAIL.equals(snap.getStatus())) + { + group.setStatus(EsBizLogSupport.STATUS_FAIL); + } + } + List list = new ArrayList(grouped.values()); + page.setTotal(list.size()); + int from = (pageNum - 1) * pageSize; + if (from >= list.size()) + { + return page; + } + int to = Math.min(from + pageSize, list.size()); + page.setRows(new ArrayList(list.subList(from, to))); + return page; + } + + private List loadAllPriceChanges(Long psvId, String priceKind, String status, int max) + { + List all = new ArrayList(); + int batch = 1000; + int pageNum = 1; + while (all.size() < max) + { + int size = Math.min(batch, max - all.size()); + BillSearchPage chunk = search(EsLogType.PRICE_CHANGE, null, status, psvId, priceKind, null, null, + pageNum, size); + List rows = chunk.getRows(); + if (rows == null || rows.isEmpty()) + { + break; + } + all.addAll(rows); + if (rows.size() < size || all.size() >= chunk.getTotal()) + { + break; + } + pageNum++; + } + return all; + } + + private JSONObject parseRequestObject(String requestJson) + { + if (StringUtils.isEmpty(requestJson)) + { + return null; + } + try + { + Object parsed = JSON.parse(requestJson); + return parsed instanceof JSONObject ? (JSONObject) parsed : null; + } + catch (Exception e) + { + return null; + } + } + private BillSyncSnapshot toSnapshot(JSONObject source, String id) { if (source == null) @@ -249,12 +377,18 @@ public class EsBillSearchStore implements IBillSearchStore return snap; } - private JSONObject buildQuery(String logType, String bizId, String status) + private JSONObject buildQuery(String logType, String bizId, String status, Long psvId, String priceKind, + String productCode, String updateTime, String updateTimePrefix) { JSONArray filters = new JSONArray(); addTerm(filters, "logType", logType); addWildcard(filters, "bizId", bizId); addTerm(filters, "status", status); + addPsvIdFilter(filters, psvId); + addTerm(filters, "request.priceKind", priceKind); + addProductCodeFilter(filters, productCode); + addUpdateTimeFilter(filters, updateTime); + addUpdateTimePrefixFilter(filters, updateTimePrefix); if (filters.isEmpty()) { JSONObject matchAll = new JSONObject(); @@ -268,6 +402,136 @@ public class EsBillSearchStore implements IBillSearchStore return query; } + private void addProductCodeFilter(JSONArray filters, String productCode) + { + if (StringUtils.isEmpty(productCode)) + { + return; + } + String code = productCode.trim(); + JSONArray should = new JSONArray(); + JSONObject termKw = new JSONObject(); + termKw.put("request.bizKeys.productCode.keyword", code); + JSONObject wrapKw = new JSONObject(); + wrapKw.put("term", termKw); + should.add(wrapKw); + + JSONObject term = new JSONObject(); + term.put("request.bizKeys.productCode", code); + JSONObject wrap = new JSONObject(); + wrap.put("term", term); + should.add(wrap); + + JSONObject inner = new JSONObject(); + inner.put("should", should); + inner.put("minimum_should_match", Integer.valueOf(1)); + JSONObject boolWrap = new JSONObject(); + boolWrap.put("bool", inner); + filters.add(boolWrap); + } + + private void addUpdateTimeFilter(JSONArray filters, String updateTime) + { + if (StringUtils.isEmpty(updateTime)) + { + return; + } + String time = updateTime.trim(); + JSONArray should = new JSONArray(); + JSONObject t1 = new JSONObject(); + t1.put("updateTime.keyword", time); + JSONObject w1 = new JSONObject(); + w1.put("term", t1); + should.add(w1); + + JSONObject t2 = new JSONObject(); + t2.put("updatedAt.keyword", time); + JSONObject w2 = new JSONObject(); + w2.put("term", t2); + should.add(w2); + + JSONObject t3 = new JSONObject(); + t3.put("updateTime", time); + JSONObject w3 = new JSONObject(); + w3.put("term", t3); + should.add(w3); + + JSONObject t4 = new JSONObject(); + t4.put("updatedAt", time); + JSONObject w4 = new JSONObject(); + w4.put("term", t4); + should.add(w4); + + JSONObject inner = new JSONObject(); + inner.put("should", should); + inner.put("minimum_should_match", Integer.valueOf(1)); + JSONObject wrap = new JSONObject(); + wrap.put("bool", inner); + filters.add(wrap); + } + + /** Match write-time by minute prefix, e.g. 2026-11-05 13:27. */ + private void addUpdateTimePrefixFilter(JSONArray filters, String updateTimePrefix) + { + if (StringUtils.isEmpty(updateTimePrefix)) + { + return; + } + String prefix = escapeWildcard(updateTimePrefix.trim()) + "*"; + JSONArray should = new JSONArray(); + for (String field : new String[] { "updateTime.keyword", "updatedAt.keyword", "updateTime", "updatedAt" }) + { + JSONObject opts = new JSONObject(); + opts.put("value", prefix); + opts.put("case_insensitive", Boolean.TRUE); + JSONObject wildcard = new JSONObject(); + wildcard.put(field, opts); + JSONObject wrap = new JSONObject(); + wrap.put("wildcard", wildcard); + should.add(wrap); + } + JSONObject inner = new JSONObject(); + inner.put("should", should); + inner.put("minimum_should_match", Integer.valueOf(1)); + JSONObject wrap = new JSONObject(); + wrap.put("bool", inner); + filters.add(wrap); + } + + /** Prefer numeric term; also accept keyword/string mapping of bizKeys.psvId. */ + private void addPsvIdFilter(JSONArray filters, Long psvId) + { + if (psvId == null) + { + return; + } + JSONArray should = new JSONArray(); + JSONObject termNum = new JSONObject(); + termNum.put("request.bizKeys.psvId", psvId); + JSONObject wrapNum = new JSONObject(); + wrapNum.put("term", termNum); + should.add(wrapNum); + + JSONObject termKw = new JSONObject(); + termKw.put("request.bizKeys.psvId.keyword", String.valueOf(psvId)); + JSONObject wrapKw = new JSONObject(); + wrapKw.put("term", termKw); + should.add(wrapKw); + + JSONObject termStr = new JSONObject(); + termStr.put("request.bizKeys.psvId", String.valueOf(psvId)); + JSONObject wrapStr = new JSONObject(); + wrapStr.put("term", termStr); + should.add(wrapStr); + + JSONObject inner = new JSONObject(); + inner.put("should", should); + inner.put("minimum_should_match", Integer.valueOf(1)); + JSONObject wrap = new JSONObject(); + wrap.put("bool", inner); + filters.add(wrap); + } + private void addTerm(JSONArray filters, String field, String value) { if (StringUtils.isEmpty(value)) diff --git a/bms-back/bms-system/src/main/java/com/bms/price/search/IBillSearchStore.java b/bms-back/bms-system/src/main/java/com/bms/price/search/IBillSearchStore.java index fc162d07..e5294582 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/search/IBillSearchStore.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/search/IBillSearchStore.java @@ -17,4 +17,37 @@ public interface IBillSearchStore BillSyncSnapshot getByDocId(String docId); BillSearchPage search(String logType, String bizId, String status, int pageNum, int pageSize); + + /** + * Search with optional sales-version / priceKind filters (request.bizKeys.psvId, request.priceKind). + */ + default BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind, + int pageNum, int pageSize) + { + return search(logType, bizId, status, psvId, priceKind, null, null, pageNum, pageSize); + } + + /** + * Search with optional productCode + updateTime (exact) / updateTimePrefix (e.g. yyyy-MM-dd HH:mm) filters. + */ + default BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind, + String productCode, String updateTime, int pageNum, int pageSize) + { + return search(logType, bizId, status, psvId, priceKind, productCode, updateTime, null, pageNum, pageSize); + } + + default BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind, + String productCode, String updateTime, String updateTimePrefix, int pageNum, int pageSize) + { + return search(logType, bizId, status, pageNum, pageSize); + } + + /** + * Group PRICE_CHANGE logs by productCode + updateTime for one package version. + */ + default PriceChangeGroupPage searchPriceChangeGroups(Long psvId, String priceKind, String status, + int pageNum, int pageSize) + { + return new PriceChangeGroupPage(); + } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeEsLog.java b/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeEsLog.java index 954cb482..b363a165 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeEsLog.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeEsLog.java @@ -25,12 +25,33 @@ public final class PriceChangeEsLog public static final String ACTION_DELETE = "DELETE"; public static final String ACTION_IMPORT = "IMPORT"; + /** Async import workers have no SecurityContext; pass operName via this override. */ + private static final ThreadLocal OPERATOR_OVERRIDE = new ThreadLocal(); + private PriceChangeEsLog() { } + public static void beginOperator(String operName) + { + if (StringUtils.isNotEmpty(operName)) + { + OPERATOR_OVERRIDE.set(operName.trim()); + } + } + + public static void endOperator() + { + OPERATOR_OVERRIDE.remove(); + } + public static String operator() { + String override = OPERATOR_OVERRIDE.get(); + if (StringUtils.isNotEmpty(override)) + { + return override; + } try { String name = SecurityUtils.getUsername(); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroup.java b/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroup.java new file mode 100644 index 00000000..d059db8c --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroup.java @@ -0,0 +1,74 @@ +package com.bms.price.search; + +/** + * Price-change log group key: productCode + write time. + */ +public class PriceChangeGroup +{ + private String productCode; + private String updateTime; + private String action; + private String operator; + private String status; + private int changeCount; + + public String getProductCode() + { + return productCode; + } + + public void setProductCode(String productCode) + { + this.productCode = productCode; + } + + public String getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(String updateTime) + { + this.updateTime = updateTime; + } + + public String getAction() + { + return action; + } + + public void setAction(String action) + { + this.action = action; + } + + public String getOperator() + { + return operator; + } + + public void setOperator(String operator) + { + this.operator = operator; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public int getChangeCount() + { + return changeCount; + } + + public void setChangeCount(int changeCount) + { + this.changeCount = changeCount; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroupPage.java b/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroupPage.java new file mode 100644 index 00000000..ce5159e1 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/search/PriceChangeGroupPage.java @@ -0,0 +1,31 @@ +package com.bms.price.search; + +import java.util.ArrayList; +import java.util.List; + +/** Paginated product+time groups of price-change logs. */ +public class PriceChangeGroupPage +{ + private long total; + private List rows = new ArrayList(); + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows == null ? new ArrayList() : rows; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java index 041cf63a..e5d908c9 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java @@ -1582,6 +1582,23 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService @Transactional(rollbackFor = Exception.class) public String importDetailPrices(Long priceSetId, Long psvId, byte[] bytes, String filename, String operName, String taskId, boolean updateOnly, boolean replaceByProductCountry, String countryCode) throws Exception + { + // Async import has no SecurityContext; bind operName for PriceChangeEsLog.operator(). + PriceChangeEsLog.beginOperator(operName); + try + { + return importDetailPricesInOperator(priceSetId, psvId, bytes, filename, operName, taskId, updateOnly, + replaceByProductCountry, countryCode); + } + finally + { + PriceChangeEsLog.endOperator(); + } + } + + private String importDetailPricesInOperator(Long priceSetId, Long psvId, byte[] bytes, String filename, + String operName, String taskId, boolean updateOnly, boolean replaceByProductCountry, String countryCode) + throws Exception { PrcPriceSet db = requireSet(priceSetId); assertChildEditable(db); diff --git a/bms-front/src/api/monitor/priceLog.js b/bms-front/src/api/monitor/priceLog.js index 5e5accca..309aff4e 100644 --- a/bms-front/src/api/monitor/priceLog.js +++ b/bms-front/src/api/monitor/priceLog.js @@ -8,6 +8,15 @@ export function listPriceLog(query) { }) } +/** Group by productCode + updateTime for a package version. */ +export function listPriceLogGroups(query) { + return request({ + url: '/monitor/priceLog/groups', + method: 'get', + params: query + }) +} + export function getPriceLog(docId) { return request({ url: '/monitor/priceLog/' + encodeURIComponent(docId), diff --git a/bms-front/src/views/price/set/components/detail/SetVersionPriceLogDrawer.vue b/bms-front/src/views/price/set/components/detail/SetVersionPriceLogDrawer.vue new file mode 100644 index 00000000..fb6c2970 --- /dev/null +++ b/bms-front/src/views/price/set/components/detail/SetVersionPriceLogDrawer.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/bms-front/src/views/price/set/components/detail/SetVersionTable.vue b/bms-front/src/views/price/set/components/detail/SetVersionTable.vue index e569bf64..999478af 100644 --- a/bms-front/src/views/price/set/components/detail/SetVersionTable.vue +++ b/bms-front/src/views/price/set/components/detail/SetVersionTable.vue @@ -35,10 +35,16 @@ - + - - From a2e986fa6ab9cde0b68e12b04d3b26d803aa0697 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 6 Nov 2026 10:46:58 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E5=9B=9E=E9=80=80=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=94=B6=E8=B4=A7=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../price/oracle/TabOrderMonitorService.java | 8 ++------ .../price/OracleUpstreamOrderMapper.xml | 18 ++--------------- .../src/views/monitor/orderSource/index.vue | 20 +++---------------- 3 files changed, 7 insertions(+), 39 deletions(-) diff --git a/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java b/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java index 1679feb8..41556ed9 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java @@ -1232,14 +1232,10 @@ public class TabOrderMonitorService query.setOrderStatuses(cleaned.isEmpty() ? new ArrayList(DEFAULT_ORDER_STATUSES) : cleaned); } - /** Empty or anything other than 0/1 falls back to 1(已收货). */ + /** Always force BL_PICKUP=1; UI no longer exposes this filter. */ private static void normalizeBlPickup(TabOrderMonitorQuery query) { - Integer blPickup = query.getBlPickup(); - if (blPickup == null || (blPickup.intValue() != 0 && blPickup.intValue() != 1)) - { - query.setBlPickup(Integer.valueOf(1)); - } + query.setBlPickup(Integer.valueOf(1)); } /** diff --git a/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml index 9f8e7b6c..33d3e605 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml @@ -66,14 +66,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND o.PICKUP_DATE <= TO_DATE(#{query.endTime}, 'YYYY-MM-DD HH24:MI:SS') - - - AND o.BL_PICKUP = #{query.blPickup} - - - - - + AND o.BL_PICKUP = 1 AND o.ORDER_STATUS IN @@ -281,14 +274,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{bid} - - - AND o.BL_PICKUP = #{blPickup} - - - - - + AND o.BL_PICKUP = 1 AND o.ORDER_STATUS IN (3, 4, 5, 6, 7, 8, 11) AND o.CUSTOMER_CODE IN diff --git a/bms-front/src/views/monitor/orderSource/index.vue b/bms-front/src/views/monitor/orderSource/index.vue index 74a44595..7020a2ae 100644 --- a/bms-front/src/views/monitor/orderSource/index.vue +++ b/bms-front/src/views/monitor/orderSource/index.vue @@ -191,14 +191,8 @@ const orderStatusMap = { const orderStatusOptions = Object.keys(orderStatusMap).map(k => ({ label: orderStatusMap[k], value: Number(k) })) -const blPickupOptions = [ - { label: '已收货', value: 1 }, - { label: '未收货', value: 0 } -] - const defaultParams = { - orderStatuses: [3, 4, 5, 6, 7, 8, 11], - blPickup: 1 + orderStatuses: [3, 4, 5, 6, 7, 8, 11] } const searchColumns = [ @@ -248,14 +242,7 @@ const searchColumns = [ collapseTags: true, collapseTagsTooltip: true } - }, - { - label: '收货状态', - prop: 'blPickup', - component: 'select', - options: blPickupOptions, - placeholder: '请选择' - }, + } ] const columns = [ @@ -402,7 +389,6 @@ function queryList(params) { const customers = splitLines(src.customerKeywords) const fillQuery = { orderIdentifiers: identifiers } if (customers.length) fillQuery.customerKeywords = customers - fillQuery.blPickup = src.blPickup === 0 || src.blPickup === 1 ? src.blPickup : 1 fillQuery.pageNum = params.pageNum fillQuery.pageSize = params.pageSize return listOrderSourceByTransferNos(fillQuery) @@ -435,7 +421,7 @@ function openOrderCompare() { return } proxy.$modal.confirm( - '将按当前客户、提货时间、订单状态和收货状态筛选,异步分页比对上游转单号是否已落库,确认继续?', + '将按当前客户、提货时间和订单状态筛选,异步分页比对上游转单号是否已落库,确认继续?', '二次确认' ).then(() => { // 比对只按客户+时间;不带单号框内容(回填后可能有数千转单号,带上会把比对拖死) From 709c315971f74f02867dac2d93a1c678957f35e0 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 6 Nov 2026 16:13:03 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AE=A1=E8=B4=B9?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=94=B6=E8=B4=A7=E6=97=B6=E9=97=B4=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/BatchAddIncomeBillFeeRequest.java | 4 + .../vo/ChargeWeightTemplateRequest.java | 3 + .../vo/PrcBillingBillListExportRow.java | 2 +- .../domain/vo/RecalcBillPriceRequest.java | 3 + .../bms/price/search/EsBillSearchStore.java | 5 - .../service/impl/PrcOrderBillServiceImpl.java | 33 +- bms-front/src/utils/excelExport.js | 30 +- bms-front/src/utils/exportFieldDefs.js | 2 +- bms-front/src/views/price/billing/bill.vue | 299 ++++++++++++++++-- bms-front/src/views/price/orderBill/index.vue | 188 ++++++++++- 10 files changed, 517 insertions(+), 52 deletions(-) diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeeRequest.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeeRequest.java index 511ee24e..58ddd1a4 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeeRequest.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeeRequest.java @@ -40,6 +40,10 @@ public class BatchAddIncomeBillFeeRequest implements Serializable private String beginBizDate; private String endBizDate; + /** Receive/pickup time range (pickup_date), inclusive. */ + private String beginPickupDate; + private String endPickupDate; + /** Surcharge config id (prc_surcharge.sc_id). */ @NotNull(message = "scId required") private Long scId; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/ChargeWeightTemplateRequest.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/ChargeWeightTemplateRequest.java index d6fc692a..f718ccb1 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/ChargeWeightTemplateRequest.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/ChargeWeightTemplateRequest.java @@ -25,4 +25,7 @@ public class ChargeWeightTemplateRequest implements Serializable private List orderStatuses; private String beginBizDate; private String endBizDate; + /** Receive/pickup time range (pickup_date), inclusive. */ + private String beginPickupDate; + private String endPickupDate; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcBillingBillListExportRow.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcBillingBillListExportRow.java index 4919e8e0..457b70ea 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcBillingBillListExportRow.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcBillingBillListExportRow.java @@ -56,7 +56,7 @@ public class PrcBillingBillListExportRow implements Serializable @Excel(name = "计费时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date bizDate; - @Excel(name = "提货时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "收货时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date pickupDate; @Excel(name = "销售员", width = 12) diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/RecalcBillPriceRequest.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/RecalcBillPriceRequest.java index 0f398f58..9a36c6dd 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/RecalcBillPriceRequest.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/RecalcBillPriceRequest.java @@ -31,4 +31,7 @@ public class RecalcBillPriceRequest implements Serializable private List billingFailStatuses; private String beginBizDate; private String endBizDate; + /** Receive/pickup time range (pickup_date), inclusive. */ + private String beginPickupDate; + private String endPickupDate; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java b/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java index 1259609e..52f99f25 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/search/EsBillSearchStore.java @@ -630,11 +630,6 @@ public class EsBillSearchStore implements IBillSearchStore filters.add(wrap); } - private String escapeWildcard(String value) - { - return value.replace("\\", "\\\\").replace("*", "\\*").replace("?", "\\?"); - } - private long readTotal(Object total) { if (total instanceof JSONObject) 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 f1617014..8614f22d 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 @@ -681,6 +681,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService request.getCustomerCodes(), request.getProductCodes(), request.getCountryCode(), request.getCreditArrearsStatus(), request.getOrderStatuses(), request.getBillingFailStatuses(), request.getBeginBizDate(), request.getEndBizDate(), + request.getBeginPickupDate(), request.getEndPickupDate(), "price.bill.recalc.filterRequired", "price.bill.recalc.scopeRequired", "price.bill.recalc.selectedRequired"); if (bills.isEmpty()) @@ -1276,6 +1277,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService request.getCustomerCodes(), request.getProductCodes(), request.getCountryCode(), request.getCreditArrearsStatus(), request.getOrderStatuses(), request.getBillingFailStatuses(), request.getBeginBizDate(), request.getEndBizDate(), + request.getBeginPickupDate(), request.getEndPickupDate(), "price.bill.fee.batch.filterRequired", "price.bill.fee.batch.scopeRequired", "price.bill.fee.batch.selectedRequired"); } @@ -1290,6 +1292,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService List customerCodesRaw, List productCodesRaw, String countryCodeRaw, String creditArrearsStatusRaw, List orderStatusesRaw, List billingFailStatusesRaw, String beginBizDateRaw, String endBizDateRaw, + String beginPickupDateRaw, String endPickupDateRaw, String msgFilter, String msgScope, String msgSelected) { String scopeMode = StringUtils.nvl(scopeModeRaw, "").trim().toUpperCase(); @@ -1311,6 +1314,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService : countryCodeRaw.trim().toUpperCase(); String beginBizDate = StringUtils.isEmpty(beginBizDateRaw) ? null : beginBizDateRaw.trim(); String endBizDate = StringUtils.isEmpty(endBizDateRaw) ? null : endBizDateRaw.trim(); + String beginPickupDate = StringUtils.isEmpty(beginPickupDateRaw) ? null : beginPickupDateRaw.trim(); + String endPickupDate = StringUtils.isEmpty(endPickupDateRaw) ? null : endPickupDateRaw.trim(); boolean hasFilter = (orderNos != null && !orderNos.isEmpty()) || (trackingNos != null && !trackingNos.isEmpty()) || (transferNos != null && !transferNos.isEmpty()) @@ -1321,7 +1326,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService || StringUtils.isNotEmpty(creditArrearsStatus) || StringUtils.isNotEmpty(countryCode) || StringUtils.isNotEmpty(beginBizDate) - || StringUtils.isNotEmpty(endBizDate); + || StringUtils.isNotEmpty(endBizDate) + || StringUtils.isNotEmpty(beginPickupDate) + || StringUtils.isNotEmpty(endPickupDate); if (!hasFilter) { throw new ServiceException(MessageUtils.message(msgFilter)); @@ -1356,6 +1363,14 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService { query.getParams().put("endBizDate", endBizDate); } + if (beginPickupDate != null) + { + query.getParams().put("beginPickupDate", beginPickupDate); + } + if (endPickupDate != null) + { + query.getParams().put("endPickupDate", endPickupDate); + } } List list = self.selectBillList(query); return list == null ? new ArrayList() : list; @@ -2754,6 +2769,10 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService : request.getBeginBizDate().trim(); String endBizDate = StringUtils.isEmpty(request.getEndBizDate()) ? null : request.getEndBizDate().trim(); + String beginPickupDate = StringUtils.isEmpty(request.getBeginPickupDate()) ? null + : request.getBeginPickupDate().trim(); + String endPickupDate = StringUtils.isEmpty(request.getEndPickupDate()) ? null + : request.getEndPickupDate().trim(); boolean hasFilter = (orderNos != null && !orderNos.isEmpty()) || (trackingNos != null && !trackingNos.isEmpty()) || (transferNos != null && !transferNos.isEmpty()) @@ -2762,7 +2781,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService || (productCodes != null && !productCodes.isEmpty()) || StringUtils.isNotEmpty(creditArrearsStatus) || StringUtils.isNotEmpty(beginBizDate) - || StringUtils.isNotEmpty(endBizDate); + || StringUtils.isNotEmpty(endBizDate) + || StringUtils.isNotEmpty(beginPickupDate) + || StringUtils.isNotEmpty(endPickupDate); if (!hasFilter) { throw new ServiceException("请先设置列表筛选条件"); @@ -2784,6 +2805,14 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService { query.getParams().put("endBizDate", endBizDate); } + if (beginPickupDate != null) + { + query.getParams().put("beginPickupDate", beginPickupDate); + } + if (endPickupDate != null) + { + query.getParams().put("endPickupDate", endPickupDate); + } List list = self.selectBillList(query); if (list == null || list.isEmpty()) { diff --git a/bms-front/src/utils/excelExport.js b/bms-front/src/utils/excelExport.js index 4ca5508e..12bcd0c8 100644 --- a/bms-front/src/utils/excelExport.js +++ b/bms-front/src/utils/excelExport.js @@ -123,11 +123,13 @@ function taskDone(task, options) { * @param {string} [options.filePrefix='export'] * @param {string} [options.loadingText] * @param {(task: object) => void} [options.onProgress] - * @param {number} [options.pollIntervalMs=800] + * @param {boolean} [options.showLoading=true] full-screen ElLoading; set false when page shows its own float progress + * @param {number} [options.pollIntervalMs=1000] * @param {number} [options.maxWaitMs] * @param {string[]} [options.successStatus] * @param {string[]} [options.failStatus] * @param {boolean} [options.showSuccessMessage=true] + * @param {boolean} [options.showErrorMessage=true] */ export async function exportAsync(options = {}) { const { submitUrl, taskUrlPrefix, downloadUrlPrefix } = resolveAsyncUrls(options) @@ -152,11 +154,15 @@ export async function exportAsync(options = {}) { const filePrefix = options.filePrefix || 'export' const loadingText = options.loadingText || '\u6b63\u5728\u5bfc\u51fa\uff0c\u8bf7\u7a0d\u5019' - const pollIntervalMs = options.pollIntervalMs || 800 + const pollIntervalMs = options.pollIntervalMs || 1000 const maxWaitMs = options.maxWaitMs || 30 * 60 * 1000 const showSuccessMessage = options.showSuccessMessage !== false + const showErrorMessage = options.showErrorMessage !== false + const useLoading = options.showLoading !== false - const loading = ElLoading.service({ text: loadingText, background: 'rgba(0, 0, 0, 0.7)' }) + const loading = useLoading + ? ElLoading.service({ text: loadingText, background: 'rgba(0, 0, 0, 0.7)' }) + : null try { const submitRes = await request({ url: submitUrl, @@ -167,15 +173,23 @@ export async function exportAsync(options = {}) { }) const taskId = pickTaskId(submitRes && submitRes.data) if (!taskId) throw new Error('missing export taskId') + if (typeof options.onTaskId === 'function') options.onTaskId(String(taskId)) const started = Date.now() let task = null + let lastSig = '' while (Date.now() - started < maxWaitMs) { + const hidden = typeof document !== 'undefined' && document.visibilityState === 'hidden' const res = await request({ url: taskUrlPrefix + String(taskId), method: 'get' }) task = res.data || {} - if (typeof options.onProgress === 'function') options.onProgress(task) const pct = Number(task.percent) - if (!Number.isNaN(pct) && pct >= 0) { + const st = String(task.status || task.exportStatus || '').toUpperCase() + const sig = [st, pct, task.message, task.rowCount].join('|') + if (typeof options.onProgress === 'function' && sig !== lastSig) { + lastSig = sig + options.onProgress(task) + } + if (loading && !Number.isNaN(pct) && pct >= 0) { loading.setText(loadingText + ' ' + Math.min(100, pct) + '%') } const state = taskDone(task, options) @@ -183,7 +197,7 @@ export async function exportAsync(options = {}) { if (state === 'FAIL') { throw new Error(task.error || task.remark || task.message || 'export failed') } - await sleep(pollIntervalMs) + await sleep(hidden ? 2500 : pollIntervalMs) } if (!task || taskDone(task, options) !== 'SUCCESS') { throw new Error('export timeout') @@ -204,10 +218,10 @@ export async function exportAsync(options = {}) { if (showSuccessMessage) ElMessage.success('\u5bfc\u51fa\u6210\u529f') return { task, taskId } } catch (e) { - ElMessage.error((e && e.message) || '\u5bfc\u51fa\u5931\u8d25') + if (showErrorMessage) ElMessage.error((e && e.message) || '\u5bfc\u51fa\u5931\u8d25') return null } finally { - loading.close() + if (loading) loading.close() } } diff --git a/bms-front/src/utils/exportFieldDefs.js b/bms-front/src/utils/exportFieldDefs.js index a710d035..0603df73 100644 --- a/bms-front/src/utils/exportFieldDefs.js +++ b/bms-front/src/utils/exportFieldDefs.js @@ -68,7 +68,7 @@ export const BILLING_BILL_EXPORT_FIELDS = [ { prop: 'customerActiveStatusLabel', label: '客户状态' }, { prop: 'creditArrearsStatusLabel', label: '额度欠费' }, { prop: 'bizDate', label: '计费时间' }, - { prop: 'pickupDate', label: '提货时间' }, + { prop: 'pickupDate', label: '收货时间' }, { prop: 'belongSaler', label: '销售员' }, { prop: 'settlementMan', label: '结算员' }, { prop: 'serviceMan', label: '客服员' } diff --git a/bms-front/src/views/price/billing/bill.vue b/bms-front/src/views/price/billing/bill.vue index 96b9de3a..7052c865 100644 --- a/bms-front/src/views/price/billing/bill.vue +++ b/bms-front/src/views/price/billing/bill.vue @@ -1,5 +1,20 @@ + + +