增加试算功能
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
package com.ruoyi.price.service;
|
||||
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
||||
|
||||
/**
|
||||
* TMS freight quote service.
|
||||
*
|
||||
* Match order: effective customer package (bind_priority ASC) FIXED override
|
||||
* then DISCOUNT, else bms_product_country default freight/registration.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface ITmsPriceQuoteService
|
||||
{
|
||||
TmsPriceQuoteResult quote(TmsPriceQuoteRequest request);
|
||||
}
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
package com.ruoyi.price.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.price.domain.BmsProduct;
|
||||
import com.ruoyi.price.domain.BmsProductCountry;
|
||||
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
||||
import com.ruoyi.price.mapper.BmsProductCountryMapper;
|
||||
import com.ruoyi.price.mapper.BmsProductMapper;
|
||||
import com.ruoyi.price.mapper.PrcCustomerPriceSetMapper;
|
||||
import com.ruoyi.price.mapper.PrcPriceSetDetailMapper;
|
||||
import com.ruoyi.price.mapper.PrcPriceSetMapper;
|
||||
import com.ruoyi.price.service.ITmsPriceQuoteService;
|
||||
|
||||
/**
|
||||
* TMS freight quote: customer package first, then product default.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service
|
||||
public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
{
|
||||
private static final String TYPE_FIXED = "FIXED";
|
||||
private static final String TYPE_DISCOUNT = "DISCOUNT";
|
||||
private static final String SRC_FIXED = "PACKAGE_FIXED";
|
||||
private static final String SRC_DISCOUNT = "PACKAGE_DISCOUNT";
|
||||
private static final String SRC_DEFAULT = "PRODUCT_DEFAULT";
|
||||
|
||||
@Autowired
|
||||
private PrcCustomerPriceSetMapper customerPriceSetMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetMapper priceSetMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetDetailMapper detailMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsProductCountryMapper productCountryMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsProductMapper productMapper;
|
||||
|
||||
@Override
|
||||
public TmsPriceQuoteResult quote(TmsPriceQuoteRequest request)
|
||||
{
|
||||
validateRequest(request);
|
||||
|
||||
String productCode = request.getProductCode().trim();
|
||||
String countryCode = emptyToNull(request.getCountryCode());
|
||||
String countryName = emptyToNull(request.getCountryName());
|
||||
if (countryCode != null)
|
||||
{
|
||||
countryCode = countryCode.toUpperCase();
|
||||
}
|
||||
BigDecimal weight = request.getWeight();
|
||||
Date bizDate = resolveBizDate(request.getBizDate());
|
||||
|
||||
BmsProductCountry base = productCountryMapper.selectMatchByProductCountryWeight(
|
||||
productCode, countryCode, countryName, weight);
|
||||
if (base == null)
|
||||
{
|
||||
throw new ServiceException("PRICE_NOT_HIT: no bms_product_country band for product/country/weight");
|
||||
}
|
||||
|
||||
String productName = resolveProductName(productCode);
|
||||
List<PrcCustomerPriceSet> binds = customerPriceSetMapper.selectEffectiveByCustomer(
|
||||
request.getCustomerId(), bizDate);
|
||||
|
||||
if (binds != null)
|
||||
{
|
||||
for (PrcCustomerPriceSet bind : binds)
|
||||
{
|
||||
PrcPriceSet set = priceSetMapper.selectPrcPriceSetById(bind.getPriceSetId());
|
||||
if (set == null || !"E".equals(set.getStatus()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String setType = StringUtils.isEmpty(set.getSetType()) ? TYPE_FIXED : set.getSetType();
|
||||
if (TYPE_FIXED.equals(setType))
|
||||
{
|
||||
TmsPriceQuoteResult hit = tryFixed(bind, set, base, productCode, productName, weight);
|
||||
if (hit != null)
|
||||
{
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
else if (TYPE_DISCOUNT.equals(setType))
|
||||
{
|
||||
TmsPriceQuoteResult hit = tryDiscount(bind, set, base, productCode, productName, weight);
|
||||
if (hit != null)
|
||||
{
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buildDefault(request.getCustomerId(), base, productCode, productName, weight,
|
||||
"no customer package override; use product default freight/registration");
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult tryFixed(PrcCustomerPriceSet bind, PrcPriceSet set,
|
||||
BmsProductCountry base, String productCode, String productName, BigDecimal weight)
|
||||
{
|
||||
int detailCount = detailMapper.countByPriceSetId(set.getPriceSetId());
|
||||
if (detailCount == 0)
|
||||
{
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight);
|
||||
fillPackage(r, bind, set);
|
||||
r.setMatchMessage("FIXED package has no details; use bms_product_country default");
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
}
|
||||
|
||||
PrcPriceSetDetail detail = detailMapper.selectFixedMatch(
|
||||
set.getPriceSetId(), productCode,
|
||||
base.getCountryCode(), base.getCountryName(), base.getDetailId(),
|
||||
formatWeightLabel(base.getWeightMin(), base.getWeightMax()));
|
||||
if (detail == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (detail.getFvValue() == null || detail.getFvRegistrationFee() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_FIXED, detail.getFvValue(), null, null,
|
||||
detail.getFvRegistrationFee(), base, productCode, productName, weight);
|
||||
fillPackage(r, bind, set);
|
||||
if (StringUtils.isNotEmpty(detail.getProductName()))
|
||||
{
|
||||
r.setProductName(detail.getProductName());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(detail.getWeightLabel()))
|
||||
{
|
||||
r.setWeightLabel(detail.getWeightLabel());
|
||||
}
|
||||
if (detail.getProductCountryId() != null)
|
||||
{
|
||||
r.setProductCountryId(detail.getProductCountryId());
|
||||
}
|
||||
r.setMatchMessage("hit customer FIXED package detail");
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult tryDiscount(PrcCustomerPriceSet bind, PrcPriceSet set,
|
||||
BmsProductCountry base, String productCode, String productName, BigDecimal weight)
|
||||
{
|
||||
int detailCount = detailMapper.countByPriceSetId(set.getPriceSetId());
|
||||
BigDecimal discount = set.getDefaultDiscount();
|
||||
if (detailCount > 0)
|
||||
{
|
||||
PrcPriceSetDetail detail = detailMapper.selectDiscountMatch(set.getPriceSetId(), productCode);
|
||||
if (detail == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (detail.getDiscountValue() != null)
|
||||
{
|
||||
discount = detail.getDiscountValue();
|
||||
}
|
||||
}
|
||||
if (discount == null
|
||||
|| discount.compareTo(BigDecimal.ONE) < 0
|
||||
|| discount.compareTo(new BigDecimal("99")) > 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (base.getFreightPrice() == null)
|
||||
{
|
||||
throw new ServiceException("FREIGHT_VALUE_MISSING: product default freight rate is null");
|
||||
}
|
||||
BigDecimal baseRate = base.getFreightPrice();
|
||||
BigDecimal rate = baseRate.multiply(discount)
|
||||
.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DISCOUNT, rate, baseRate, discount,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight);
|
||||
fillPackage(r, bind, set);
|
||||
r.setMatchMessage("hit customer DISCOUNT package; freight = default * discount%");
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult buildDefault(Long customerId, BmsProductCountry base,
|
||||
String productCode, String productName, BigDecimal weight, String message)
|
||||
{
|
||||
if (base.getFreightPrice() == null || base.getRegistrationFee() == null)
|
||||
{
|
||||
throw new ServiceException("FREIGHT_VALUE_MISSING: product default freight or registration is null");
|
||||
}
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight);
|
||||
r.setCustomerId(customerId);
|
||||
r.setMatchMessage(message);
|
||||
return r;
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult buildFromRates(String source, BigDecimal freightRate,
|
||||
BigDecimal baseFreightRate, BigDecimal discountPercent, BigDecimal registrationFee,
|
||||
BmsProductCountry base, String productCode, String productName, BigDecimal weight)
|
||||
{
|
||||
if (freightRate == null)
|
||||
{
|
||||
throw new ServiceException("FREIGHT_VALUE_MISSING: freight rate is null");
|
||||
}
|
||||
if (registrationFee == null)
|
||||
{
|
||||
registrationFee = BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal freightAmount = freightRate.multiply(weight).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal payable = freightAmount.add(registrationFee).setScale(2, RoundingMode.HALF_UP);
|
||||
|
||||
TmsPriceQuoteResult r = new TmsPriceQuoteResult();
|
||||
r.setMatchSource(source);
|
||||
r.setProductCode(productCode);
|
||||
r.setProductName(productName);
|
||||
r.setCountryCode(base.getCountryCode());
|
||||
r.setCountryName(base.getCountryName());
|
||||
r.setProductCountryId(base.getDetailId());
|
||||
r.setWeightMin(base.getWeightMin());
|
||||
r.setWeightMax(base.getWeightMax());
|
||||
r.setWeightLabel(formatWeightLabel(base.getWeightMin(), base.getWeightMax()));
|
||||
r.setWeight(weight);
|
||||
r.setChargeWeight(weight);
|
||||
r.setFreightRate(freightRate);
|
||||
r.setBaseFreightRate(baseFreightRate);
|
||||
r.setDiscountPercent(discountPercent);
|
||||
r.setRegistrationFee(registrationFee);
|
||||
r.setFreightAmount(freightAmount);
|
||||
r.setPayableTotal(payable);
|
||||
r.setCurrency("RMB");
|
||||
return r;
|
||||
}
|
||||
|
||||
private void fillPackage(TmsPriceQuoteResult r, PrcCustomerPriceSet bind, PrcPriceSet set)
|
||||
{
|
||||
r.setPriceSetId(set.getPriceSetId());
|
||||
r.setPriceSetCode(set.getPriceSetCode());
|
||||
r.setPriceSetName(set.getPriceSetName());
|
||||
r.setSetType(set.getSetType());
|
||||
r.setBindPriority(bind.getBindPriority());
|
||||
r.setCustomerPricesetId(bind.getCustomerPricesetId());
|
||||
}
|
||||
|
||||
private void validateRequest(TmsPriceQuoteRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ServiceException("request body is required");
|
||||
}
|
||||
if (request.getCustomerId() == null)
|
||||
{
|
||||
throw new ServiceException("customerId is required");
|
||||
}
|
||||
if (StringUtils.isEmpty(request.getProductCode()))
|
||||
{
|
||||
throw new ServiceException("productCode is required");
|
||||
}
|
||||
if (StringUtils.isEmpty(request.getCountryCode()) && StringUtils.isEmpty(request.getCountryName()))
|
||||
{
|
||||
throw new ServiceException("countryCode or countryName is required");
|
||||
}
|
||||
if (request.getWeight() == null || request.getWeight().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw new ServiceException("weight must be > 0");
|
||||
}
|
||||
}
|
||||
|
||||
private Date resolveBizDate(String bizDate)
|
||||
{
|
||||
if (StringUtils.isEmpty(bizDate))
|
||||
{
|
||||
return new Date();
|
||||
}
|
||||
try
|
||||
{
|
||||
return new SimpleDateFormat("yyyy-MM-dd").parse(bizDate.trim());
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new ServiceException("bizDate format must be yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveProductName(String productCode)
|
||||
{
|
||||
BmsProduct p = productMapper.selectBmsProductByCode(productCode);
|
||||
return p == null ? null : p.getProductName();
|
||||
}
|
||||
|
||||
private static String emptyToNull(String s)
|
||||
{
|
||||
return StringUtils.isEmpty(s) ? null : s.trim();
|
||||
}
|
||||
|
||||
private static String formatWeightLabel(BigDecimal min, BigDecimal max)
|
||||
{
|
||||
return strip(min) + "\u2264W\u2264" + strip(max);
|
||||
}
|
||||
|
||||
private static String strip(BigDecimal v)
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return v.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user