修改计费逻辑
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+20
-35
@@ -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);
|
||||
|
||||
@@ -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 '命中说明',
|
||||
|
||||
@@ -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)';
|
||||
@@ -68,8 +68,8 @@
|
||||
<el-form-item label="计费时间" prop="bizDate">
|
||||
<el-date-picker
|
||||
v-model="form.bizDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="默认今天,用于筛选已生效的产品明细"
|
||||
style="width: 100%"
|
||||
/>
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user