套餐加分区功能
This commit is contained in:
@@ -70,6 +70,7 @@ price.set.detail.country.empty=固定价明细必须填写国家
|
||||
price.set.detail.weight.empty=固定价明细必须填写重量段
|
||||
price.set.detail.freight.empty=固定价明细必须填写运费单价
|
||||
price.set.detail.registration.empty=固定价明细必须填写挂号费
|
||||
price.set.detail.zone.empty=固定价明细必须选择分区
|
||||
price.set.detail.roundMode.invalid=取值方式仅支持 UP / DOWN / HALF_UP
|
||||
price.set.customer.notExists=客户绑定不存在
|
||||
price.set.customer.unbindAudit=审核中的套餐不允许解绑客户
|
||||
|
||||
@@ -18,6 +18,12 @@ public class BmsProductCountry implements Serializable
|
||||
private String productCode;
|
||||
private String countryName;
|
||||
private String countryCode;
|
||||
/** prc_zone.zone_id; null when product country has no zone */
|
||||
private Long zoneId;
|
||||
/** e.g. Z1; null when no zone */
|
||||
private String zoneCode;
|
||||
/** e.g. Australia(Z1); null when no zone */
|
||||
private String zoneName;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
private BigDecimal freightPrice;
|
||||
|
||||
@@ -18,6 +18,12 @@ public class PrcPriceSetDetail implements Serializable
|
||||
private String productName;
|
||||
private String countryCode;
|
||||
private String countryName;
|
||||
/** prc_zone.zone_id; null when country has no zone */
|
||||
private Long zoneId;
|
||||
/** e.g. Z1; null when no zone */
|
||||
private String zoneCode;
|
||||
/** zone display name; null when no zone */
|
||||
private String zoneName;
|
||||
private Long zdId;
|
||||
private Long wsdId;
|
||||
private String weightLabel;
|
||||
|
||||
+2
-1
@@ -27,12 +27,13 @@ public interface PrcPriceSetDetailMapper
|
||||
|
||||
public int countByPriceSetId(Long priceSetId);
|
||||
|
||||
/** FIXED:按产品+国家+关联产品国家明细/重量段命中 */
|
||||
/** FIXED: product + country + zone + productCountryId / weight */
|
||||
public PrcPriceSetDetail selectFixedMatch(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("zoneCode") String zoneCode,
|
||||
@Param("productCountryId") Long productCountryId,
|
||||
@Param("weightLabel") String weightLabel);
|
||||
|
||||
|
||||
+97
-38
@@ -34,10 +34,13 @@ import com.ruoyi.common.utils.MessageUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.price.domain.BmsProduct;
|
||||
import com.ruoyi.price.domain.BmsProductCountry;
|
||||
import com.ruoyi.price.domain.PrcZone;
|
||||
import com.ruoyi.price.mapper.BmsProductCountryMapper;
|
||||
import com.ruoyi.price.mapper.BmsProductMapper;
|
||||
import com.ruoyi.price.mapper.PrcZoneMapper;
|
||||
import com.ruoyi.price.service.IBmsProductService;
|
||||
import com.ruoyi.price.service.IPrcSurchargeRelService;
|
||||
import com.ruoyi.price.util.ZoneCodeAbbrevs;
|
||||
import com.ruoyi.system.domain.BmsRegion;
|
||||
import com.ruoyi.system.mapper.BmsRegionMapper;
|
||||
|
||||
@@ -74,6 +77,9 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
@Autowired
|
||||
private BmsRegionMapper regionMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcZoneMapper zoneMapper;
|
||||
|
||||
@Autowired
|
||||
private IPrcSurchargeRelService surchargeRelService;
|
||||
|
||||
@@ -248,6 +254,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.regionEmpty"));
|
||||
}
|
||||
List<BmsRegion> euRegions = loadEuRegions(regionByNameCn);
|
||||
Map<String, PrcZone> zoneByCountryAndCode = loadZoneByCountryAndCode();
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
@@ -263,7 +270,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
try
|
||||
{
|
||||
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
|
||||
unmatchedCountries);
|
||||
zoneByCountryAndCode, unmatchedCountries);
|
||||
transactionManager.commit(tx);
|
||||
successNum += nums[0];
|
||||
skipNum += nums[1];
|
||||
@@ -300,7 +307,8 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
|
||||
/** @return int[]{added, skipped, countryRows} */
|
||||
private int[] importOneProduct(Workbook workbook, BmsProduct product, String operName,
|
||||
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions, Set<String> unmatchedCountries)
|
||||
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
|
||||
Map<String, PrcZone> zoneByCountryAndCode, Set<String> unmatchedCountries)
|
||||
{
|
||||
product.setSourceSheet(product.getProductName());
|
||||
product.setCreateBy(operName);
|
||||
@@ -314,7 +322,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
product.setSourceSheet(productSheet.getSheetName());
|
||||
List<BmsProductCountry> countries = parseProductSheet(productSheet, product,
|
||||
regionByNameCn, euRegions, unmatchedCountries);
|
||||
regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries);
|
||||
if (countries == null || countries.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.noCountry",
|
||||
@@ -412,7 +420,8 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.needFeeAlter"));
|
||||
}
|
||||
if (tip.contains("expire_date") || tip.contains("uk_product_country_weight"))
|
||||
if (tip.contains("expire_date") || tip.contains("uk_product_country_weight")
|
||||
|| tip.contains("uk_product_country_zone_weight"))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.needExpireAlter"));
|
||||
}
|
||||
@@ -716,7 +725,8 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
|
||||
private List<BmsProductCountry> parseProductSheet(Sheet sheet, BmsProduct product,
|
||||
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions, Set<String> unmatchedCountries)
|
||||
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
|
||||
Map<String, PrcZone> zoneByCountryAndCode, Set<String> unmatchedCountries)
|
||||
{
|
||||
if (sheet == null)
|
||||
{
|
||||
@@ -739,7 +749,6 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
int colSize = findColByHeader(headerRow, "\u5c3a\u5bf8", colCountry + 1);
|
||||
int colLastMile = findColByHeader(headerRow, "\u5c3e\u7a0b", colCountry + 1);
|
||||
int colLeadTime = findColByHeader(headerRow, "\u65f6\u6548", colCountry + 1);
|
||||
// Fallback offsets when header text missing (Weishi layout: B=country ... H=lead time)
|
||||
if (colWeight < 0)
|
||||
{
|
||||
colWeight = colCountry + 1;
|
||||
@@ -765,7 +774,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
colLeadTime = colCountry + 6;
|
||||
}
|
||||
|
||||
String currentCountry = null;
|
||||
String currentCountryRaw = null;
|
||||
String currentSize = null;
|
||||
String currentLastMile = null;
|
||||
String currentLeadTime = null;
|
||||
@@ -785,12 +794,12 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
if (StringUtils.isNotEmpty(countryCell))
|
||||
{
|
||||
currentCountry = normalizeCountryName(countryCell);
|
||||
currentCountryRaw = countryCell;
|
||||
currentSize = emptyToNull(cellString(row.getCell(colSize)));
|
||||
currentLastMile = emptyToNull(cellString(row.getCell(colLastMile)));
|
||||
currentLeadTime = emptyToNull(cellString(row.getCell(colLeadTime)));
|
||||
}
|
||||
if (StringUtils.isEmpty(currentCountry))
|
||||
if (StringUtils.isEmpty(currentCountryRaw))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -803,14 +812,23 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
BigDecimal registration = cellDecimal(row.getCell(colRegister));
|
||||
Date eff = sheetEffective != null ? sheetEffective : product.getEffectiveDate();
|
||||
|
||||
BmsRegion region = resolveRegion(currentCountry, regionByNameCn);
|
||||
ZoneCodeAbbrevs.CountryZoneParts parts = ZoneCodeAbbrevs.splitCountryZone(currentCountryRaw);
|
||||
if (parts == null || StringUtils.isEmpty(parts.countryName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String baseCountry = parts.countryName;
|
||||
String zoneLabel = parts.zoneLabel;
|
||||
|
||||
BmsRegion region = resolveRegion(baseCountry, regionByNameCn);
|
||||
if (region != null)
|
||||
{
|
||||
list.add(buildCountryRow(region.getCountryNameCn(), region.getCountryCode2(),
|
||||
zoneLabel, zoneByCountryAndCode,
|
||||
range, freight, registration, currentSize, currentLastMile, currentLeadTime, eff));
|
||||
continue;
|
||||
}
|
||||
if (isEuZoneName(currentCountry))
|
||||
if (isEuZoneName(baseCountry) && StringUtils.isEmpty(zoneLabel))
|
||||
{
|
||||
if (euRegions == null || euRegions.isEmpty())
|
||||
{
|
||||
@@ -819,23 +837,26 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
for (BmsRegion eu : euRegions)
|
||||
{
|
||||
list.add(buildCountryRow(eu.getCountryNameCn(), eu.getCountryCode2(),
|
||||
null, zoneByCountryAndCode,
|
||||
range, freight, registration, currentSize, currentLastMile, currentLeadTime, eff));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
unmatchedCountries.add(currentCountry);
|
||||
list.add(buildCountryRow(currentCountry, null,
|
||||
unmatchedCountries.add(currentCountryRaw.trim().replace("\n", " ").replace("\r", " "));
|
||||
list.add(buildCountryRow(baseCountry, null, zoneLabel, zoneByCountryAndCode,
|
||||
range, freight, registration, currentSize, currentLastMile, currentLeadTime, eff));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private BmsProductCountry buildCountryRow(String countryName, String countryCode, BigDecimal[] range,
|
||||
private BmsProductCountry buildCountryRow(String countryName, String countryCode, String zoneLabel,
|
||||
Map<String, PrcZone> zoneByCountryAndCode, BigDecimal[] range,
|
||||
BigDecimal freight, BigDecimal registration, String sizeLimit, String lastMile, String leadTime, Date eff)
|
||||
{
|
||||
BmsProductCountry c = new BmsProductCountry();
|
||||
c.setCountryName(countryName);
|
||||
c.setCountryCode(countryCode);
|
||||
applyZoneFields(c, countryCode, zoneLabel, zoneByCountryAndCode);
|
||||
c.setWeightMin(range[0]);
|
||||
c.setWeightMax(range[1]);
|
||||
c.setFreightPrice(freight);
|
||||
@@ -847,6 +868,68 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
return c;
|
||||
}
|
||||
|
||||
private void applyZoneFields(BmsProductCountry c, String countryCode, String zoneLabel,
|
||||
Map<String, PrcZone> zoneByCountryAndCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(zoneLabel))
|
||||
{
|
||||
c.setZoneId(null);
|
||||
c.setZoneCode(null);
|
||||
c.setZoneName(null);
|
||||
return;
|
||||
}
|
||||
String zoneCode = ZoneCodeAbbrevs.toAbbrev(zoneLabel);
|
||||
c.setZoneCode(zoneCode);
|
||||
PrcZone zone = null;
|
||||
if (StringUtils.isNotEmpty(countryCode) && zoneByCountryAndCode != null)
|
||||
{
|
||||
zone = zoneByCountryAndCode.get(zoneKey(countryCode, zoneCode));
|
||||
}
|
||||
if (zone != null)
|
||||
{
|
||||
c.setZoneId(zone.getZoneId());
|
||||
c.setZoneCode(zone.getZoneCode());
|
||||
c.setZoneName(zone.getZoneName());
|
||||
}
|
||||
else
|
||||
{
|
||||
c.setZoneId(null);
|
||||
c.setZoneName(StringUtils.isNotEmpty(c.getCountryName())
|
||||
? c.getCountryName() + "(" + zoneCode + ")"
|
||||
: zoneCode);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, PrcZone> loadZoneByCountryAndCode()
|
||||
{
|
||||
Map<String, PrcZone> map = new HashMap<>();
|
||||
List<PrcZone> list = zoneMapper.selectPrcZoneList(new PrcZone());
|
||||
if (list == null)
|
||||
{
|
||||
return map;
|
||||
}
|
||||
for (PrcZone zone : list)
|
||||
{
|
||||
if (zone == null || StringUtils.isEmpty(zone.getZoneCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String code2 = zone.getCountryCode();
|
||||
if (StringUtils.isEmpty(code2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
map.put(zoneKey(code2, zone.getZoneCode()), zone);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static String zoneKey(String countryCode, String zoneCode)
|
||||
{
|
||||
return String.valueOf(countryCode).trim().toUpperCase(Locale.ROOT)
|
||||
+ "|" + String.valueOf(zoneCode).trim().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bms_region map: country_name_cn -> region (for country_code_2).
|
||||
*/
|
||||
@@ -907,7 +990,6 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
return candidates.get(0);
|
||||
}
|
||||
// Prefer longest Chinese name match (e.g. 捷克共和国 over 捷克)
|
||||
BmsRegion best = candidates.get(0);
|
||||
for (int i = 1; i < candidates.size(); i++)
|
||||
{
|
||||
@@ -920,29 +1002,6 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
return best;
|
||||
}
|
||||
|
||||
/** Strip Excel footnotes like "澳大利亚(1区)\\n分区点击" */
|
||||
private static String normalizeCountryName(String name)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String n = name.trim();
|
||||
int cut = n.length();
|
||||
int nl = n.indexOf('\n');
|
||||
if (nl >= 0 && nl < cut)
|
||||
{
|
||||
cut = nl;
|
||||
}
|
||||
int cr = n.indexOf('\r');
|
||||
if (cr >= 0 && cr < cut)
|
||||
{
|
||||
cut = cr;
|
||||
}
|
||||
n = n.substring(0, cut).trim();
|
||||
return n.isEmpty() ? null : n;
|
||||
}
|
||||
|
||||
private BigDecimal[] parseWeightRange(String label)
|
||||
{
|
||||
if (StringUtils.isEmpty(label))
|
||||
|
||||
+130
-12
@@ -248,6 +248,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
nd.setProductName(d.getProductName());
|
||||
nd.setCountryCode(d.getCountryCode());
|
||||
nd.setCountryName(d.getCountryName());
|
||||
nd.setZoneId(d.getZoneId());
|
||||
nd.setZoneCode(d.getZoneCode());
|
||||
nd.setZoneName(d.getZoneName());
|
||||
nd.setZdId(d.getZdId());
|
||||
nd.setWsdId(d.getWsdId());
|
||||
nd.setWeightLabel(d.getWeightLabel());
|
||||
@@ -659,6 +662,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.detail.country.empty"));
|
||||
}
|
||||
requireZoneIfNeeded(detail);
|
||||
if (detail.getWsdId() == null && StringUtils.isEmpty(detail.getWeightLabel()))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.detail.weight.empty"));
|
||||
@@ -692,6 +696,29 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When product country rows for this country have zones, zone is required.
|
||||
*/
|
||||
private void requireZoneIfNeeded(PrcPriceSetDetail detail)
|
||||
{
|
||||
if (detail.getZoneId() != null || StringUtils.isNotEmpty(detail.getZoneCode()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<BmsProductCountry> rows = listOpenProductCountries(detail.getProductCode());
|
||||
for (BmsProductCountry row : rows)
|
||||
{
|
||||
if (!matchDetailCountryOnly(detail, row))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (row.getZoneId() != null || StringUtils.isNotEmpty(row.getZoneCode()))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.detail.zone.empty"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve / verify product_country_id from bms_product_country when missing.
|
||||
*/
|
||||
@@ -699,25 +726,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
if (detail.getProductCountryId() != null)
|
||||
{
|
||||
syncZoneFromProductCountry(detail);
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isEmpty(detail.getProductCode()) || StringUtils.isEmpty(detail.getWeightLabel()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BmsProductCountry query = new BmsProductCountry();
|
||||
query.setProductCode(detail.getProductCode());
|
||||
List<BmsProductCountry> rows = productCountryMapper.selectBmsProductCountryList(query);
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<BmsProductCountry> rows = listOpenProductCountries(detail.getProductCode());
|
||||
for (BmsProductCountry row : rows)
|
||||
{
|
||||
if (row.getExpireDate() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!matchDetailCountry(detail, row))
|
||||
{
|
||||
continue;
|
||||
@@ -730,12 +748,80 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
&& detail.getWeightLabel().contains(stripTrailingZeros(row.getWeightMax()))))
|
||||
{
|
||||
detail.setProductCountryId(row.getDetailId());
|
||||
applyZoneFromRow(detail, row);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchDetailCountry(PrcPriceSetDetail detail, BmsProductCountry row)
|
||||
private void syncZoneFromProductCountry(PrcPriceSetDetail detail)
|
||||
{
|
||||
if (detail.getProductCountryId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (detail.getZoneId() != null || StringUtils.isNotEmpty(detail.getZoneCode()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BmsProductCountry query = new BmsProductCountry();
|
||||
query.setProductCode(detail.getProductCode());
|
||||
List<BmsProductCountry> rows = productCountryMapper.selectBmsProductCountryList(query);
|
||||
if (rows == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (BmsProductCountry row : rows)
|
||||
{
|
||||
if (detail.getProductCountryId().equals(row.getDetailId()))
|
||||
{
|
||||
applyZoneFromRow(detail, row);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<BmsProductCountry> listOpenProductCountries(String productCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
BmsProductCountry query = new BmsProductCountry();
|
||||
query.setProductCode(productCode);
|
||||
List<BmsProductCountry> rows = productCountryMapper.selectBmsProductCountryList(query);
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
List<BmsProductCountry> open = new ArrayList<BmsProductCountry>();
|
||||
for (BmsProductCountry row : rows)
|
||||
{
|
||||
if (row.getExpireDate() == null)
|
||||
{
|
||||
open.add(row);
|
||||
}
|
||||
}
|
||||
return open;
|
||||
}
|
||||
|
||||
private static void applyZoneFromRow(PrcPriceSetDetail detail, BmsProductCountry row)
|
||||
{
|
||||
if (detail.getZoneId() == null)
|
||||
{
|
||||
detail.setZoneId(row.getZoneId());
|
||||
}
|
||||
if (StringUtils.isEmpty(detail.getZoneCode()))
|
||||
{
|
||||
detail.setZoneCode(row.getZoneCode());
|
||||
}
|
||||
if (StringUtils.isEmpty(detail.getZoneName()))
|
||||
{
|
||||
detail.setZoneName(row.getZoneName());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchDetailCountryOnly(PrcPriceSetDetail detail, BmsProductCountry row)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(detail.getCountryCode()) && StringUtils.isNotEmpty(row.getCountryCode()))
|
||||
{
|
||||
@@ -745,6 +831,38 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
&& detail.getCountryName().equals(row.getCountryName());
|
||||
}
|
||||
|
||||
private static boolean matchDetailCountry(PrcPriceSetDetail detail, BmsProductCountry row)
|
||||
{
|
||||
if (!matchDetailCountryOnly(detail, row))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return matchDetailZone(detail, row);
|
||||
}
|
||||
|
||||
private static boolean matchDetailZone(PrcPriceSetDetail detail, BmsProductCountry row)
|
||||
{
|
||||
boolean detailHas = detail.getZoneId() != null || StringUtils.isNotEmpty(detail.getZoneCode());
|
||||
boolean rowHas = row.getZoneId() != null || StringUtils.isNotEmpty(row.getZoneCode());
|
||||
if (!detailHas && !rowHas)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (detailHas != rowHas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (detail.getZoneId() != null && row.getZoneId() != null)
|
||||
{
|
||||
return detail.getZoneId().equals(row.getZoneId());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(detail.getZoneCode()) && StringUtils.isNotEmpty(row.getZoneCode()))
|
||||
{
|
||||
return detail.getZoneCode().equalsIgnoreCase(row.getZoneCode());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String formatWeightLabel(BigDecimal min, BigDecimal max)
|
||||
{
|
||||
return stripTrailingZeros(min) + "<W\u2264" + stripTrailingZeros(max);
|
||||
|
||||
+4
-75
@@ -31,6 +31,7 @@ import com.ruoyi.price.domain.PrcZoneRegion;
|
||||
import com.ruoyi.price.mapper.PrcZoneDetailMapper;
|
||||
import com.ruoyi.price.mapper.PrcZoneMapper;
|
||||
import com.ruoyi.price.service.IPrcZoneService;
|
||||
import com.ruoyi.price.util.ZoneCodeAbbrevs;
|
||||
import com.ruoyi.system.domain.BmsRegion;
|
||||
import com.ruoyi.system.mapper.BmsRegionMapper;
|
||||
|
||||
@@ -130,7 +131,7 @@ public class PrcZoneServiceImpl implements IPrcZoneService
|
||||
}
|
||||
if (StringUtils.isNotEmpty(zone.getZoneCode()))
|
||||
{
|
||||
zone.setZoneCode(toZoneCodeAbbrev(zone.getZoneCode()));
|
||||
zone.setZoneCode(ZoneCodeAbbrevs.toAbbrev(zone.getZoneCode()));
|
||||
}
|
||||
if (StringUtils.isEmpty(zone.getZoneName()))
|
||||
{
|
||||
@@ -156,7 +157,7 @@ public class PrcZoneServiceImpl implements IPrcZoneService
|
||||
}
|
||||
if (StringUtils.isNotEmpty(zone.getZoneCode()))
|
||||
{
|
||||
zone.setZoneCode(toZoneCodeAbbrev(zone.getZoneCode()));
|
||||
zone.setZoneCode(ZoneCodeAbbrevs.toAbbrev(zone.getZoneCode()));
|
||||
}
|
||||
if (zone.getZoneSort() == null && StringUtils.isNotEmpty(zone.getZoneCode()))
|
||||
{
|
||||
@@ -293,7 +294,7 @@ public class PrcZoneServiceImpl implements IPrcZoneService
|
||||
String countryName = region.getCountryNameCn();
|
||||
for (Map.Entry<String, List<String>> entry : entries)
|
||||
{
|
||||
String zoneCode = toZoneCodeAbbrev(entry.getKey());
|
||||
String zoneCode = ZoneCodeAbbrevs.toAbbrev(entry.getKey());
|
||||
PrcZone zone = new PrcZone();
|
||||
zone.setRegionId(regionId);
|
||||
zone.setCountryCode(region.getCountryCode2());
|
||||
@@ -868,78 +869,6 @@ public class PrcZoneServiceImpl implements IPrcZoneService
|
||||
return (countryName == null ? "" : countryName) + "(" + zoneCode + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Excel/local zone labels to English abbreviations for zone_code.
|
||||
*/
|
||||
private static String toZoneCodeAbbrev(String raw)
|
||||
{
|
||||
if (StringUtils.isEmpty(raw))
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
String key = raw.replace('\r', ' ').replace('\n', ' ').replaceAll("\\s+", " ").trim();
|
||||
switch (key)
|
||||
{
|
||||
case "1\u533a":
|
||||
return "Z1";
|
||||
case "2\u533a":
|
||||
return "Z2";
|
||||
case "3\u533a":
|
||||
return "Z3";
|
||||
case "4\u533a":
|
||||
return "Z4";
|
||||
case "5\u533a":
|
||||
return "Z5";
|
||||
case "Mondial Relay":
|
||||
return "MR";
|
||||
case "\u6c96\u7e04\u770c":
|
||||
return "OKN";
|
||||
case "\u5317\u7231\u5c14\u5170":
|
||||
return "NI";
|
||||
case "\u9ad8\u5730\u548c\u5c9b\u5c7f":
|
||||
return "HI";
|
||||
case "\u6d77\u5ce1\u7fa4\u5c9b(\u542b\u9521\u5229\u5c9b)":
|
||||
return "CI";
|
||||
case "\u9a6c\u6069\u5c9b":
|
||||
return "IM";
|
||||
case "Beckton":
|
||||
return "BEC";
|
||||
case "Bristol":
|
||||
return "BRS";
|
||||
case "Cambridge":
|
||||
return "CAM";
|
||||
case "Collier Wood":
|
||||
return "CW";
|
||||
case "Guernsey":
|
||||
return "GG";
|
||||
case "Isle of Man Bailiwick of Guernsey States of Jersey":
|
||||
return "IM_GG_JE";
|
||||
case "Lakeside":
|
||||
return "LKS";
|
||||
case "London Congestion Zone":
|
||||
return "LCZ";
|
||||
case "London Congestion Zone - Gemini":
|
||||
return "LCZ_GEM";
|
||||
case "London ULEZ - Wimbledon":
|
||||
return "ULEZ_WIM";
|
||||
case "Northern Ireland Isles of Scilly":
|
||||
return "NI_SCILLY";
|
||||
case "Old Kent Road Cargo Bikes":
|
||||
return "OKR_CB";
|
||||
case "Oxford":
|
||||
return "OXF";
|
||||
case "The Highlands Islands of Scotland":
|
||||
return "HIGHLANDS";
|
||||
default:
|
||||
// already abbrev or unknown: keep trimmed original (spaces -> underscore)
|
||||
if (key.matches(".*[\\u4e00-\\u9fff].*"))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
return key.replace(' ', '_').toUpperCase(Locale.ROOT).replaceAll("_+", "_");
|
||||
}
|
||||
}
|
||||
|
||||
private static int extractZoneSort(String zoneCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(zoneCode))
|
||||
|
||||
+2
-1
@@ -248,7 +248,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
|
||||
PrcPriceSetDetail detail = detailMapper.selectFixedMatch(
|
||||
set.getPriceSetId(), productCode,
|
||||
base.getCountryCode(), base.getCountryName(), base.getDetailId(),
|
||||
base.getCountryCode(), base.getCountryName(), base.getZoneCode(),
|
||||
base.getDetailId(),
|
||||
formatWeightLabel(base.getWeightMin(), base.getWeightMax()));
|
||||
if (detail == null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.ruoyi.price.util;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* Parse Excel country cell with optional zone suffix, map labels to English abbreviations.
|
||||
*/
|
||||
public final class ZoneCodeAbbrevs
|
||||
{
|
||||
// fullwidth () and ASCII ()
|
||||
private static final Pattern ZONE_SUFFIX = Pattern.compile(
|
||||
"^(.+?)[\\(\\uFF08]([^\\)\\uFF09]+)[\\)\\uFF09]\\s*$");
|
||||
|
||||
private ZoneCodeAbbrevs()
|
||||
{
|
||||
}
|
||||
|
||||
public static final class CountryZoneParts
|
||||
{
|
||||
public final String countryName;
|
||||
public final String zoneLabel;
|
||||
|
||||
public CountryZoneParts(String countryName, String zoneLabel)
|
||||
{
|
||||
this.countryName = countryName;
|
||||
this.zoneLabel = zoneLabel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split country cell like Country(zoneLabel); no parenthesis -> zoneLabel null.
|
||||
*/
|
||||
public static CountryZoneParts splitCountryZone(String raw)
|
||||
{
|
||||
if (StringUtils.isEmpty(raw))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String n = raw.trim();
|
||||
int cut = n.length();
|
||||
int nl = n.indexOf('\n');
|
||||
if (nl >= 0 && nl < cut)
|
||||
{
|
||||
cut = nl;
|
||||
}
|
||||
int cr = n.indexOf('\r');
|
||||
if (cr >= 0 && cr < cut)
|
||||
{
|
||||
cut = cr;
|
||||
}
|
||||
n = n.substring(0, cut).trim();
|
||||
if (n.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Matcher m = ZONE_SUFFIX.matcher(n);
|
||||
if (m.matches())
|
||||
{
|
||||
String country = m.group(1).trim();
|
||||
String zone = m.group(2).trim();
|
||||
if (StringUtils.isEmpty(country))
|
||||
{
|
||||
return new CountryZoneParts(n, null);
|
||||
}
|
||||
return new CountryZoneParts(country, StringUtils.isEmpty(zone) ? null : zone);
|
||||
}
|
||||
return new CountryZoneParts(n, null);
|
||||
}
|
||||
|
||||
public static String toAbbrev(String raw)
|
||||
{
|
||||
if (StringUtils.isEmpty(raw))
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
String key = raw.replace('\r', ' ').replace('\n', ' ').replaceAll("\\s+", " ").trim();
|
||||
switch (key)
|
||||
{
|
||||
case "1\u533a":
|
||||
return "Z1";
|
||||
case "2\u533a":
|
||||
return "Z2";
|
||||
case "3\u533a":
|
||||
return "Z3";
|
||||
case "4\u533a":
|
||||
return "Z4";
|
||||
case "5\u533a":
|
||||
return "Z5";
|
||||
case "Mondial Relay":
|
||||
return "MR";
|
||||
case "\u6c96\u7e04\u770c":
|
||||
return "OKN";
|
||||
case "\u5317\u7231\u5c14\u5170":
|
||||
return "NI";
|
||||
case "\u9ad8\u5730\u548c\u5c9b\u5c7f":
|
||||
return "HI";
|
||||
case "\u6d77\u5ce1\u7fa4\u5c9b(\u542b\u9521\u5229\u5c9b)":
|
||||
return "CI";
|
||||
case "\u9a6c\u6069\u5c9b":
|
||||
return "IM";
|
||||
case "Beckton":
|
||||
return "BEC";
|
||||
case "Bristol":
|
||||
return "BRS";
|
||||
case "Cambridge":
|
||||
return "CAM";
|
||||
case "Collier Wood":
|
||||
return "CW";
|
||||
case "Guernsey":
|
||||
return "GG";
|
||||
case "Isle of Man Bailiwick of Guernsey States of Jersey":
|
||||
return "IM_GG_JE";
|
||||
case "Lakeside":
|
||||
return "LKS";
|
||||
case "London Congestion Zone":
|
||||
return "LCZ";
|
||||
case "London Congestion Zone - Gemini":
|
||||
return "LCZ_GEM";
|
||||
case "London ULEZ - Wimbledon":
|
||||
return "ULEZ_WIM";
|
||||
case "Northern Ireland Isles of Scilly":
|
||||
return "NI_SCILLY";
|
||||
case "Old Kent Road Cargo Bikes":
|
||||
return "OKR_CB";
|
||||
case "Oxford":
|
||||
return "OXF";
|
||||
case "The Highlands Islands of Scotland":
|
||||
return "HIGHLANDS";
|
||||
default:
|
||||
if (key.matches("(?i)Z[1-9]\\d*"))
|
||||
{
|
||||
return key.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
if (key.matches(".*[\\u4e00-\\u9fff].*"))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
return key.replace(' ', '_').toUpperCase(Locale.ROOT).replaceAll("_+", "_");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="productCode" column="product_code" />
|
||||
<result property="countryName" column="country_name" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
<result property="zoneId" column="zone_id" />
|
||||
<result property="zoneCode" column="zone_code" />
|
||||
<result property="zoneName" column="zone_name" />
|
||||
<result property="weightMin" column="weight_min" />
|
||||
<result property="weightMax" column="weight_max" />
|
||||
<result property="freightPrice" column="freight_price" />
|
||||
@@ -26,7 +29,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectVo">
|
||||
select detail_id, product_id, product_code, country_name, country_code, weight_min, weight_max,
|
||||
select detail_id, product_id, product_code, country_name, country_code, zone_id, zone_code, zone_name,
|
||||
weight_min, weight_max,
|
||||
freight_price, registration_fee, size_limit, last_mile, lead_time,
|
||||
effective_date, expire_date, status, create_time, update_time
|
||||
from bms_product_country
|
||||
@@ -47,6 +51,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="countryCode != null and countryCode != ''">
|
||||
AND country_code = #{countryCode}
|
||||
</if>
|
||||
<if test="zoneId != null">
|
||||
AND zone_id = #{zoneId}
|
||||
</if>
|
||||
<if test="zoneCode != null and zoneCode != ''">
|
||||
AND zone_code = #{zoneCode}
|
||||
</if>
|
||||
<if test="weightMin != null">
|
||||
AND weight_min = #{weightMin}
|
||||
</if>
|
||||
@@ -66,16 +76,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND status = #{effectStatus}
|
||||
</if>
|
||||
</where>
|
||||
order by effective_date desc, country_name asc, weight_min asc, detail_id asc
|
||||
order by effective_date desc, country_name asc, zone_code asc, weight_min asc, detail_id asc
|
||||
</select>
|
||||
|
||||
<insert id="insertBmsProductCountry" parameterType="BmsProductCountry" useGeneratedKeys="true" keyProperty="detailId">
|
||||
insert into bms_product_country (
|
||||
product_id, product_code, country_name, country_code, weight_min, weight_max,
|
||||
product_id, product_code, country_name, country_code, zone_id, zone_code, zone_name,
|
||||
weight_min, weight_max,
|
||||
freight_price, registration_fee, size_limit, last_mile, lead_time,
|
||||
effective_date, expire_date, status, create_time
|
||||
) values (
|
||||
#{productId}, #{productCode}, #{countryName}, #{countryCode}, #{weightMin}, #{weightMax},
|
||||
#{productId}, #{productCode}, #{countryName}, #{countryCode}, #{zoneId}, #{zoneCode}, #{zoneName},
|
||||
#{weightMin}, #{weightMax},
|
||||
#{freightPrice}, #{registrationFee}, #{sizeLimit}, #{lastMile}, #{leadTime},
|
||||
#{effectiveDate}, #{expireDate}, IFNULL(#{status}, '0'), sysdate()
|
||||
)
|
||||
@@ -162,7 +174,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="bizDate == null">
|
||||
and expire_date is null
|
||||
</if>
|
||||
order by effective_date desc, weight_min asc, detail_id asc
|
||||
order by
|
||||
case when zone_code is null or zone_code = '' then 0 else 1 end,
|
||||
effective_date desc, weight_min asc, detail_id asc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="productName" column="product_name" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
<result property="countryName" column="country_name" />
|
||||
<result property="zoneId" column="zone_id" />
|
||||
<result property="zoneCode" column="zone_code" />
|
||||
<result property="zoneName" column="zone_name" />
|
||||
<result property="zdId" column="zd_id" />
|
||||
<result property="wsdId" column="wsd_id" />
|
||||
<result property="weightLabel" column="weight_label" />
|
||||
@@ -26,6 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<sql id="selectPrcPriceSetDetailVo">
|
||||
select psd_id, price_set_id, product_code, product_name, country_code, country_name,
|
||||
zone_id, zone_code, zone_name,
|
||||
zd_id, wsd_id, weight_label, product_country_id, fv_value, fv_registration_fee,
|
||||
discount_value, round_mode, pr_id, create_time, update_time
|
||||
from prc_price_set_detail
|
||||
@@ -48,6 +52,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="countryCode != null and countryCode != ''">
|
||||
AND country_code = #{countryCode}
|
||||
</if>
|
||||
<if test="zoneCode != null and zoneCode != ''">
|
||||
AND zone_code = #{zoneCode}
|
||||
</if>
|
||||
</where>
|
||||
order by psd_id asc
|
||||
</select>
|
||||
@@ -67,6 +74,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="productName != null">product_name,</if>
|
||||
<if test="countryCode != null">country_code,</if>
|
||||
<if test="countryName != null">country_name,</if>
|
||||
<if test="zoneId != null">zone_id,</if>
|
||||
<if test="zoneCode != null">zone_code,</if>
|
||||
<if test="zoneName != null">zone_name,</if>
|
||||
<if test="zdId != null">zd_id,</if>
|
||||
<if test="wsdId != null">wsd_id,</if>
|
||||
<if test="weightLabel != null">weight_label,</if>
|
||||
@@ -83,6 +93,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="productName != null">#{productName},</if>
|
||||
<if test="countryCode != null">#{countryCode},</if>
|
||||
<if test="countryName != null">#{countryName},</if>
|
||||
<if test="zoneId != null">#{zoneId},</if>
|
||||
<if test="zoneCode != null">#{zoneCode},</if>
|
||||
<if test="zoneName != null">#{zoneName},</if>
|
||||
<if test="zdId != null">#{zdId},</if>
|
||||
<if test="wsdId != null">#{wsdId},</if>
|
||||
<if test="weightLabel != null">#{weightLabel},</if>
|
||||
@@ -103,6 +116,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="productName != null">product_name = #{productName},</if>
|
||||
country_code = #{countryCode},
|
||||
country_name = #{countryName},
|
||||
zone_id = #{zoneId},
|
||||
zone_code = #{zoneCode},
|
||||
zone_name = #{zoneName},
|
||||
zd_id = #{zdId},
|
||||
wsd_id = #{wsdId},
|
||||
weight_label = #{weightLabel},
|
||||
@@ -149,6 +165,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
1 = 0
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="zoneCode != null and zoneCode != ''">
|
||||
and (zone_code is null or upper(zone_code) = upper(#{zoneCode}))
|
||||
</if>
|
||||
<if test="weightLabel != null and weightLabel != ''">
|
||||
and (weight_label is null or weight_label = #{weightLabel} or weight_label like concat('%', #{weightLabel}, '%'))
|
||||
</if>
|
||||
@@ -156,6 +175,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
)
|
||||
order by
|
||||
case when #{productCountryId} is not null and product_country_id = #{productCountryId} then 0 else 1 end,
|
||||
case when #{zoneCode} is not null and upper(ifnull(zone_code,'')) = upper(#{zoneCode}) then 0 else 1 end,
|
||||
case when zone_code is null then 1 else 0 end,
|
||||
psd_id asc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
@@ -38,9 +38,12 @@ CREATE TABLE IF NOT EXISTS prc_price_set_detail (
|
||||
price_set_id BIGINT(20) NOT NULL COMMENT '价格套餐id',
|
||||
product_code VARCHAR(10) NOT NULL COMMENT '产品代码',
|
||||
product_name VARCHAR(50) DEFAULT NULL COMMENT '产品名称',
|
||||
country_code CHAR(2) DEFAULT NULL COMMENT '国家二字码',
|
||||
country_name VARCHAR(50) DEFAULT NULL COMMENT '国家名称',
|
||||
zd_id BIGINT(20) DEFAULT NULL COMMENT '收费区划id',
|
||||
country_code CHAR(2) DEFAULT NULL COMMENT 'country ISO2',
|
||||
country_name VARCHAR(50) DEFAULT NULL COMMENT 'country name',
|
||||
zone_id BIGINT(20) DEFAULT NULL COMMENT 'prc_zone.zone_id',
|
||||
zone_code VARCHAR(50) DEFAULT NULL COMMENT 'zone code e.g. Z1',
|
||||
zone_name VARCHAR(100) DEFAULT NULL COMMENT 'zone display name',
|
||||
zd_id BIGINT(20) DEFAULT NULL COMMENT 'legacy zone division id',
|
||||
wsd_id BIGINT(20) DEFAULT NULL COMMENT '重量段明细id',
|
||||
weight_label VARCHAR(50) DEFAULT NULL COMMENT '重量段展示',
|
||||
product_country_id BIGINT(20) DEFAULT NULL COMMENT '关联 bms_product_country.detail_id',
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
-- Add zone fields to prc_price_set_detail (nullable when country has no zone)
|
||||
-- Idempotent: safe to re-run
|
||||
-- charset: utf8mb4
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- zone_id
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'zone_id'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD COLUMN zone_id BIGINT(20) DEFAULT NULL COMMENT ''prc_zone.zone_id'' AFTER country_name',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- zone_code
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'zone_code'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD COLUMN zone_code VARCHAR(50) DEFAULT NULL COMMENT ''zone code e.g. Z1'' AFTER zone_id',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- zone_name
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'zone_name'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD COLUMN zone_name VARCHAR(100) DEFAULT NULL COMMENT ''zone display name'' AFTER zone_code',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- index
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_zone'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_zone (zone_id)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_country_zone'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_country_zone (price_set_id, product_code, country_code, zone_code)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
@@ -34,6 +34,9 @@ CREATE TABLE IF NOT EXISTS bms_product_country (
|
||||
product_code varchar(20) NOT NULL COMMENT '产品代码',
|
||||
country_name varchar(50) NOT NULL COMMENT '国家中文名',
|
||||
country_code char(2) DEFAULT NULL COMMENT 'ISO2,可空待补齐',
|
||||
zone_id bigint DEFAULT NULL COMMENT 'prc_zone.zone_id',
|
||||
zone_code varchar(50) DEFAULT NULL COMMENT 'zone code e.g. Z1',
|
||||
zone_name varchar(100) DEFAULT NULL COMMENT 'zone display name',
|
||||
weight_min decimal(10,3) DEFAULT NULL COMMENT '重量段最小值KG(含)',
|
||||
weight_max decimal(10,3) DEFAULT NULL COMMENT '重量段最大值KG(含)',
|
||||
freight_price decimal(10,4) DEFAULT NULL COMMENT '运费 RMB/KG',
|
||||
@@ -47,9 +50,11 @@ CREATE TABLE IF NOT EXISTS bms_product_country (
|
||||
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (detail_id),
|
||||
UNIQUE KEY uk_product_country_weight_eff (product_code, country_name, weight_min, weight_max, effective_date),
|
||||
UNIQUE KEY uk_product_country_zone_weight_eff (product_code, country_name, zone_code, weight_min, weight_max, effective_date),
|
||||
KEY idx_product_country_pid (product_id),
|
||||
KEY idx_country_code (country_code),
|
||||
KEY idx_product_country_zone (zone_id),
|
||||
KEY idx_product_country_zone_code (country_code, zone_code),
|
||||
KEY idx_weight_match (product_code, country_code, weight_min, weight_max),
|
||||
KEY idx_product_eff (product_code, effective_date, expire_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品国家明细-重量段+运费挂号费+尺寸/尾程/时效';
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
-- Add zone fields to bms_product_country (nullable when no zone)
|
||||
-- Idempotent: safe to re-run (skip if column/index already exists)
|
||||
-- charset: utf8mb4
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- zone_id
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND COLUMN_NAME = 'zone_id'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE bms_product_country ADD COLUMN zone_id BIGINT(20) DEFAULT NULL COMMENT ''prc_zone.zone_id'' AFTER country_code',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- zone_code
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND COLUMN_NAME = 'zone_code'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE bms_product_country ADD COLUMN zone_code VARCHAR(50) DEFAULT NULL COMMENT ''zone code e.g. Z1'' AFTER zone_id',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- zone_name
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND COLUMN_NAME = 'zone_name'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE bms_product_country ADD COLUMN zone_name VARCHAR(100) DEFAULT NULL COMMENT ''zone display name'' AFTER zone_code',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- indexes
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'idx_product_country_zone'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE bms_product_country ADD INDEX idx_product_country_zone (zone_id)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'idx_product_country_zone_code'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE bms_product_country ADD INDEX idx_product_country_zone_code (country_code, zone_code)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- unique key: drop old, add new
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'uk_product_country_weight_eff'
|
||||
);
|
||||
SET @sql := IF(@has > 0,
|
||||
'ALTER TABLE bms_product_country DROP INDEX uk_product_country_weight_eff',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'uk_product_country_zone_weight_eff'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE bms_product_country ADD UNIQUE KEY uk_product_country_zone_weight_eff (product_code, country_name, zone_code, weight_min, weight_max, effective_date)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
Reference in New Issue
Block a user