修改套餐价格

This commit is contained in:
wuweihua
2026-10-16 10:43:45 +08:00
parent 622d2a4c40
commit 2689138b6a
9 changed files with 572 additions and 277 deletions
@@ -349,7 +349,8 @@ public class BmsProductServiceImpl implements IBmsProductService
}
List<BmsRegion> euRegions = loadEuRegions(regionByNameCn);
Map<String, PrcZone> zoneByCountryAndCode = loadZoneByCountryAndCode();
// Pre-check: same product cannot have duplicate country + weight band in Excel
// Pre-check: any price sheet with duplicate country+weight fails the whole import.
assertAllPriceSheetsNoDupCountryWeight(workbook, regionByNameCn, euRegions, zoneByCountryAndCode);
assertImportProductsNoDupCountryWeight(workbook, products, regionByNameCn, euRegions,
zoneByCountryAndCode);
@@ -632,6 +633,8 @@ public class BmsProductServiceImpl implements IBmsProductService
Map<String, BmsRegion> regionByNameCn = loadRegionByNameCn();
List<BmsRegion> euRegions = loadEuRegions(regionByNameCn);
Map<String, PrcZone> zoneByCountryAndCode = loadZoneByCountryAndCode();
// Fail whole import when any price sheet has duplicate country+weight (incl. unmatched sheets).
assertAllPriceSheetsNoDupCountryWeight(workbook, regionByNameCn, euRegions, zoneByCountryAndCode);
Set<String> unmatchedCountries = new HashSet<>();
Map<String, List<BmsProductCountry>> out = new LinkedHashMap<>();
for (BmsProduct product : products)
@@ -1166,6 +1169,12 @@ public class BmsProductServiceImpl implements IBmsProductService
{
score = 80;
}
else if (StringUtils.isNotEmpty(nameKey) && sheetNameBase(nameKey).equals(sheetNameBase(key))
&& sheetNameBase(nameKey).length() >= 2)
{
// e.g. catalog name suffix differs from sheet (tax/cargo label)
score = 70;
}
else if (StringUtils.isNotEmpty(codeKey) && key.contains(codeKey))
{
score = 60;
@@ -1192,6 +1201,21 @@ public class BmsProductServiceImpl implements IBmsProductService
return out;
}
/** Base name before last '-' (catalog/sheet suffix may differ). */
private static String sheetNameBase(String normalizedKey)
{
if (StringUtils.isEmpty(normalizedKey))
{
return "";
}
int idx = normalizedKey.lastIndexOf('-');
if (idx <= 0)
{
return normalizedKey;
}
return normalizedKey.substring(0, idx);
}
private List<BmsRegion> loadEuRegions(Map<String, BmsRegion> regionByNameCn)
{
List<BmsRegion> list = new ArrayList<>();
@@ -2119,6 +2143,51 @@ public class BmsProductServiceImpl implements IBmsProductService
}
}
/**
* Scan every price sheet (not only catalog-matched ones).
* Duplicate country+weight on any sheet fails the whole import
* (e.g. same country twice for different last-mile carriers).
*/
private void assertAllPriceSheetsNoDupCountryWeight(Workbook workbook,
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
Map<String, PrcZone> zoneByCountryAndCode)
{
if (workbook == null)
{
return;
}
List<String> tips = new ArrayList<String>();
Set<String> unmatchedCountries = new HashSet<String>();
for (int i = 0; i < workbook.getNumberOfSheets(); i++)
{
String sheetName = workbook.getSheetName(i);
if (shouldSkipCountryPriceSheet(sheetName))
{
continue;
}
Sheet sheet = workbook.getSheetAt(i);
if (sheet == null || findHeaderCell(sheet, "\u56fd\u5bb6") == null)
{
continue;
}
BmsProduct stub = new BmsProduct();
stub.setProductCode(sheetName);
stub.setProductName(sheetName);
List<BmsProductCountry> rows = parseProductSheet(sheet, stub,
regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries);
String tip = buildDuplicateCountryWeightTip(stub, rows);
if (StringUtils.isNotEmpty(tip))
{
tips.add(tip);
}
}
if (!tips.isEmpty())
{
throw new ServiceException(MessageUtils.message("price.product.import.dupCountry",
String.join("<br/>", tips)));
}
}
/**
* Workbook pre-check for full import: reject before any write when a product sheet
* has duplicate country + weight band (same zone) rows.
@@ -3018,6 +3018,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
/**
* Draft only: when priceBuildMode changes (PULL/IMPORT), delete this version's
* product / detail / SALES surcharge rel rows. Same mode or non-draft: no-op.
* IMPORT: after wipe, copy products+details from latest prior version (if any).
*/
private void clearDraftVersionChildrenIfBuildModeChanged(PrcPriceSetVersion db, PrcPriceSetVersion incoming)
{
@@ -3043,9 +3044,80 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
deleteSalesRelsByPsvId(psvId);
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
setProductMapper.deleteByPsvId(psvId);
if ("IMPORT".equalsIgnoreCase(newMode))
{
copyLatestPriorVersionChildren(db.getPriceSetId(), psvId);
}
}
/** Prefer same effective_time, else draft, else first version of the set. */
/**
* Copy products + details from latest prior version into the draft (Excel build-price base).
* Prefer EFFECT, then PENDING, then highest versionSeq.
*/
private void copyLatestPriorVersionChildren(Long priceSetId, Long targetPsvId)
{
Long srcPsvId = findLatestPriorPsvId(priceSetId, targetPsvId);
if (srcPsvId == null || targetPsvId == null)
{
return;
}
setProductMapper.copyFromVersion(srcPsvId, targetPsvId);
int detailRows = detailMapper.copyDetailsFromVersion(srcPsvId, targetPsvId);
if (detailRows > 0)
{
setProductMapper.bindDetailPspByPsvId(targetPsvId);
}
}
private Long findLatestPriorPsvId(Long priceSetId, Long excludePsvId)
{
if (priceSetId == null)
{
return null;
}
PrcPriceSetVersion q = new PrcPriceSetVersion();
q.setPriceSetId(priceSetId);
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
if (list == null || list.isEmpty())
{
return null;
}
PrcPriceSetVersion effect = null;
PrcPriceSetVersion pending = null;
PrcPriceSetVersion best = null;
for (PrcPriceSetVersion v : list)
{
if (v == null || v.getPsvId() == null)
{
continue;
}
if (excludePsvId != null && excludePsvId.equals(v.getPsvId()))
{
continue;
}
if (VER_EFFECT.equals(v.getStatus()) && effect == null)
{
effect = v;
}
else if (VER_PENDING.equals(v.getStatus()) && pending == null)
{
pending = v;
}
if (best == null || (v.getVersionSeq() != null && best.getVersionSeq() != null
&& v.getVersionSeq().intValue() > best.getVersionSeq().intValue())
|| (best.getVersionSeq() == null && v.getVersionSeq() != null))
{
best = v;
}
}
PrcPriceSetVersion pick = effect != null ? effect : (pending != null ? pending : best);
return pick == null ? null : pick.getPsvId();
}
/**
* Prefer same effective_time, else an existing draft.
* Never fall back to EFFECT/EXPIRED — that would overwrite a live version when creating a new one.
*/
private PrcPriceSetVersion findReusableVersion(Long priceSetId, Date effectiveTime)
{
PrcPriceSetVersion q = new PrcPriceSetVersion();
@@ -3072,7 +3144,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
return v;
}
}
return list.get(0);
return null;
}
/**