From a115926e9fc62d0411d29ab7bd40bdc4c6437aef Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Mon, 12 Oct 2026 14:44:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AE=A1=E8=B4=B9=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bms/price/domain/PrcQuote.java | 3 +- .../price/service/ITmsPriceQuoteService.java | 2 +- .../impl/TmsPriceQuoteServiceImpl.java | 55 +++++++------------ bms-back/sql/bms_init.sql | 4 +- .../prc_quote_biz_date_datetime_alter.sql | 4 ++ bms-front/src/views/price/billing/quote.vue | 15 +++-- 6 files changed, 39 insertions(+), 44 deletions(-) create mode 100644 bms-back/sql/price/prc_quote_biz_date_datetime_alter.sql diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcQuote.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcQuote.java index 0f3b45d4..1b33b592 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcQuote.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcQuote.java @@ -26,7 +26,8 @@ public class PrcQuote extends BaseEntity private String countryName; private String destPostcode; private BigDecimal weight; - @JsonFormat(pattern = "yyyy-MM-dd") + /** 计费时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date bizDate; private Long tmsId; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/ITmsPriceQuoteService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/ITmsPriceQuoteService.java index 3ca2e4b7..b554a882 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/ITmsPriceQuoteService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/ITmsPriceQuoteService.java @@ -12,7 +12,7 @@ import java.util.List; * * Match order: * 1) Customer binds (package not stopped), bind_priority ASC; bind date range is not used for billing - * 2) Active package version at bill-time end-of-day (version effective_time / expire_time) + * 2) Active package version at bill datetime (version effective_time / expire_time) * 3) FIXED detail override, else DISCOUNT on that version * 4) Else product default only when a version existed but detail missed * Package hit uses version surcharge snapshot when present; else product library. diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java index 5daaee76..25d2fe4a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -240,8 +240,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { return Collections.emptyList(); } - Date day = bizDate != null ? bizDate : new Date(); - Date asOf = endOfDay(day); + Date asOf = bizDate != null ? bizDate : new Date(); for (PrcCustomerPriceSet bind : binds) { if (bind == null || bind.getPriceSetId() == null) @@ -355,7 +354,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService boolean productExcludedBySetProducts = false; boolean attemptedPriceMatch = false; boolean anyZoneRequired = false; - Date asOf = endOfDay(bizDate); + // Match version by exact bill datetime (effective_time / expire_time), not end-of-day. + Date asOf = bizDate != null ? bizDate : new Date(); for (PrcCustomerPriceSet bind : binds) { PrcPriceSet set = priceSetMapper.selectPrcPriceSetById(bind.getPriceSetId()); @@ -406,7 +406,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService if (TYPE_FIXED.equals(setType)) { TmsPriceQuoteResult hit = tryFixed(bind, set, version, catalog, zone.zone, - productCode, productName, cc, cn, zone.zoneCode, weight); + productCode, productName, cc, cn, zone.zoneCode, weight, bizDate); if (hit != null) { stampRequestMeta(request, hit); @@ -419,7 +419,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { continue; } - TmsPriceQuoteResult hit = tryDiscount(bind, set, version, catalog, productCode, productName, weight); + TmsPriceQuoteResult hit = tryDiscount(bind, set, version, catalog, productCode, productName, weight, bizDate); if (hit != null) { stampRequestMeta(request, hit); @@ -691,7 +691,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService private TmsPriceQuoteResult tryFixed(PrcCustomerPriceSet bind, PrcPriceSet set, PrcPriceSetVersion version, BmsProductCountry catalog, PrcZone resolvedZone, String productCode, String productName, - String countryCode, String countryName, String zoneCode, BigDecimal weight) + String countryCode, String countryName, String zoneCode, BigDecimal weight, Date bizDate) { Long psvId = version.getPsvId(); int detailCount = detailMapper.countByPsvId(psvId); @@ -713,7 +713,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService return null; } TmsPriceQuoteResult r = buildFromPackageDetail(SRC_FIXED, detail, catalog, resolvedZone, - productCode, productName, weight, detail.getFvValue(), null, null, detail.getFvRegistrationFee()); + productCode, productName, weight, detail.getFvValue(), null, null, detail.getFvRegistrationFee(), bizDate); fillPackage(r, bind, set); stampVersion(r, version, detail.getPsdId()); r.setMatchMessage(MessageUtils.message("price.quote.hitCustomerFixedPackageDetail") @@ -723,7 +723,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService } private TmsPriceQuoteResult tryDiscount(PrcCustomerPriceSet bind, PrcPriceSet set, PrcPriceSetVersion version, - BmsProductCountry base, String productCode, String productName, BigDecimal weight) + BmsProductCountry base, String productCode, String productName, BigDecimal weight, Date bizDate) { Long psvId = version.getPsvId(); int detailCount = detailMapper.countByPsvId(psvId); @@ -760,7 +760,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService BigDecimal rate = baseRate.multiply(discount) .divide(new BigDecimal("100"), 2, rm); TmsPriceQuoteResult r = buildFromRates(SRC_DISCOUNT, rate, baseRate, discount, - base.getRegistrationFee(), base, productCode, productName, weight, roundMode); + base.getRegistrationFee(), base, productCode, productName, weight, roundMode, bizDate); fillPackage(r, bind, set); stampVersion(r, version, psdId); r.setMatchMessage("hit customer DISCOUNT package version " @@ -787,7 +787,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService private TmsPriceQuoteResult buildFromPackageDetail(String source, PrcPriceSetDetail detail, BmsProductCountry catalog, PrcZone resolvedZone, String productCode, String productName, BigDecimal weight, BigDecimal freightRate, BigDecimal baseFreightRate, BigDecimal discountPercent, - BigDecimal registrationFee) + BigDecimal registrationFee, Date bizDate) { if (freightRate == null) { @@ -879,15 +879,16 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService r.setCurrency("CNY"); r.setRoundMode(mode); r.setFromCache(false); + r.setBizDate(bizDate); fillProductAmountAndPay(r, catalog, weight, rm, mode); - fillRedlineAmount(r, weight, rm); + fillRedlineAmount(r, weight, rm, bizDate); return r; } private TmsPriceQuoteResult buildFromRates(String source, BigDecimal freightRate, BigDecimal baseFreightRate, BigDecimal discountPercent, BigDecimal registrationFee, BmsProductCountry base, String productCode, String productName, BigDecimal weight, - String roundMode) + String roundMode, Date bizDate) { if (freightRate == null) { @@ -935,8 +936,9 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService r.setCurrency("CNY"); r.setRoundMode(mode); r.setFromCache(false); + r.setBizDate(bizDate); fillProductAmountAndPay(r, base, weight, rm, mode); - fillRedlineAmount(r, weight, rm); + fillRedlineAmount(r, weight, rm, bizDate); return r; } @@ -971,16 +973,17 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService } /** - * Redline catalog amount from redline_product_country (same match keys as product catalog). + * Redline catalog amount from redline_product_country + * (same match keys as product catalog, including bill datetime). */ - private void fillRedlineAmount(TmsPriceQuoteResult r, BigDecimal weight, RoundingMode rm) + private void fillRedlineAmount(TmsPriceQuoteResult r, BigDecimal weight, RoundingMode rm, Date bizDate) { if (r == null || weight == null || StringUtils.isEmpty(r.getProductCode())) { return; } BmsRedlineProductCountry base = redlineProductCountryMapper.selectMatchByProductCountryWeight( - r.getProductCode(), r.getCountryCode(), r.getCountryName(), r.getZoneCode(), weight, null); + r.getProductCode(), r.getCountryCode(), r.getCountryName(), r.getZoneCode(), weight, bizDate); if (base == null || base.getFreightPrice() == null) { return; @@ -1469,7 +1472,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService countryCode = countryCode.toUpperCase(); } String countryName = emptyToNull(request.getCountryName()); - String biz = new SimpleDateFormat("yyyy-MM-dd").format(bizDate); + String biz = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(bizDate); String raw = request.getCustomerCode().trim() + "|" + request.getProductCode().trim() + "|" + nvlStr(countryCode) + "|" @@ -1639,24 +1642,6 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService return parsed; } - /** Use end of bill day so same-day version activate still matches that day's quote. */ - private static Date endOfDay(Date day) - { - if (day == null) - { - return new Date(); - } - try - { - String d = new SimpleDateFormat("yyyy-MM-dd").format(day); - return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(d + " 23:59:59"); - } - catch (ParseException e) - { - return day; - } - } - private String resolveProductName(String productCode, Date bizDate) { BmsProduct p = productMapper.selectEffectiveBmsProductByCode(productCode, bizDate); diff --git a/bms-back/sql/bms_init.sql b/bms-back/sql/bms_init.sql index c66c5924..c74803b9 100644 --- a/bms-back/sql/bms_init.sql +++ b/bms-back/sql/bms_init.sql @@ -1427,7 +1427,7 @@ CREATE TABLE `prc_price_set_product` ( -- ---------------------------- CREATE TABLE `prc_quote` ( `quote_id` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL, - `quote_key` char(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '试算指纹 MD5(客户|产品|国家|重量|计费日期)', + `quote_key` char(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '试算指纹 MD5(客户|产品|国家|重量|计费时间)', `customer_id` bigint(20) NOT NULL COMMENT '客户ID', `customer_code` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'customer code', `product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品代码', @@ -1437,7 +1437,7 @@ CREATE TABLE `prc_quote` ( `dest_postcode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'dest postcode', `quote_postcode_key` varchar(20) COLLATE utf8mb4_unicode_ci GENERATED ALWAYS AS (ifnull(`dest_postcode`,'')) STORED COMMENT 'dest_postcode for unique key', `weight` decimal(10,3) NOT NULL COMMENT '计费重量KG', - `biz_date` date NOT NULL COMMENT '计费日期', + `biz_date` datetime NOT NULL COMMENT '计费时间', `tms_id` bigint(20) DEFAULT NULL COMMENT '多租户系统ID', `match_source` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '命中来源 PACKAGE_FIXED/套餐固定价 PACKAGE_DISCOUNT/套餐折扣 PRODUCT_DEFAULT/产品默认价', `match_message` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '命中说明', diff --git a/bms-back/sql/price/prc_quote_biz_date_datetime_alter.sql b/bms-back/sql/price/prc_quote_biz_date_datetime_alter.sql new file mode 100644 index 00000000..59e1f376 --- /dev/null +++ b/bms-back/sql/price/prc_quote_biz_date_datetime_alter.sql @@ -0,0 +1,4 @@ +-- prc_quote.biz_date: date -> datetime (same-day different bill times) +ALTER TABLE prc_quote + MODIFY COLUMN `biz_date` datetime NOT NULL COMMENT '计费时间', + MODIFY COLUMN `quote_key` char(32) NOT NULL COMMENT 'MD5(customer|product|country|weight|biz_datetime)'; diff --git a/bms-front/src/views/price/billing/quote.vue b/bms-front/src/views/price/billing/quote.vue index c83b45b7..9c91cf57 100644 --- a/bms-front/src/views/price/billing/quote.vue +++ b/bms-front/src/views/price/billing/quote.vue @@ -68,8 +68,8 @@ @@ -288,7 +288,7 @@ const historyColumns = [ { label: '产品', prop: 'product', minWidth: 160, slot: 'product' }, { label: '国家', prop: 'country', minWidth: 130, slot: 'country' }, { label: '计费重量', prop: 'weight', align: 'center', width: 100, slot: 'weight' }, - { label: '计费日期', prop: 'bizDate', align: 'center', width: 120 }, + { label: '计费时间', prop: 'bizDate', align: 'center', width: 170, parseTime: true }, { label: '计价来源', prop: 'matchSource', align: 'center', width: 140, slot: 'matchSource' }, { label: '套餐', prop: 'priceSetName', minWidth: 120, showOverflowTooltip: true }, { label: '产品价', prop: 'productAmount', align: 'right', width: 110 }, @@ -527,10 +527,15 @@ function formatRate(val) { function formatDate(val) { if (!val) return '-' - // 兼容后端 java.util.Date 转 ISO 字符串 + const raw = String(val).replace('T', ' ') + if (/^\d{4}-\d{2}-\d{2}/.test(raw)) { + return raw.length >= 19 ? raw.slice(0, 19) : raw.slice(0, 10) + } const d = new Date(val) if (Number.isNaN(d.getTime())) return val - return d.toISOString().slice(0, 10) + const pad = (n) => String(n).padStart(2, '0') + return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds()) } function sourceLabel(source) {