修改基础价格版本功能

This commit is contained in:
wuweihua
2026-09-28 15:45:07 +08:00
parent 1083c2876f
commit 32ab61b51c
22 changed files with 831 additions and 132 deletions
@@ -5,6 +5,7 @@ import java.util.List;
import org.springframework.web.multipart.MultipartFile;
import com.bms.price.domain.BmsProduct;
import com.bms.price.domain.BmsProductCountry;
import com.bms.price.domain.BmsProductPriceVersion;
/**
* 产品列表 服务层
@@ -54,4 +55,7 @@ public interface IBmsProductService
* @return HTML result message
*/
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName) throws Exception;
/** Distinct base-price version options (by effective_date desc) for package create */
public List<BmsProductPriceVersion> selectPriceVersionOptions();
}
@@ -34,9 +34,11 @@ import com.bms.common.utils.MessageUtils;
import com.bms.common.utils.StringUtils;
import com.bms.price.domain.BmsProduct;
import com.bms.price.domain.BmsProductCountry;
import com.bms.price.domain.BmsProductPriceVersion;
import com.bms.price.domain.PrcZone;
import com.bms.price.mapper.BmsProductCountryMapper;
import com.bms.price.mapper.BmsProductMapper;
import com.bms.price.mapper.BmsProductPriceVersionMapper;
import com.bms.price.mapper.PrcZoneMapper;
import com.bms.price.service.IBmsProductService;
import com.bms.price.service.IPrcSurchargeRelService;
@@ -74,6 +76,9 @@ public class BmsProductServiceImpl implements IBmsProductService
@Autowired
private BmsProductCountryMapper countryMapper;
@Autowired
private BmsProductPriceVersionMapper priceVersionMapper;
@Autowired
private BmsRegionMapper regionMapper;
@@ -97,6 +102,7 @@ public class BmsProductServiceImpl implements IBmsProductService
{
productMapper.refreshStatusByDates();
countryMapper.refreshStatusByDates();
priceVersionMapper.refreshStatusByDates();
if (product != null)
{
product.setStatus(normalizeStatusCode(product.getStatus()));
@@ -415,6 +421,13 @@ public class BmsProductServiceImpl implements IBmsProductService
}
}
@Override
public List<BmsProductPriceVersion> selectPriceVersionOptions()
{
List<BmsProductPriceVersion> list = priceVersionMapper.selectVersionOptions();
return list == null ? new ArrayList<>() : list;
}
/** @return int[]{added, skipped, countryRows} */
private int[] importOneProduct(Workbook workbook, BmsProduct product, String operName,
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
@@ -476,8 +489,10 @@ public class BmsProductServiceImpl implements IBmsProductService
/**
* Versioned country save:
* - newEff &gt; last open: close old (expire_date = newEff), insert new (keep history)
* - newEff equals last open: replace open rows
* - require effective_date
* - newEff &gt; last open: expire previous version (expire_date = newEff), create new version,
* copy previous country rows, then match import (hit=update, miss=insert)
* - newEff equals last open: reuse open version, match update/insert (no full replace)
* - newEff &lt; last open: reject
*/
private int saveCountriesVersioned(BmsProduct product, List<BmsProductCountry> countries)
@@ -488,40 +503,125 @@ public class BmsProductServiceImpl implements IBmsProductService
}
String productCode = product.getProductCode();
Long productId = product.getProductId();
Date newEff = resolveImportEffectiveDate(countries);
Date lastOpenEff = countryMapper.selectMaxOpenEffectiveDate(productCode);
Date newEff = toDateOnly(resolveImportEffectiveDate(countries));
if (newEff == null)
{
newEff = toDateOnly(product.getEffectiveDate());
}
if (newEff == null)
{
throw new ServiceException(MessageUtils.message("price.product.import.effEmpty"));
}
if (lastOpenEff != null && newEff != null && newEff.before(lastOpenEff))
BmsProductPriceVersion openVer = priceVersionMapper.selectOpenByProductCode(productCode);
Date lastOpenEff = openVer != null ? toDateOnly(openVer.getEffectiveDate())
: toDateOnly(countryMapper.selectMaxOpenEffectiveDate(productCode));
if (lastOpenEff != null && newEff.before(lastOpenEff))
{
throw new ServiceException(MessageUtils.message("price.product.import.effTooEarly",
formatDate(newEff), formatDate(lastOpenEff)));
}
if (lastOpenEff != null && newEff != null && newEff.after(lastOpenEff))
BmsProductPriceVersion targetVer;
boolean newVersionDay = lastOpenEff == null || newEff.after(lastOpenEff);
if (newVersionDay)
{
// Previous open version expire_date = this effective_date; keep old rows
countryMapper.expireOpenByProductCode(productCode, newEff);
countryMapper.refreshStatusByDates();
List<BmsProductCountry> prevSnapshot = null;
Long srcPpvId = openVer != null ? openVer.getPpvId() : null;
if (lastOpenEff != null)
{
if (srcPpvId != null)
{
prevSnapshot = null; // copy via SQL by ppv_id after expire
}
else
{
prevSnapshot = countryMapper.selectOpenByProductCode(productCode);
}
priceVersionMapper.expireOpenByProductCode(productCode, newEff);
countryMapper.expireOpenByProductCode(productCode, newEff);
}
targetVer = createPriceVersion(product, newEff);
String newStatus = resolveStoredStatus(newEff, null);
if (srcPpvId != null)
{
countryMapper.copyCountriesFromVersion(srcPpvId, targetVer.getPpvId(), productId,
productCode, newEff, newStatus);
}
else if (prevSnapshot != null && !prevSnapshot.isEmpty())
{
insertCountryCopies(prevSnapshot, targetVer.getPpvId(), productId, productCode, newEff, newStatus);
}
}
else if (lastOpenEff != null)
else
{
// Same effective day: replace open rows so re-import can fill/fix details
countryMapper.deleteOpenByProductCodeAndEffectiveDate(productCode, newEff);
targetVer = priceVersionMapper.selectByProductCodeAndEffectiveDate(productCode, newEff);
if (targetVer == null)
{
targetVer = openVer != null ? openVer : createPriceVersion(product, newEff);
}
else if (targetVer.getProductId() == null && productId != null)
{
targetVer.setProductId(productId);
targetVer.setUpdateBy(product.getUpdateBy());
priceVersionMapper.updateBmsProductPriceVersion(targetVer);
}
assignOpenRowsToVersion(productCode, targetVer.getPpvId());
}
for (BmsProductCountry c : countries)
List<BmsProductCountry> baseRows = countryMapper.selectByPpvId(targetVer.getPpvId());
if (baseRows == null)
{
c.setProductId(productId);
c.setProductCode(productCode);
c.setExpireDate(null);
if (c.getEffectiveDate() == null)
{
c.setEffectiveDate(newEff);
}
c.setStatus(resolveStoredStatus(c.getEffectiveDate(), c.getExpireDate()));
baseRows = new ArrayList<>();
}
Map<String, BmsProductCountry> byKey = new HashMap<>();
for (BmsProductCountry row : baseRows)
{
byKey.put(countryMatchKey(row), row);
}
int touched = 0;
for (BmsProductCountry incoming : countries)
{
incoming.setProductId(productId);
incoming.setProductCode(productCode);
incoming.setPpvId(targetVer.getPpvId());
incoming.setEffectiveDate(newEff);
incoming.setExpireDate(null);
incoming.setStatus(resolveStoredStatus(newEff, null));
String key = countryMatchKey(incoming);
BmsProductCountry existing = byKey.get(key);
try
{
countryMapper.insertBmsProductCountry(c);
if (existing != null)
{
existing.setProductId(productId);
existing.setPpvId(targetVer.getPpvId());
existing.setCountryCode(incoming.getCountryCode());
existing.setZoneId(incoming.getZoneId());
existing.setZoneCode(incoming.getZoneCode());
existing.setZoneName(incoming.getZoneName());
existing.setWeightMin(incoming.getWeightMin());
existing.setWeightMax(incoming.getWeightMax());
existing.setFreightPrice(incoming.getFreightPrice());
existing.setRegistrationFee(incoming.getRegistrationFee());
existing.setSizeLimit(incoming.getSizeLimit());
existing.setLastMile(incoming.getLastMile());
existing.setLeadTime(incoming.getLeadTime());
existing.setEffectiveDate(newEff);
existing.setExpireDate(null);
existing.setStatus(resolveStoredStatus(newEff, null));
countryMapper.updateBmsProductCountryImport(existing);
}
else
{
countryMapper.insertBmsProductCountry(incoming);
byKey.put(key, incoming);
}
touched++;
}
catch (Exception e)
{
@@ -530,6 +630,10 @@ public class BmsProductServiceImpl implements IBmsProductService
{
throw new ServiceException(MessageUtils.message("price.product.import.needFeeAlter"));
}
if (tip.contains("ppv_id") || tip.contains("bms_product_price_version"))
{
throw new ServiceException(MessageUtils.message("price.product.import.needVersionAlter"));
}
if (tip.contains("expire_date") || tip.contains("uk_product_country_weight")
|| tip.contains("uk_product_country_zone_weight"))
{
@@ -538,7 +642,94 @@ public class BmsProductServiceImpl implements IBmsProductService
throw e;
}
}
return countries.size();
countryMapper.refreshStatusByDates();
priceVersionMapper.refreshStatusByDates();
return touched;
}
private BmsProductPriceVersion createPriceVersion(BmsProduct product, Date effectiveDate)
{
Integer maxSeq = priceVersionMapper.selectMaxVersionSeq(product.getProductCode());
int seq = maxSeq == null ? 1 : maxSeq.intValue() + 1;
BmsProductPriceVersion ver = new BmsProductPriceVersion();
ver.setProductCode(product.getProductCode());
ver.setProductId(product.getProductId());
ver.setVersionSeq(Integer.valueOf(seq));
ver.setVersionNo("V" + seq);
ver.setEffectiveDate(effectiveDate);
ver.setExpireDate(null);
ver.setStatus(resolveStoredStatus(effectiveDate, null));
ver.setCreateBy(product.getCreateBy() != null ? product.getCreateBy() : product.getUpdateBy());
priceVersionMapper.insertBmsProductPriceVersion(ver);
return ver;
}
/** After expire, insert in-memory previous open rows into the new version */
private void insertCountryCopies(List<BmsProductCountry> prevSnapshot, Long newPpvId, Long productId,
String productCode, Date newEff, String status)
{
for (BmsProductCountry src : prevSnapshot)
{
BmsProductCountry copy = new BmsProductCountry();
copy.setProductId(productId);
copy.setPpvId(newPpvId);
copy.setProductCode(productCode);
copy.setCountryName(src.getCountryName());
copy.setCountryCode(src.getCountryCode());
copy.setZoneId(src.getZoneId());
copy.setZoneCode(src.getZoneCode());
copy.setZoneName(src.getZoneName());
copy.setWeightMin(src.getWeightMin());
copy.setWeightMax(src.getWeightMax());
copy.setFreightPrice(src.getFreightPrice());
copy.setRegistrationFee(src.getRegistrationFee());
copy.setSizeLimit(src.getSizeLimit());
copy.setLastMile(src.getLastMile());
copy.setLeadTime(src.getLeadTime());
copy.setEffectiveDate(newEff);
copy.setExpireDate(null);
copy.setStatus(status);
countryMapper.insertBmsProductCountry(copy);
}
}
private void assignOpenRowsToVersion(String productCode, Long ppvId)
{
if (ppvId == null)
{
return;
}
List<BmsProductCountry> openRows = countryMapper.selectOpenByProductCode(productCode);
if (openRows == null)
{
return;
}
for (BmsProductCountry row : openRows)
{
if (row.getPpvId() == null)
{
countryMapper.updatePpvId(row.getDetailId(), ppvId);
}
}
}
/** Match key: country + zone + weight band */
private String countryMatchKey(BmsProductCountry row)
{
String country = row.getCountryName() == null ? "" : row.getCountryName().trim();
String zone = row.getZoneCode() == null ? "" : row.getZoneCode().trim().toUpperCase(Locale.ROOT);
String wMin = normalizeDecimal(row.getWeightMin());
String wMax = normalizeDecimal(row.getWeightMax());
return country + "|" + zone + "|" + wMin + "|" + wMax;
}
private String normalizeDecimal(BigDecimal v)
{
if (v == null)
{
return "";
}
return v.stripTrailingZeros().toPlainString();
}
private Date resolveImportEffectiveDate(List<BmsProductCountry> countries)
@@ -1830,20 +1830,21 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
detailMapper.deletePrcPriceSetDetailByPsvId(version.getPsvId());
// clean legacy rows without psv_id under this set (would look like duplicates in UI)
detailMapper.deleteOrphanDetailsByPriceSetId(set.getPriceSetId());
String baseVersionNo = set.getBaseVersionNo();
if (StringUtils.isEmpty(baseVersionNo))
{
throw new ServiceException(MessageUtils.message("price.set.baseVersionNo.empty"));
}
int inserted;
if (TYPE_DISCOUNT.equals(set.getSetType()))
{
inserted = detailMapper.insertDiscountDetailsFromLibrary(set.getPriceSetId(), version.getPsvId());
inserted = detailMapper.insertDiscountDetailsFromLibrary(
set.getPriceSetId(), version.getPsvId(), baseVersionNo);
}
else
{
inserted = detailMapper.insertFixedDetailsFromLibrary(
set.getPriceSetId(), version.getPsvId(), version.getEffectiveTime(), false);
if (inserted <= 0)
{
inserted = detailMapper.insertFixedDetailsFromLibrary(
set.getPriceSetId(), version.getPsvId(), version.getEffectiveTime(), true);
}
set.getPriceSetId(), version.getPsvId(), null, false, baseVersionNo);
}
if (inserted <= 0)
{