重构产品价和红线价功能
This commit is contained in:
@@ -6,6 +6,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.price.domain.BmsProduct;
|
||||
import com.bms.price.domain.BmsProductCountry;
|
||||
import com.bms.price.domain.BmsProductImportTask;
|
||||
import com.bms.price.domain.CatalogPreviewApplyRequest;
|
||||
|
||||
/**
|
||||
* Product list service
|
||||
@@ -61,4 +62,42 @@ public interface IBmsProductService
|
||||
*/
|
||||
public java.util.Map<String, List<BmsProductCountry>> parseCountryQuoteExcelByProduct(byte[] bytes, String filename,
|
||||
String countryCode) throws Exception;
|
||||
|
||||
/** Parse multi-country workbook; return product list only (no DB write). */
|
||||
public java.util.Map<String, Object> previewImportCatalog(byte[] bytes, String filename) throws Exception;
|
||||
|
||||
/** Parse single-country workbook; return product list only (no DB write). */
|
||||
public java.util.Map<String, Object> previewImportCountryCatalog(byte[] bytes, String filename, String countryCode)
|
||||
throws Exception;
|
||||
|
||||
/** Submit async multi-country parse-only preview; poll getImportTask for result. */
|
||||
public String submitPreviewImportAsync(org.springframework.web.multipart.MultipartFile file) throws Exception;
|
||||
|
||||
/** One task parses up to 5 workbooks and merges by product code. */
|
||||
public String submitPreviewImportAsync(java.util.List<org.springframework.web.multipart.MultipartFile> files)
|
||||
throws Exception;
|
||||
|
||||
/** Submit async single-country parse-only preview; poll getImportTask for result. */
|
||||
public String submitPreviewImportCountryAsync(org.springframework.web.multipart.MultipartFile file, String countryCode)
|
||||
throws Exception;
|
||||
|
||||
/** One task parses up to 5 single-country workbooks and merges by product code. */
|
||||
public String submitPreviewImportCountryAsync(java.util.List<org.springframework.web.multipart.MultipartFile> files,
|
||||
String countryCode) throws Exception;
|
||||
|
||||
/** Later workbook overwrites the same product code. */
|
||||
public java.util.Map<String, Object> mergeCatalogPreview(java.util.Map<String, Object> base,
|
||||
java.util.Map<String, Object> incoming);
|
||||
|
||||
/**
|
||||
* Persist create-page preview into draft/pending version (no activate, no effectiveDate required).
|
||||
* Replaces only countries present in detailsByProductCode for each product.
|
||||
*/
|
||||
public int applyCatalogPreview(CatalogPreviewApplyRequest request);
|
||||
|
||||
/** One product in its own transaction, for parallel apply. */
|
||||
public int applyOneCatalogProduct(Long psvId, String code, BmsProduct meta, java.util.List<BmsProductCountry> details);
|
||||
|
||||
/** Returns taskId; poll import task. Products are saved on a worker pool. */
|
||||
public String submitApplyCatalogPreviewAsync(CatalogPreviewApplyRequest request);
|
||||
}
|
||||
|
||||
+40
@@ -6,6 +6,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.price.domain.BmsRedlineProduct;
|
||||
import com.bms.price.domain.BmsRedlineProductCountry;
|
||||
import com.bms.price.domain.BmsRedlineProductImportTask;
|
||||
import com.bms.price.domain.RedlineCatalogPreviewApplyRequest;
|
||||
|
||||
/**
|
||||
* redline product list service
|
||||
@@ -54,4 +55,43 @@ public interface IBmsRedlineProductService
|
||||
public java.util.Map<String, List<BmsRedlineProductCountry>> parseQuoteExcelByProduct(MultipartFile file) throws Exception;
|
||||
|
||||
public java.util.Map<String, List<BmsRedlineProductCountry>> parseQuoteExcelByProduct(byte[] bytes, String filename) throws Exception;
|
||||
|
||||
/** Parse multi-country workbook; return product list only (no DB write). */
|
||||
public java.util.Map<String, Object> previewImportCatalog(byte[] bytes, String filename) throws Exception;
|
||||
|
||||
/** Parse single-country workbook; return product list only (no DB write). */
|
||||
public java.util.Map<String, Object> previewImportCountryCatalog(byte[] bytes, String filename, String countryCode)
|
||||
throws Exception;
|
||||
|
||||
/** Submit async multi-country parse-only preview; poll getImportTask for result. */
|
||||
public String submitPreviewImportAsync(org.springframework.web.multipart.MultipartFile file) throws Exception;
|
||||
|
||||
/** One task parses up to 5 workbooks and merges by product code. */
|
||||
public String submitPreviewImportAsync(java.util.List<org.springframework.web.multipart.MultipartFile> files)
|
||||
throws Exception;
|
||||
|
||||
/** Submit async single-country parse-only preview; poll getImportTask for result. */
|
||||
public String submitPreviewImportCountryAsync(org.springframework.web.multipart.MultipartFile file, String countryCode)
|
||||
throws Exception;
|
||||
|
||||
/** One task parses up to 5 single-country workbooks and merges by product code. */
|
||||
public String submitPreviewImportCountryAsync(java.util.List<org.springframework.web.multipart.MultipartFile> files,
|
||||
String countryCode) throws Exception;
|
||||
|
||||
/** Later workbook overwrites the same product code. */
|
||||
public java.util.Map<String, Object> mergeCatalogPreview(java.util.Map<String, Object> base,
|
||||
java.util.Map<String, Object> incoming);
|
||||
|
||||
/**
|
||||
* Persist create-page preview into draft/pending version (no activate, no effectiveDate required).
|
||||
* Replaces only countries present in detailsByProductCode for each product.
|
||||
*/
|
||||
public int applyCatalogPreview(RedlineCatalogPreviewApplyRequest request);
|
||||
|
||||
/** One product in its own transaction, for parallel apply. */
|
||||
public int applyOneCatalogProduct(Long psvId, String code, BmsRedlineProduct meta,
|
||||
java.util.List<BmsRedlineProductCountry> details);
|
||||
|
||||
/** Returns taskId; poll import task. Products are saved on a worker pool. */
|
||||
public String submitApplyCatalogPreviewAsync(RedlineCatalogPreviewApplyRequest request);
|
||||
}
|
||||
|
||||
+6
-7
@@ -11,20 +11,19 @@ public interface IPrcPriceExcelFileService
|
||||
{
|
||||
public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId);
|
||||
|
||||
/** Import Excel attachments linked to this product_code via product.pef_id */
|
||||
public List<PrcPriceExcelFile> selectByProductCode(String productCode);
|
||||
|
||||
List<PrcPriceExcelFile> selectByRedlineProductCode(String productCode);
|
||||
|
||||
/** Import excel files for a price-set version via prc_price_set_product.psv_id. */
|
||||
/** Excel files bound to a price version */
|
||||
List<PrcPriceExcelFile> selectByPsvId(Long psvId);
|
||||
|
||||
/**
|
||||
* Persist uploaded Excel under upload/price/excel and insert metadata row.
|
||||
*
|
||||
* @param excelType PRODUCT / REDLINE / SALES
|
||||
* @param psvId version id (nullable for draft preview; bind later)
|
||||
* @return inserted row with pefId
|
||||
*/
|
||||
public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes,
|
||||
String filename, String operName) throws Exception;
|
||||
String filename, String operName, Long psvId) throws Exception;
|
||||
|
||||
/** Bind an existing attachment row to a version */
|
||||
public int bindPsvId(Long pefId, Long psvId);
|
||||
}
|
||||
|
||||
@@ -128,6 +128,15 @@ public interface IPrcPriceSetService
|
||||
public String submitImportDetailPricesAsync(Long priceSetId, Long psvId, MultipartFile file, String operName,
|
||||
boolean updateOnly, boolean replaceByProductCountry, String countryCode) throws Exception;
|
||||
|
||||
/** One task imports up to 5 workbooks in order. */
|
||||
public String submitImportDetailPricesAsync(Long priceSetId, Long psvId, List<MultipartFile> files, String operName,
|
||||
boolean updateOnly, boolean replaceByProductCountry, String countryCode) throws Exception;
|
||||
|
||||
/** Parse up to 5 workbooks in one call. Later file wins on the same product+country scope. */
|
||||
public Map<String, Object> previewImportDetailPricesMany(List<byte[]> bytesList, List<String> names,
|
||||
boolean updateOnly, List<PrcPriceSetDetail> existingDetails, boolean replaceByProductCountry, String countryCode)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Sync worker: parse bytes and upsert FIXED detail prices matched by
|
||||
* product + country + zone(if any) + weight band (insert when missing unless updateOnly).
|
||||
|
||||
+568
-52
@@ -37,6 +37,7 @@ import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.BmsProduct;
|
||||
import com.bms.price.domain.BmsProductCountry;
|
||||
import com.bms.price.domain.BmsProductImportTask;
|
||||
import com.bms.price.domain.CatalogPreviewApplyRequest;
|
||||
import com.bms.price.domain.PrcZone;
|
||||
import com.bms.price.mapper.BmsProductCountryMapper;
|
||||
import com.bms.price.mapper.BmsProductMapper;
|
||||
@@ -45,6 +46,9 @@ import com.bms.price.domain.PrcPriceExcelFile;
|
||||
import com.bms.price.service.IBmsProductService;
|
||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
import com.bms.price.service.IPrcSurchargeRelService;
|
||||
import com.bms.price.domain.PriceType;
|
||||
import com.bms.price.domain.PrcPriceSetVersion;
|
||||
import com.bms.price.service.support.CatalogVersionSupport;
|
||||
import com.bms.price.task.BmsProductImportAsyncRunner;
|
||||
import com.bms.price.util.ZoneCodeAbbrevs;
|
||||
import com.bms.system.domain.BmsRegion;
|
||||
@@ -74,6 +78,9 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
"(\\d+(?:\\.\\d+)?)\\s*[<\\u2264]=?\\s*W\\s*[<\\u2264]=?\\s*(\\d+(?:\\.\\d+)?)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@Autowired
|
||||
private CatalogVersionSupport catalogVersionSupport;
|
||||
|
||||
@Autowired
|
||||
private BmsProductMapper productMapper;
|
||||
|
||||
@@ -131,9 +138,12 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
applyProductStatus(product);
|
||||
assertCodeDateUnique(product, null);
|
||||
product.setPriceType(PriceType.PRODUCT);
|
||||
Long psvId = catalogVersionSupport.requirePsvId(PriceType.PRODUCT, product.getPsvId(), product.getEffectiveDate());
|
||||
product.setPsvId(psvId);
|
||||
int rows = productMapper.insertBmsProduct(product);
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), product.getEffectiveDate(), product.getProductId());
|
||||
productMapper.refreshStatusByDates();
|
||||
catalogVersionSupport.activateVersion(psvId, product.getEffectiveDate());
|
||||
return rows;
|
||||
}
|
||||
|
||||
@@ -297,7 +307,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
Date effectiveDate) throws Exception
|
||||
{
|
||||
validateImportFile(file);
|
||||
Date eff = requireImportEffectiveDate(effectiveDate);
|
||||
Date eff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
byte[] bytes = file.getBytes();
|
||||
String filename = file.getOriginalFilename();
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
@@ -321,7 +331,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
String taskId, Date effectiveDate) throws Exception
|
||||
{
|
||||
validateImportBytes(bytes, filename);
|
||||
Date forcedEff = requireImportEffectiveDate(effectiveDate);
|
||||
Date forcedEff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
reportImportProgress(taskId, 10, "parsing excel");
|
||||
|
||||
try (InputStream is = new java.io.ByteArrayInputStream(bytes); Workbook workbook = WorkbookFactory.create(is))
|
||||
@@ -354,9 +364,11 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
assertImportProductsNoDupCountryWeight(workbook, products, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode);
|
||||
|
||||
Long importPsvId = forcedEff == null
|
||||
? catalogVersionSupport.ensureDraftVersion(PriceType.PRODUCT, null, false).getPsvId()
|
||||
: catalogVersionSupport.requirePsvId(PriceType.PRODUCT, null, forcedEff);
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName, importPsvId);
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
@@ -376,7 +388,10 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
try
|
||||
{
|
||||
product.setEffectiveDate(forcedEff);
|
||||
product.setPefId(pefId);
|
||||
if (importPsvId != null)
|
||||
{
|
||||
product.setPsvId(importPsvId);
|
||||
}
|
||||
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode, unmatchedCountries, forcedEff);
|
||||
transactionManager.commit(tx);
|
||||
@@ -432,7 +447,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
}
|
||||
Date eff = requireImportEffectiveDate(effectiveDate);
|
||||
Date eff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
byte[] bytes = file.getBytes();
|
||||
String filename = file.getOriginalFilename();
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
@@ -446,7 +461,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
String taskId, Date effectiveDate) throws Exception
|
||||
{
|
||||
validateImportBytes(bytes, filename);
|
||||
Date forcedEff = requireImportEffectiveDate(effectiveDate);
|
||||
Date forcedEff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
@@ -477,9 +492,11 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
// Pre-check all products: reject whole import before any write if any is earlier than prev version
|
||||
assertImportProductsEffectiveNotBeforePrev(products, forcedEff);
|
||||
|
||||
Long importPsvId = forcedEff == null
|
||||
? catalogVersionSupport.ensureDraftVersion(PriceType.PRODUCT, null, false).getPsvId()
|
||||
: catalogVersionSupport.requirePsvId(PriceType.PRODUCT, null, forcedEff);
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName, importPsvId);
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
@@ -508,7 +525,10 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
product.setDestRegion(countryLabel);
|
||||
product.setSourceSheet(block.sheetName);
|
||||
product.setEffectiveDate(forcedEff);
|
||||
product.setPefId(pefId);
|
||||
if (importPsvId != null)
|
||||
{
|
||||
product.setPsvId(importPsvId);
|
||||
}
|
||||
if (StringUtils.isEmpty(product.getTaxMode()))
|
||||
{
|
||||
product.setTaxMode(resolveTaxMode(product.getProductName()));
|
||||
@@ -608,6 +628,12 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
|
||||
@Override
|
||||
public Map<String, List<BmsProductCountry>> parseQuoteExcelByProduct(byte[] bytes, String filename) throws Exception
|
||||
{
|
||||
return readQuoteWorkbook(bytes, filename).byCode;
|
||||
}
|
||||
|
||||
/** One workbook open; each price sheet is parsed once. */
|
||||
private QuoteWorkbook readQuoteWorkbook(byte[] bytes, String filename) throws Exception
|
||||
{
|
||||
if (bytes == null || bytes.length == 0)
|
||||
{
|
||||
@@ -630,13 +656,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.noData"));
|
||||
}
|
||||
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<>();
|
||||
Map<String, List<BmsProduct>> ownersBySheet = new LinkedHashMap<String, List<BmsProduct>>();
|
||||
for (BmsProduct product : products)
|
||||
{
|
||||
if (product == null || StringUtils.isEmpty(product.getProductCode()))
|
||||
@@ -648,24 +668,89 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<BmsProductCountry> rows = parseProductSheet(productSheet, product,
|
||||
regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries);
|
||||
if (rows == null || rows.isEmpty())
|
||||
List<BmsProduct> owners = ownersBySheet.get(productSheet.getSheetName());
|
||||
if (owners == null)
|
||||
{
|
||||
owners = new ArrayList<BmsProduct>();
|
||||
ownersBySheet.put(productSheet.getSheetName(), owners);
|
||||
}
|
||||
owners.add(product);
|
||||
}
|
||||
Map<String, BmsRegion> regionByNameCn = loadRegionByNameCn();
|
||||
List<BmsRegion> euRegions = loadEuRegions(regionByNameCn);
|
||||
Map<String, PrcZone> zoneByCountryAndCode = loadZoneByCountryAndCode();
|
||||
Set<String> unmatchedCountries = new HashSet<String>();
|
||||
Map<String, List<BmsProductCountry>> out = new LinkedHashMap<String, List<BmsProductCountry>>();
|
||||
List<String> tips = new ArrayList<String>();
|
||||
for (int i = 0; i < workbook.getNumberOfSheets(); i++)
|
||||
{
|
||||
String sheetName = workbook.getSheetName(i);
|
||||
if (shouldSkipCountryPriceSheet(sheetName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
assertNoDuplicateCountryWeightRows(product, rows);
|
||||
String code = product.getProductCode().trim().toUpperCase(Locale.ROOT);
|
||||
out.put(code, rows);
|
||||
Sheet sheet = workbook.getSheetAt(i);
|
||||
if (sheet == null || findHeaderCell(sheet, "\u56fd\u5bb6") == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<BmsProduct> owners = ownersBySheet.get(sheetName);
|
||||
BmsProduct parseAs = (owners == null || owners.isEmpty()) ? null : owners.get(0);
|
||||
if (parseAs == null)
|
||||
{
|
||||
parseAs = new BmsProduct();
|
||||
parseAs.setProductCode(sheetName);
|
||||
parseAs.setProductName(sheetName);
|
||||
}
|
||||
List<BmsProductCountry> rows = parseProductSheet(sheet, parseAs,
|
||||
regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries);
|
||||
String tip = buildDuplicateCountryWeightTip(parseAs, rows);
|
||||
if (StringUtils.isNotEmpty(tip))
|
||||
{
|
||||
tips.add(tip);
|
||||
}
|
||||
if (owners == null || owners.isEmpty() || rows == null || rows.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int p = 0; p < owners.size(); p++)
|
||||
{
|
||||
BmsProduct owner = owners.get(p);
|
||||
List<BmsProductCountry> use = rows;
|
||||
if (p > 0)
|
||||
{
|
||||
use = parseProductSheet(sheet, owner,
|
||||
regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries);
|
||||
}
|
||||
if (use == null || use.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
out.put(owner.getProductCode().trim().toUpperCase(Locale.ROOT), use);
|
||||
}
|
||||
}
|
||||
if (!tips.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.dupCountry",
|
||||
String.join("<br/>", tips)));
|
||||
}
|
||||
if (out.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noPriceRows"));
|
||||
}
|
||||
return out;
|
||||
QuoteWorkbook parsed = new QuoteWorkbook();
|
||||
parsed.products = products;
|
||||
parsed.byCode = out;
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class QuoteWorkbook
|
||||
{
|
||||
private List<BmsProduct> products;
|
||||
private Map<String, List<BmsProductCountry>> byCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<BmsProductCountry>> parseCountryQuoteExcelByProduct(byte[] bytes, String filename,
|
||||
String countryCode) throws Exception
|
||||
@@ -727,6 +812,415 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> previewImportCatalog(byte[] bytes, String filename) throws Exception
|
||||
{
|
||||
QuoteWorkbook parsed = readQuoteWorkbook(bytes, filename);
|
||||
return buildCatalogPreview(parsed.products, parsed.byCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> previewImportCountryCatalog(byte[] bytes, String filename, String countryCode)
|
||||
throws Exception
|
||||
{
|
||||
Map<String, List<BmsProductCountry>> byCode = parseCountryQuoteExcelByProduct(bytes, filename, countryCode);
|
||||
String countryLabel = resolveCountryLabel(countryCode);
|
||||
try (InputStream is = new java.io.ByteArrayInputStream(bytes); Workbook workbook = WorkbookFactory.create(is))
|
||||
{
|
||||
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
|
||||
List<BmsProduct> catalogProducts = dedupeCatalogProducts(parseCountryCatalog(catalog));
|
||||
if (StringUtils.isNotEmpty(countryLabel) && catalogProducts != null)
|
||||
{
|
||||
for (BmsProduct p : catalogProducts)
|
||||
{
|
||||
if (p != null)
|
||||
{
|
||||
p.setDestRegion(countryLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buildCatalogPreview(catalogProducts, byCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportAsync(MultipartFile file) throws Exception
|
||||
{
|
||||
return submitPreviewImportAsync(java.util.Collections.singletonList(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportAsync(List<MultipartFile> files) throws Exception
|
||||
{
|
||||
List<byte[]> bytesList = new ArrayList<byte[]>();
|
||||
List<String> names = new ArrayList<String>();
|
||||
readPreviewFiles(files, bytesList, names);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
importAsyncRunner.put(taskId, BmsProductImportTask.STATUS_PENDING, 0, "queued", null);
|
||||
importAsyncRunner.runPreviewAll(taskId, bytesList, names);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportCountryAsync(MultipartFile file, String countryCode) throws Exception
|
||||
{
|
||||
return submitPreviewImportCountryAsync(java.util.Collections.singletonList(file), countryCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportCountryAsync(List<MultipartFile> files, String countryCode) throws Exception
|
||||
{
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
}
|
||||
List<byte[]> bytesList = new ArrayList<byte[]>();
|
||||
List<String> names = new ArrayList<String>();
|
||||
readPreviewFiles(files, bytesList, names);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
importAsyncRunner.put(taskId, BmsProductImportTask.STATUS_PENDING, 0, "queued", null);
|
||||
importAsyncRunner.runPreviewCountry(taskId, bytesList, names, countryCode.trim());
|
||||
return taskId;
|
||||
}
|
||||
|
||||
private void readPreviewFiles(List<MultipartFile> files, List<byte[]> bytesList, List<String> names) throws Exception
|
||||
{
|
||||
if (files == null || files.isEmpty())
|
||||
{
|
||||
throw new ServiceException("file required");
|
||||
}
|
||||
if (files.size() > com.bms.price.service.support.UploadFileBatch.MAX_FILES)
|
||||
{
|
||||
throw new ServiceException("\u5355\u6b21\u6700\u591a\u5bfc\u5165 5 \u4e2a\u6587\u4ef6");
|
||||
}
|
||||
for (MultipartFile file : files)
|
||||
{
|
||||
validateImportFile(file);
|
||||
bytesList.add(file.getBytes());
|
||||
names.add(file.getOriginalFilename());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> mergeCatalogPreview(Map<String, Object> base, Map<String, Object> incoming)
|
||||
{
|
||||
if (base == null || base.isEmpty())
|
||||
{
|
||||
return incoming;
|
||||
}
|
||||
if (incoming == null || incoming.isEmpty())
|
||||
{
|
||||
return base;
|
||||
}
|
||||
Map<String, BmsProduct> products = new LinkedHashMap<String, BmsProduct>();
|
||||
putPreviewProducts(products, castProductList(base.get("products")));
|
||||
putPreviewProducts(products, castProductList(incoming.get("products")));
|
||||
Map<String, List<BmsProductCountry>> details = new LinkedHashMap<String, List<BmsProductCountry>>();
|
||||
putPreviewDetails(details, castDetailMap(base.get("detailsByProductCode")));
|
||||
putPreviewDetails(details, castDetailMap(incoming.get("detailsByProductCode")));
|
||||
int detailCount = 0;
|
||||
for (List<BmsProductCountry> rows : details.values())
|
||||
{
|
||||
if (rows != null)
|
||||
{
|
||||
detailCount += rows.size();
|
||||
}
|
||||
}
|
||||
Map<String, Object> out = new HashMap<String, Object>();
|
||||
out.put("products", new ArrayList<BmsProduct>(products.values()));
|
||||
out.put("productCount", Integer.valueOf(products.size()));
|
||||
out.put("detailCount", Integer.valueOf(detailCount));
|
||||
out.put("detailsByProductCode", details);
|
||||
return out;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<BmsProduct> castProductList(Object value)
|
||||
{
|
||||
if (value instanceof List)
|
||||
{
|
||||
return (List<BmsProduct>) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, List<BmsProductCountry>> castDetailMap(Object value)
|
||||
{
|
||||
if (value instanceof Map)
|
||||
{
|
||||
return (Map<String, List<BmsProductCountry>>) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void putPreviewProducts(Map<String, BmsProduct> products, List<BmsProduct> list)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (BmsProduct p : list)
|
||||
{
|
||||
if (p == null || StringUtils.isEmpty(p.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
products.put(p.getProductCode().trim().toUpperCase(Locale.ROOT), p);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putPreviewDetails(Map<String, List<BmsProductCountry>> details,
|
||||
Map<String, List<BmsProductCountry>> incoming)
|
||||
{
|
||||
if (incoming == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, List<BmsProductCountry>> e : incoming.entrySet())
|
||||
{
|
||||
if (e == null || StringUtils.isEmpty(e.getKey()) || e.getValue() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
details.put(e.getKey().trim().toUpperCase(Locale.ROOT), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int applyCatalogPreview(CatalogPreviewApplyRequest request)
|
||||
{
|
||||
if (request == null || request.getPsvId() == null)
|
||||
{
|
||||
throw new ServiceException("psvId required");
|
||||
}
|
||||
Long psvId = request.getPsvId();
|
||||
catalogVersionSupport.requireEditableCatalogVersion(psvId, PriceType.PRODUCT);
|
||||
Map<String, List<BmsProductCountry>> byCode = request.getDetailsByProductCode();
|
||||
if (byCode == null || byCode.isEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Map<String, BmsProduct> productMeta = new LinkedHashMap<String, BmsProduct>();
|
||||
if (request.getProducts() != null)
|
||||
{
|
||||
for (BmsProduct p : request.getProducts())
|
||||
{
|
||||
if (p == null || StringUtils.isEmpty(p.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
productMeta.put(p.getProductCode().trim().toUpperCase(Locale.ROOT), p);
|
||||
}
|
||||
}
|
||||
int detailRows = 0;
|
||||
for (Map.Entry<String, List<BmsProductCountry>> e : byCode.entrySet())
|
||||
{
|
||||
if (e == null || StringUtils.isEmpty(e.getKey()) || e.getValue() == null || e.getValue().isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String code = e.getKey().trim().toUpperCase(Locale.ROOT);
|
||||
detailRows += applyOneCatalogProduct(psvId, code, productMeta.get(code), e.getValue());
|
||||
}
|
||||
return detailRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int applyOneCatalogProduct(Long psvId, String code, BmsProduct meta, List<BmsProductCountry> details)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(code) || details == null || details.isEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
String key = code.trim().toUpperCase(Locale.ROOT);
|
||||
PrcPriceSetVersion ver = catalogVersionSupport.findVersion(psvId);
|
||||
Date eff = ver == null ? null : ver.getEffectiveTime();
|
||||
Date exp = ver == null ? null : ver.getExpireTime();
|
||||
String st = ver == null || StringUtils.isEmpty(ver.getStatus()) ? "0" : ver.getStatus();
|
||||
String destRegion = resolveDestRegion(meta == null ? null : meta.getDestRegion(), details);
|
||||
BmsProduct existing = productMapper.selectByCodeAndPsvId(key, psvId);
|
||||
if (existing == null)
|
||||
{
|
||||
BmsProduct row = new BmsProduct();
|
||||
row.setPsvId(psvId);
|
||||
row.setPriceType(PriceType.PRODUCT);
|
||||
row.setProductCode(meta != null && StringUtils.isNotEmpty(meta.getProductCode())
|
||||
? meta.getProductCode().trim() : key);
|
||||
row.setProductName(meta == null ? null : meta.getProductName());
|
||||
row.setServiceLevel(meta == null ? null : meta.getServiceLevel());
|
||||
row.setCargoAttr(meta == null ? null : meta.getCargoAttr());
|
||||
row.setDestRegion(destRegion);
|
||||
row.setTaxMode(meta != null && StringUtils.isNotEmpty(meta.getTaxMode())
|
||||
? meta.getTaxMode() : "TAX_INCLUDED");
|
||||
row.setEffectiveDate(eff);
|
||||
row.setExpireDate(exp);
|
||||
row.setStatus(st);
|
||||
row.setIsDel(BmsProduct.IS_DEL_NO);
|
||||
productMapper.insertBmsProduct(row);
|
||||
existing = row;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (meta != null)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(meta.getProductName()))
|
||||
{
|
||||
existing.setProductName(meta.getProductName());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(meta.getServiceLevel()))
|
||||
{
|
||||
existing.setServiceLevel(meta.getServiceLevel());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(meta.getCargoAttr()))
|
||||
{
|
||||
existing.setCargoAttr(meta.getCargoAttr());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotEmpty(destRegion))
|
||||
{
|
||||
existing.setDestRegion(destRegion);
|
||||
}
|
||||
existing.setEffectiveDate(eff);
|
||||
existing.setExpireDate(exp);
|
||||
existing.setStatus(st);
|
||||
productMapper.updateBmsProduct(existing);
|
||||
}
|
||||
|
||||
Set<String> replaced = new HashSet<String>();
|
||||
List<BmsProductCountry> toInsert = new ArrayList<BmsProductCountry>();
|
||||
for (BmsProductCountry d : details)
|
||||
{
|
||||
if (d == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String ckey = countryIdentityKey(d);
|
||||
if (StringUtils.isNotEmpty(ckey) && !replaced.contains(ckey))
|
||||
{
|
||||
countryMapper.physicalDeleteByPspIdAndCountry(existing.getProductId(), d.getCountryCode(),
|
||||
d.getCountryName());
|
||||
replaced.add(ckey);
|
||||
}
|
||||
d.setDetailId(null);
|
||||
d.setProductId(existing.getProductId());
|
||||
d.setPsvId(psvId);
|
||||
d.setProductCode(existing.getProductCode());
|
||||
if (StringUtils.isEmpty(d.getProductName()))
|
||||
{
|
||||
d.setProductName(existing.getProductName());
|
||||
}
|
||||
d.setEffectiveDate(eff);
|
||||
d.setExpireDate(exp);
|
||||
d.setStatus(st);
|
||||
d.setIsDel(BmsProduct.IS_DEL_NO);
|
||||
toInsert.add(d);
|
||||
}
|
||||
insertProductCountryChunks(toInsert);
|
||||
return toInsert.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitApplyCatalogPreviewAsync(CatalogPreviewApplyRequest request)
|
||||
{
|
||||
if (request == null || request.getPsvId() == null)
|
||||
{
|
||||
throw new ServiceException("psvId required");
|
||||
}
|
||||
catalogVersionSupport.requireEditableCatalogVersion(request.getPsvId(), PriceType.PRODUCT);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
importAsyncRunner.put(taskId, BmsProductImportTask.STATUS_PENDING, 0, "queued", null);
|
||||
importAsyncRunner.runApplyPreview(taskId, request);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
private void insertProductCountryChunks(List<BmsProductCountry> rows)
|
||||
{
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int size = 200;
|
||||
for (int i = 0; i < rows.size(); i += size)
|
||||
{
|
||||
int end = Math.min(i + size, rows.size());
|
||||
countryMapper.insertBmsProductCountryBatch(new ArrayList<BmsProductCountry>(rows.subList(i, end)));
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveCountryLabel(String countryCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
BmsRegion region = regionMapper.selectRegionByCode2(countryCode.trim().toUpperCase(Locale.ROOT));
|
||||
if (region == null)
|
||||
{
|
||||
return countryCode.trim().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
return StringUtils.isNotEmpty(region.getCountryNameCn()) ? region.getCountryNameCn()
|
||||
: region.getCountryCode2();
|
||||
}
|
||||
|
||||
private String resolveDestRegion(String preferred, List<BmsProductCountry> details)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(preferred))
|
||||
{
|
||||
return preferred.trim();
|
||||
}
|
||||
if (details == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (BmsProductCountry d : details)
|
||||
{
|
||||
if (d != null && StringUtils.isNotEmpty(d.getCountryName()))
|
||||
{
|
||||
return d.getCountryName().trim();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildCatalogPreview(List<BmsProduct> catalogProducts,
|
||||
Map<String, List<BmsProductCountry>> byCode)
|
||||
{
|
||||
List<BmsProduct> products = new ArrayList<BmsProduct>();
|
||||
int detailCount = 0;
|
||||
if (catalogProducts != null)
|
||||
{
|
||||
for (BmsProduct src : catalogProducts)
|
||||
{
|
||||
if (src == null || StringUtils.isEmpty(src.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String code = src.getProductCode().trim().toUpperCase(Locale.ROOT);
|
||||
List<BmsProductCountry> details = byCode == null ? null : byCode.get(code);
|
||||
if (details == null || details.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BmsProduct row = new BmsProduct();
|
||||
row.setProductCode(src.getProductCode());
|
||||
row.setProductName(src.getProductName());
|
||||
row.setServiceLevel(src.getServiceLevel());
|
||||
row.setCargoAttr(src.getCargoAttr());
|
||||
row.setDestRegion(src.getDestRegion());
|
||||
products.add(row);
|
||||
detailCount += details.size();
|
||||
}
|
||||
}
|
||||
Map<String, Object> out = new HashMap<String, Object>();
|
||||
out.put("products", products);
|
||||
out.put("productCount", Integer.valueOf(products.size()));
|
||||
out.put("detailCount", Integer.valueOf(detailCount));
|
||||
out.put("detailsByProductCode", byCode == null ? new LinkedHashMap<String, List<BmsProductCountry>>() : byCode);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @return int[]{added, skipped, countryRows} */
|
||||
private int[] importOneProduct(Workbook workbook, BmsProduct product, String operName,
|
||||
@@ -753,22 +1247,28 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
assertNoDuplicateCountryWeightRows(product, countries);
|
||||
// UI-selected effective date wins over Excel; previous open product/country rows expire at this date
|
||||
Date newEff = requireImportEffectiveDate(forcedEffectiveDate != null ? forcedEffectiveDate
|
||||
: product.getEffectiveDate());
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
Date newEff = forcedEffectiveDate == null && product.getEffectiveDate() == null
|
||||
? null
|
||||
: requireImportEffectiveDate(forcedEffectiveDate != null ? forcedEffectiveDate
|
||||
: product.getEffectiveDate());
|
||||
if (newEff != null)
|
||||
{
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
}
|
||||
BmsProduct existing = productMapper.selectBmsProductByCodeAndDate(
|
||||
product.getProductCode(), newEff);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
product.setProductId(existing.getProductId());
|
||||
patchProductPefId(product, operName);
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 0, 1, countryRows };
|
||||
}
|
||||
|
||||
applyProductStatus(product);
|
||||
product.setPriceType(PriceType.PRODUCT);
|
||||
product.setPsvId(catalogVersionSupport.requirePsvId(PriceType.PRODUCT, product.getPsvId(), newEff));
|
||||
try
|
||||
{
|
||||
productMapper.insertBmsProduct(product);
|
||||
@@ -781,26 +1281,22 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
productMapper.refreshStatusByDates();
|
||||
if (newEff != null)
|
||||
{
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
if (product.getPsvId() != null)
|
||||
{
|
||||
catalogVersionSupport.activateVersion(product.getPsvId(), newEff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
productMapper.refreshStatusByDates();
|
||||
}
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 1, 0, countryRows };
|
||||
}
|
||||
|
||||
private void patchProductPefId(BmsProduct product, String operName)
|
||||
{
|
||||
if (product == null || product.getProductId() == null || product.getPefId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BmsProduct pefPatch = new BmsProduct();
|
||||
pefPatch.setProductId(product.getProductId());
|
||||
pefPatch.setPefId(product.getPefId());
|
||||
pefPatch.setUpdateBy(operName);
|
||||
productMapper.updateBmsProduct(pefPatch);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save country rows by effective_date.
|
||||
* For each product_code + country in the Excel:
|
||||
@@ -842,6 +1338,8 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
countryMapper.expireOpenByProductCodeAndCountry(
|
||||
productCode, incoming.getCountryCode(), incoming.getCountryName(), newEff);
|
||||
countryMapper.deleteSurchargeRelByProductCodeAndCountryOnDate(
|
||||
productCode, incoming.getCountryCode(), incoming.getCountryName(), newEff);
|
||||
countryMapper.deleteOpenByProductCodeAndCountryOnDate(
|
||||
productCode, incoming.getCountryCode(), incoming.getCountryName(), newEff);
|
||||
replacedCountries.add(countryKey);
|
||||
@@ -2127,6 +2625,10 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
/** Batch pre-check: fail whole import before any DB write. */
|
||||
private void assertImportProductsEffectiveNotBeforePrev(List<BmsProduct> products, Date forcedEff)
|
||||
{
|
||||
if (forcedEff == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Date newEff = requireImportEffectiveDate(forcedEff);
|
||||
if (products == null || products.isEmpty())
|
||||
{
|
||||
@@ -2346,18 +2848,22 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
product.setSourceSheet(product.getProductName());
|
||||
}
|
||||
Date newEff = requireImportEffectiveDate(product.getEffectiveDate());
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
Date newEff = product.getEffectiveDate() == null ? null : requireImportEffectiveDate(product.getEffectiveDate());
|
||||
if (newEff != null)
|
||||
{
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
}
|
||||
BmsProduct existing = productMapper.selectBmsProductByCodeAndDate(product.getProductCode(), newEff);
|
||||
if (existing != null)
|
||||
{
|
||||
product.setProductId(existing.getProductId());
|
||||
patchProductPefId(product, operName);
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 0, 1, countryRows };
|
||||
}
|
||||
applyProductStatus(product);
|
||||
product.setPriceType(PriceType.PRODUCT);
|
||||
product.setPsvId(catalogVersionSupport.requirePsvId(PriceType.PRODUCT, product.getPsvId(), newEff));
|
||||
try
|
||||
{
|
||||
productMapper.insertBmsProduct(product);
|
||||
@@ -2370,8 +2876,18 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
productMapper.refreshStatusByDates();
|
||||
if (newEff != null)
|
||||
{
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
if (product.getPsvId() != null)
|
||||
{
|
||||
catalogVersionSupport.activateVersion(product.getPsvId(), newEff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
productMapper.refreshStatusByDates();
|
||||
}
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 1, 0, countryRows };
|
||||
}
|
||||
|
||||
+528
-38
@@ -37,6 +37,7 @@ import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.BmsRedlineProduct;
|
||||
import com.bms.price.domain.BmsRedlineProductCountry;
|
||||
import com.bms.price.domain.BmsRedlineProductImportTask;
|
||||
import com.bms.price.domain.RedlineCatalogPreviewApplyRequest;
|
||||
import com.bms.price.domain.PrcZone;
|
||||
import com.bms.price.domain.PrcSurchargeRel;
|
||||
import com.bms.price.mapper.BmsRedlineProductCountryMapper;
|
||||
@@ -46,6 +47,9 @@ import com.bms.price.mapper.PrcZoneMapper;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
import com.bms.price.service.IBmsRedlineProductService;
|
||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
import com.bms.price.domain.PriceType;
|
||||
import com.bms.price.domain.PrcPriceSetVersion;
|
||||
import com.bms.price.service.support.CatalogVersionSupport;
|
||||
import com.bms.price.task.BmsRedlineProductImportAsyncRunner;
|
||||
import com.bms.price.util.ZoneCodeAbbrevs;
|
||||
import com.bms.system.domain.BmsRegion;
|
||||
@@ -75,6 +79,9 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
"(\\d+(?:\\.\\d+)?)\\s*[<\\u2264]=?\\s*W\\s*[<\\u2264]=?\\s*(\\d+(?:\\.\\d+)?)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@Autowired
|
||||
private CatalogVersionSupport catalogVersionSupport;
|
||||
|
||||
@Autowired
|
||||
private BmsRedlineProductMapper productMapper;
|
||||
|
||||
@@ -132,9 +139,12 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
}
|
||||
applyProductStatus(product);
|
||||
assertCodeDateUnique(product, null);
|
||||
product.setPriceType(PriceType.REDLINE);
|
||||
Long psvId = catalogVersionSupport.requirePsvId(PriceType.REDLINE, product.getPsvId(), product.getEffectiveDate());
|
||||
product.setPsvId(psvId);
|
||||
int rows = productMapper.insertBmsRedlineProduct(product);
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), product.getEffectiveDate(), product.getProductId());
|
||||
productMapper.refreshStatusByDates();
|
||||
catalogVersionSupport.activateVersion(psvId, product.getEffectiveDate());
|
||||
return rows;
|
||||
}
|
||||
|
||||
@@ -290,7 +300,7 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
Date effectiveDate) throws Exception
|
||||
{
|
||||
validateImportFile(file);
|
||||
Date eff = requireImportEffectiveDate(effectiveDate);
|
||||
Date eff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
byte[] bytes = file.getBytes();
|
||||
String filename = file.getOriginalFilename();
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
@@ -314,7 +324,7 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
String taskId, Date effectiveDate) throws Exception
|
||||
{
|
||||
validateImportBytes(bytes, filename);
|
||||
Date forcedEff = requireImportEffectiveDate(effectiveDate);
|
||||
Date forcedEff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
reportImportProgress(taskId, 10, "parsing excel");
|
||||
|
||||
try (InputStream is = new java.io.ByteArrayInputStream(bytes); Workbook workbook = WorkbookFactory.create(is))
|
||||
@@ -346,9 +356,11 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
assertImportProductsNoDupCountryWeight(workbook, products, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode);
|
||||
|
||||
Long importPsvId = forcedEff == null
|
||||
? catalogVersionSupport.ensureDraftVersion(PriceType.REDLINE, null, false).getPsvId()
|
||||
: catalogVersionSupport.requirePsvId(PriceType.REDLINE, null, forcedEff);
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_REDLINE, forcedEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
PrcPriceExcelFile.TYPE_REDLINE, forcedEff, bytes, filename, operName, importPsvId);
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
@@ -368,7 +380,10 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
try
|
||||
{
|
||||
product.setEffectiveDate(forcedEff);
|
||||
product.setPefId(pefId);
|
||||
if (importPsvId != null)
|
||||
{
|
||||
product.setPsvId(importPsvId);
|
||||
}
|
||||
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode, unmatchedCountries, forcedEff);
|
||||
transactionManager.commit(tx);
|
||||
@@ -424,7 +439,7 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
}
|
||||
Date eff = requireImportEffectiveDate(effectiveDate);
|
||||
Date eff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
byte[] bytes = file.getBytes();
|
||||
String filename = file.getOriginalFilename();
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
@@ -438,7 +453,7 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
String taskId, Date effectiveDate) throws Exception
|
||||
{
|
||||
validateImportBytes(bytes, filename);
|
||||
Date forcedEff = requireImportEffectiveDate(effectiveDate);
|
||||
Date forcedEff = effectiveDate == null ? null : requireImportEffectiveDate(effectiveDate);
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
@@ -469,9 +484,11 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
// Pre-check all products: reject whole import before any write if any is earlier than prev version
|
||||
assertImportProductsEffectiveNotBeforePrev(products, forcedEff);
|
||||
|
||||
Long importPsvId = forcedEff == null
|
||||
? catalogVersionSupport.ensureDraftVersion(PriceType.REDLINE, null, false).getPsvId()
|
||||
: catalogVersionSupport.requirePsvId(PriceType.REDLINE, null, forcedEff);
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_REDLINE, forcedEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
PrcPriceExcelFile.TYPE_REDLINE, forcedEff, bytes, filename, operName, importPsvId);
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
@@ -500,7 +517,10 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
product.setDestRegion(countryLabel);
|
||||
product.setSourceSheet(block.sheetName);
|
||||
product.setEffectiveDate(forcedEff);
|
||||
product.setPefId(pefId);
|
||||
if (importPsvId != null)
|
||||
{
|
||||
product.setPsvId(importPsvId);
|
||||
}
|
||||
if (StringUtils.isEmpty(product.getTaxMode()))
|
||||
{
|
||||
product.setTaxMode(resolveTaxMode(product.getProductName()));
|
||||
@@ -656,6 +676,454 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> previewImportCatalog(byte[] bytes, String filename) throws Exception
|
||||
{
|
||||
Map<String, List<BmsRedlineProductCountry>> byCode = parseQuoteExcelByProduct(bytes, filename);
|
||||
try (InputStream is = new java.io.ByteArrayInputStream(bytes); Workbook workbook = WorkbookFactory.create(is))
|
||||
{
|
||||
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
|
||||
List<BmsRedlineProduct> catalogProducts = dedupeCatalogProducts(parseCatalog(catalog));
|
||||
return buildCatalogPreview(catalogProducts, byCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportAsync(MultipartFile file) throws Exception
|
||||
{
|
||||
return submitPreviewImportAsync(java.util.Collections.singletonList(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportAsync(List<MultipartFile> files) throws Exception
|
||||
{
|
||||
List<byte[]> bytesList = new ArrayList<byte[]>();
|
||||
List<String> names = new ArrayList<String>();
|
||||
readPreviewFiles(files, bytesList, names);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
importAsyncRunner.put(taskId, BmsRedlineProductImportTask.STATUS_PENDING, 0, "queued", null);
|
||||
importAsyncRunner.runPreviewAll(taskId, bytesList, names);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportCountryAsync(MultipartFile file, String countryCode) throws Exception
|
||||
{
|
||||
return submitPreviewImportCountryAsync(java.util.Collections.singletonList(file), countryCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPreviewImportCountryAsync(List<MultipartFile> files, String countryCode) throws Exception
|
||||
{
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
}
|
||||
List<byte[]> bytesList = new ArrayList<byte[]>();
|
||||
List<String> names = new ArrayList<String>();
|
||||
readPreviewFiles(files, bytesList, names);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
importAsyncRunner.put(taskId, BmsRedlineProductImportTask.STATUS_PENDING, 0, "queued", null);
|
||||
importAsyncRunner.runPreviewCountry(taskId, bytesList, names, countryCode.trim());
|
||||
return taskId;
|
||||
}
|
||||
|
||||
private void readPreviewFiles(List<MultipartFile> files, List<byte[]> bytesList, List<String> names) throws Exception
|
||||
{
|
||||
if (files == null || files.isEmpty())
|
||||
{
|
||||
throw new ServiceException("file required");
|
||||
}
|
||||
if (files.size() > com.bms.price.service.support.UploadFileBatch.MAX_FILES)
|
||||
{
|
||||
throw new ServiceException("\u5355\u6b21\u6700\u591a\u5bfc\u5165 5 \u4e2a\u6587\u4ef6");
|
||||
}
|
||||
for (MultipartFile file : files)
|
||||
{
|
||||
validateImportFile(file);
|
||||
bytesList.add(file.getBytes());
|
||||
names.add(file.getOriginalFilename());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> mergeCatalogPreview(Map<String, Object> base, Map<String, Object> incoming)
|
||||
{
|
||||
if (base == null || base.isEmpty())
|
||||
{
|
||||
return incoming;
|
||||
}
|
||||
if (incoming == null || incoming.isEmpty())
|
||||
{
|
||||
return base;
|
||||
}
|
||||
Map<String, BmsRedlineProduct> products = new LinkedHashMap<String, BmsRedlineProduct>();
|
||||
putPreviewProducts(products, castProductList(base.get("products")));
|
||||
putPreviewProducts(products, castProductList(incoming.get("products")));
|
||||
Map<String, List<BmsRedlineProductCountry>> details = new LinkedHashMap<String, List<BmsRedlineProductCountry>>();
|
||||
putPreviewDetails(details, castDetailMap(base.get("detailsByProductCode")));
|
||||
putPreviewDetails(details, castDetailMap(incoming.get("detailsByProductCode")));
|
||||
int detailCount = 0;
|
||||
for (List<BmsRedlineProductCountry> rows : details.values())
|
||||
{
|
||||
if (rows != null)
|
||||
{
|
||||
detailCount += rows.size();
|
||||
}
|
||||
}
|
||||
Map<String, Object> out = new HashMap<String, Object>();
|
||||
out.put("products", new ArrayList<BmsRedlineProduct>(products.values()));
|
||||
out.put("productCount", Integer.valueOf(products.size()));
|
||||
out.put("detailCount", Integer.valueOf(detailCount));
|
||||
out.put("detailsByProductCode", details);
|
||||
return out;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<BmsRedlineProduct> castProductList(Object value)
|
||||
{
|
||||
if (value instanceof List)
|
||||
{
|
||||
return (List<BmsRedlineProduct>) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, List<BmsRedlineProductCountry>> castDetailMap(Object value)
|
||||
{
|
||||
if (value instanceof Map)
|
||||
{
|
||||
return (Map<String, List<BmsRedlineProductCountry>>) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void putPreviewProducts(Map<String, BmsRedlineProduct> products, List<BmsRedlineProduct> list)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (BmsRedlineProduct p : list)
|
||||
{
|
||||
if (p == null || StringUtils.isEmpty(p.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
products.put(p.getProductCode().trim().toUpperCase(Locale.ROOT), p);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putPreviewDetails(Map<String, List<BmsRedlineProductCountry>> details,
|
||||
Map<String, List<BmsRedlineProductCountry>> incoming)
|
||||
{
|
||||
if (incoming == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, List<BmsRedlineProductCountry>> e : incoming.entrySet())
|
||||
{
|
||||
if (e == null || StringUtils.isEmpty(e.getKey()) || e.getValue() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
details.put(e.getKey().trim().toUpperCase(Locale.ROOT), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int applyCatalogPreview(RedlineCatalogPreviewApplyRequest request)
|
||||
{
|
||||
if (request == null || request.getPsvId() == null)
|
||||
{
|
||||
throw new ServiceException("psvId required");
|
||||
}
|
||||
Long psvId = request.getPsvId();
|
||||
catalogVersionSupport.requireEditableCatalogVersion(psvId, PriceType.REDLINE);
|
||||
Map<String, List<BmsRedlineProductCountry>> byCode = request.getDetailsByProductCode();
|
||||
if (byCode == null || byCode.isEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Map<String, BmsRedlineProduct> productMeta = new LinkedHashMap<String, BmsRedlineProduct>();
|
||||
if (request.getProducts() != null)
|
||||
{
|
||||
for (BmsRedlineProduct p : request.getProducts())
|
||||
{
|
||||
if (p == null || StringUtils.isEmpty(p.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
productMeta.put(p.getProductCode().trim().toUpperCase(Locale.ROOT), p);
|
||||
}
|
||||
}
|
||||
int detailRows = 0;
|
||||
for (Map.Entry<String, List<BmsRedlineProductCountry>> e : byCode.entrySet())
|
||||
{
|
||||
if (e == null || StringUtils.isEmpty(e.getKey()) || e.getValue() == null || e.getValue().isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String code = e.getKey().trim().toUpperCase(Locale.ROOT);
|
||||
detailRows += applyOneCatalogProduct(psvId, code, productMeta.get(code), e.getValue());
|
||||
}
|
||||
return detailRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int applyOneCatalogProduct(Long psvId, String code, BmsRedlineProduct meta, List<BmsRedlineProductCountry> details)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(code) || details == null || details.isEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
String key = code.trim().toUpperCase(Locale.ROOT);
|
||||
PrcPriceSetVersion ver = catalogVersionSupport.findVersion(psvId);
|
||||
Date eff = ver == null ? null : ver.getEffectiveTime();
|
||||
Date exp = ver == null ? null : ver.getExpireTime();
|
||||
String st = ver == null || StringUtils.isEmpty(ver.getStatus()) ? "0" : ver.getStatus();
|
||||
String destRegion = resolveDestRegion(meta == null ? null : meta.getDestRegion(), details);
|
||||
BmsRedlineProduct existing = productMapper.selectByCodeAndPsvId(key, psvId);
|
||||
if (existing == null)
|
||||
{
|
||||
BmsRedlineProduct row = new BmsRedlineProduct();
|
||||
row.setPsvId(psvId);
|
||||
row.setPriceType(PriceType.REDLINE);
|
||||
row.setProductCode(meta != null && StringUtils.isNotEmpty(meta.getProductCode())
|
||||
? meta.getProductCode().trim() : key);
|
||||
row.setProductName(meta == null ? null : meta.getProductName());
|
||||
row.setServiceLevel(meta == null ? null : meta.getServiceLevel());
|
||||
row.setCargoAttr(meta == null ? null : meta.getCargoAttr());
|
||||
row.setDestRegion(destRegion);
|
||||
row.setTaxMode(meta != null && StringUtils.isNotEmpty(meta.getTaxMode())
|
||||
? meta.getTaxMode() : "TAX_INCLUDED");
|
||||
row.setEffectiveDate(eff);
|
||||
row.setExpireDate(exp);
|
||||
row.setStatus(st);
|
||||
row.setIsDel(BmsRedlineProduct.IS_DEL_NO);
|
||||
productMapper.insertBmsRedlineProduct(row);
|
||||
existing = row;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (meta != null)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(meta.getProductName()))
|
||||
{
|
||||
existing.setProductName(meta.getProductName());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(meta.getServiceLevel()))
|
||||
{
|
||||
existing.setServiceLevel(meta.getServiceLevel());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(meta.getCargoAttr()))
|
||||
{
|
||||
existing.setCargoAttr(meta.getCargoAttr());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotEmpty(destRegion))
|
||||
{
|
||||
existing.setDestRegion(destRegion);
|
||||
}
|
||||
existing.setEffectiveDate(eff);
|
||||
existing.setExpireDate(exp);
|
||||
existing.setStatus(st);
|
||||
productMapper.updateBmsRedlineProduct(existing);
|
||||
}
|
||||
|
||||
Set<String> replaced = new HashSet<String>();
|
||||
List<BmsRedlineProductCountry> toInsert = new ArrayList<BmsRedlineProductCountry>();
|
||||
for (BmsRedlineProductCountry d : details)
|
||||
{
|
||||
if (d == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String ckey = countryIdentityKey(d);
|
||||
if (StringUtils.isNotEmpty(ckey) && !replaced.contains(ckey))
|
||||
{
|
||||
countryMapper.physicalDeleteByPspIdAndCountry(existing.getProductId(), d.getCountryCode(),
|
||||
d.getCountryName());
|
||||
replaced.add(ckey);
|
||||
}
|
||||
d.setDetailId(null);
|
||||
d.setProductId(existing.getProductId());
|
||||
d.setPsvId(psvId);
|
||||
d.setProductCode(existing.getProductCode());
|
||||
if (StringUtils.isEmpty(d.getProductName()))
|
||||
{
|
||||
d.setProductName(existing.getProductName());
|
||||
}
|
||||
d.setEffectiveDate(eff);
|
||||
d.setExpireDate(exp);
|
||||
d.setStatus(st);
|
||||
d.setIsDel(BmsRedlineProduct.IS_DEL_NO);
|
||||
toInsert.add(d);
|
||||
}
|
||||
insertRedlineCountryChunks(toInsert);
|
||||
return toInsert.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitApplyCatalogPreviewAsync(RedlineCatalogPreviewApplyRequest request)
|
||||
{
|
||||
if (request == null || request.getPsvId() == null)
|
||||
{
|
||||
throw new ServiceException("psvId required");
|
||||
}
|
||||
catalogVersionSupport.requireEditableCatalogVersion(request.getPsvId(), PriceType.REDLINE);
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
importAsyncRunner.put(taskId, BmsRedlineProductImportTask.STATUS_PENDING, 0, "queued", null);
|
||||
importAsyncRunner.runApplyPreview(taskId, request);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
private void insertRedlineCountryChunks(List<BmsRedlineProductCountry> rows)
|
||||
{
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int size = 200;
|
||||
for (int i = 0; i < rows.size(); i += size)
|
||||
{
|
||||
int end = Math.min(i + size, rows.size());
|
||||
countryMapper.insertBmsRedlineProductCountryBatch(new ArrayList<BmsRedlineProductCountry>(rows.subList(i, end)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> previewImportCountryCatalog(byte[] bytes, String filename, String countryCode)
|
||||
throws Exception
|
||||
{
|
||||
validateImportBytes(bytes, filename);
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
|
||||
}
|
||||
BmsRegion region = regionMapper.selectRegionByCode2(countryCode.trim().toUpperCase(Locale.ROOT));
|
||||
if (region == null || "2".equals(region.getDelFlag())
|
||||
|| (StringUtils.isNotEmpty(region.getStatus()) && !"0".equals(region.getStatus())))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countryNotFound", countryCode));
|
||||
}
|
||||
try (InputStream is = new java.io.ByteArrayInputStream(bytes); Workbook workbook = WorkbookFactory.create(is))
|
||||
{
|
||||
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
|
||||
if (catalog == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.countrySheetNotFound"));
|
||||
}
|
||||
List<BmsRedlineProduct> products = dedupeCatalogProducts(parseCountryCatalog(catalog));
|
||||
if (products.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.noData"));
|
||||
}
|
||||
Map<String, PriceBlock> priceBlocks = parseCountryPriceBlocks(workbook);
|
||||
Map<String, List<BmsRedlineProductCountry>> byCode = new LinkedHashMap<String, List<BmsRedlineProductCountry>>();
|
||||
for (BmsRedlineProduct product : products)
|
||||
{
|
||||
if (product == null || StringUtils.isEmpty(product.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PriceBlock block = findPriceBlock(product.getProductName(), priceBlocks);
|
||||
if (block == null || block.rows == null || block.rows.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<BmsRedlineProductCountry> countries = stampSelectedCountry(block.rows, region, null);
|
||||
for (BmsRedlineProductCountry c : countries)
|
||||
{
|
||||
if (c == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
c.setProductCode(product.getProductCode());
|
||||
c.setProductName(product.getProductName());
|
||||
}
|
||||
assertNoDuplicateCountryWeightRows(product, countries);
|
||||
byCode.put(product.getProductCode().trim().toUpperCase(Locale.ROOT), countries);
|
||||
}
|
||||
if (byCode.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noPriceRows"));
|
||||
}
|
||||
String countryLabel = StringUtils.isNotEmpty(region.getCountryNameCn())
|
||||
? region.getCountryNameCn() : region.getCountryCode2();
|
||||
if (StringUtils.isNotEmpty(countryLabel))
|
||||
{
|
||||
for (BmsRedlineProduct p : products)
|
||||
{
|
||||
if (p != null)
|
||||
{
|
||||
p.setDestRegion(countryLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buildCatalogPreview(products, byCode);
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveDestRegion(String preferred, List<BmsRedlineProductCountry> details)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(preferred))
|
||||
{
|
||||
return preferred.trim();
|
||||
}
|
||||
if (details == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (BmsRedlineProductCountry d : details)
|
||||
{
|
||||
if (d != null && StringUtils.isNotEmpty(d.getCountryName()))
|
||||
{
|
||||
return d.getCountryName().trim();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildCatalogPreview(List<BmsRedlineProduct> catalogProducts,
|
||||
Map<String, List<BmsRedlineProductCountry>> byCode)
|
||||
{
|
||||
List<BmsRedlineProduct> products = new ArrayList<BmsRedlineProduct>();
|
||||
int detailCount = 0;
|
||||
if (catalogProducts != null)
|
||||
{
|
||||
for (BmsRedlineProduct src : catalogProducts)
|
||||
{
|
||||
if (src == null || StringUtils.isEmpty(src.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String code = src.getProductCode().trim().toUpperCase(Locale.ROOT);
|
||||
List<BmsRedlineProductCountry> details = byCode == null ? null : byCode.get(code);
|
||||
if (details == null || details.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BmsRedlineProduct row = new BmsRedlineProduct();
|
||||
row.setProductCode(src.getProductCode());
|
||||
row.setProductName(src.getProductName());
|
||||
row.setServiceLevel(src.getServiceLevel());
|
||||
row.setCargoAttr(src.getCargoAttr());
|
||||
row.setDestRegion(src.getDestRegion());
|
||||
products.add(row);
|
||||
detailCount += details.size();
|
||||
}
|
||||
}
|
||||
Map<String, Object> out = new HashMap<String, Object>();
|
||||
out.put("products", products);
|
||||
out.put("productCount", Integer.valueOf(products.size()));
|
||||
out.put("detailCount", Integer.valueOf(detailCount));
|
||||
out.put("detailsByProductCode", byCode == null ? new LinkedHashMap<String, List<BmsRedlineProductCountry>>() : byCode);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @return int[]{added, skipped, countryRows} */
|
||||
private int[] importOneProduct(Workbook workbook, BmsRedlineProduct product, String operName,
|
||||
@@ -682,22 +1150,28 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
}
|
||||
assertNoDuplicateCountryWeightRows(product, countries);
|
||||
// UI-selected effective date wins over Excel; previous open product/country rows expire at this date
|
||||
Date newEff = requireImportEffectiveDate(forcedEffectiveDate != null ? forcedEffectiveDate
|
||||
: product.getEffectiveDate());
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
Date newEff = forcedEffectiveDate == null && product.getEffectiveDate() == null
|
||||
? null
|
||||
: requireImportEffectiveDate(forcedEffectiveDate != null ? forcedEffectiveDate
|
||||
: product.getEffectiveDate());
|
||||
if (newEff != null)
|
||||
{
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
}
|
||||
BmsRedlineProduct existing = productMapper.selectBmsRedlineProductByCodeAndDate(
|
||||
product.getProductCode(), newEff);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
product.setProductId(existing.getProductId());
|
||||
patchProductPefId(product, operName);
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 0, 1, countryRows };
|
||||
}
|
||||
|
||||
applyProductStatus(product);
|
||||
product.setPriceType(PriceType.REDLINE);
|
||||
product.setPsvId(catalogVersionSupport.requirePsvId(PriceType.REDLINE, product.getPsvId(), newEff));
|
||||
try
|
||||
{
|
||||
productMapper.insertBmsRedlineProduct(product);
|
||||
@@ -710,26 +1184,22 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
productMapper.refreshStatusByDates();
|
||||
if (newEff != null)
|
||||
{
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
if (product.getPsvId() != null)
|
||||
{
|
||||
catalogVersionSupport.activateVersion(product.getPsvId(), newEff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
productMapper.refreshStatusByDates();
|
||||
}
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 1, 0, countryRows };
|
||||
}
|
||||
|
||||
private void patchProductPefId(BmsRedlineProduct product, String operName)
|
||||
{
|
||||
if (product == null || product.getProductId() == null || product.getPefId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BmsRedlineProduct pefPatch = new BmsRedlineProduct();
|
||||
pefPatch.setProductId(product.getProductId());
|
||||
pefPatch.setPefId(product.getPefId());
|
||||
pefPatch.setUpdateBy(operName);
|
||||
productMapper.updateBmsRedlineProduct(pefPatch);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save country rows by effective_date.
|
||||
* For each product_code + country in the Excel:
|
||||
@@ -771,6 +1241,8 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
{
|
||||
countryMapper.expireOpenByProductCodeAndCountry(
|
||||
productCode, incoming.getCountryCode(), incoming.getCountryName(), newEff);
|
||||
countryMapper.deleteSurchargeRelByProductCodeAndCountryOnDate(
|
||||
productCode, incoming.getCountryCode(), incoming.getCountryName(), newEff);
|
||||
countryMapper.deleteOpenByProductCodeAndCountryOnDate(
|
||||
productCode, incoming.getCountryCode(), incoming.getCountryName(), newEff);
|
||||
replacedCountries.add(countryKey);
|
||||
@@ -2035,6 +2507,10 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
/** Batch pre-check: fail whole import before any DB write. */
|
||||
private void assertImportProductsEffectiveNotBeforePrev(List<BmsRedlineProduct> products, Date forcedEff)
|
||||
{
|
||||
if (forcedEff == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Date newEff = requireImportEffectiveDate(forcedEff);
|
||||
if (products == null || products.isEmpty())
|
||||
{
|
||||
@@ -2209,18 +2685,22 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
{
|
||||
product.setSourceSheet(product.getProductName());
|
||||
}
|
||||
Date newEff = requireImportEffectiveDate(product.getEffectiveDate());
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
Date newEff = product.getEffectiveDate() == null ? null : requireImportEffectiveDate(product.getEffectiveDate());
|
||||
if (newEff != null)
|
||||
{
|
||||
applyForcedEffectiveDate(product, countries, newEff);
|
||||
assertImportEffectiveNotBeforePrev(product, newEff);
|
||||
}
|
||||
BmsRedlineProduct existing = productMapper.selectBmsRedlineProductByCodeAndDate(product.getProductCode(), newEff);
|
||||
if (existing != null)
|
||||
{
|
||||
product.setProductId(existing.getProductId());
|
||||
patchProductPefId(product, operName);
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 0, 1, countryRows };
|
||||
}
|
||||
applyProductStatus(product);
|
||||
product.setPriceType(PriceType.REDLINE);
|
||||
product.setPsvId(catalogVersionSupport.requirePsvId(PriceType.REDLINE, product.getPsvId(), newEff));
|
||||
try
|
||||
{
|
||||
productMapper.insertBmsRedlineProduct(product);
|
||||
@@ -2233,8 +2713,18 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
productMapper.refreshStatusByDates();
|
||||
if (newEff != null)
|
||||
{
|
||||
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
|
||||
if (product.getPsvId() != null)
|
||||
{
|
||||
catalogVersionSupport.activateVersion(product.getPsvId(), newEff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
productMapper.refreshStatusByDates();
|
||||
}
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 1, 0, countryRows };
|
||||
}
|
||||
|
||||
+15
-23
@@ -37,28 +37,6 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
return excelFileMapper.selectPrcPriceExcelFileById(pefId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceExcelFile> selectByProductCode(String productCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<PrcPriceExcelFile> list = excelFileMapper.selectByProductCode(productCode.trim());
|
||||
return list == null ? Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceExcelFile> selectByRedlineProductCode(String productCode)
|
||||
{
|
||||
if (productCode == null || productCode.trim().isEmpty())
|
||||
{
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
List<PrcPriceExcelFile> list = excelFileMapper.selectByRedlineProductCode(productCode.trim());
|
||||
return list == null ? java.util.Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceExcelFile> selectByPsvId(Long psvId)
|
||||
{
|
||||
@@ -72,7 +50,7 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
|
||||
@Override
|
||||
public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes,
|
||||
String filename, String operName) throws Exception
|
||||
String filename, String operName, Long psvId) throws Exception
|
||||
{
|
||||
if (StringUtils.isEmpty(excelType))
|
||||
{
|
||||
@@ -111,6 +89,7 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
|
||||
PrcPriceExcelFile row = new PrcPriceExcelFile();
|
||||
row.setExcelType(excelType.trim().toUpperCase(Locale.ROOT));
|
||||
row.setPsvId(psvId);
|
||||
row.setEffectiveTime(effectiveTime);
|
||||
row.setFileName(originalName);
|
||||
row.setFilePath(storedPath);
|
||||
@@ -122,6 +101,19 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bindPsvId(Long pefId, Long psvId)
|
||||
{
|
||||
if (pefId == null || psvId == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
PrcPriceExcelFile row = new PrcPriceExcelFile();
|
||||
row.setPefId(pefId);
|
||||
row.setPsvId(psvId);
|
||||
return excelFileMapper.updatePrcPriceExcelFile(row);
|
||||
}
|
||||
|
||||
private static String resolveContentType(String ext)
|
||||
{
|
||||
if ("xls".equals(ext))
|
||||
|
||||
+156
-71
@@ -399,6 +399,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
int curSeq = version.getVersionSeq() == null ? 0 : version.getVersionSeq().intValue();
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(version.getPriceSetId());
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (list == null || list.isEmpty())
|
||||
{
|
||||
@@ -455,6 +456,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
int seq = maxSeq == null ? 1 : maxSeq.intValue() + 1;
|
||||
PrcPriceSetVersion nv = new PrcPriceSetVersion();
|
||||
nv.setPriceSetId(set.getPriceSetId());
|
||||
nv.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
nv.setVersionSeq(Integer.valueOf(seq));
|
||||
nv.setVersionNo("V" + seq);
|
||||
nv.setStatus(VER_DRAFT);
|
||||
@@ -492,6 +494,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
int seq = maxSeq == null ? 1 : maxSeq.intValue() + 1;
|
||||
PrcPriceSetVersion nv = new PrcPriceSetVersion();
|
||||
nv.setPriceSetId(set.getPriceSetId());
|
||||
nv.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
nv.setVersionSeq(Integer.valueOf(seq));
|
||||
nv.setVersionNo("V" + seq);
|
||||
nv.setStatus(VER_DRAFT);
|
||||
@@ -908,10 +911,13 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
@Override
|
||||
public int insertDetail(PrcPriceSetDetail detail)
|
||||
{
|
||||
PrcPriceSet db = requireSet(detail.getPriceSetId());
|
||||
Long psvId = detail.getPsvId();
|
||||
Long priceSetId = resolvePriceSetIdByPsvId(psvId);
|
||||
PrcPriceSet db = requireSet(priceSetId);
|
||||
assertChildEditable(db);
|
||||
detail.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
detail.setPsvId(resolvePsvId(priceSetId, psvId));
|
||||
validateDetail(db, detail);
|
||||
detail.setPsvId(resolvePsvId(detail.getPriceSetId(), detail.getPsvId()));
|
||||
detail.setPspId(lookupSetProductId(detail.getPsvId(), detail.getProductCode()));
|
||||
int rows = detailMapper.insertPrcPriceSetDetail(detail);
|
||||
if (detail.getSurcharges() != null)
|
||||
@@ -955,14 +961,52 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.fileType"));
|
||||
}
|
||||
byte[] bytes = file.getBytes();
|
||||
return submitImportDetailPricesAsync(priceSetId, psvId, java.util.Collections.singletonList(file), operName,
|
||||
updateOnly, replaceByProductCountry, countryCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitImportDetailPricesAsync(Long priceSetId, Long psvId, List<MultipartFile> files, String operName,
|
||||
boolean updateOnly, boolean replaceByProductCountry, String countryCode) throws Exception
|
||||
{
|
||||
PrcPriceSet db = requireSet(priceSetId);
|
||||
assertChildEditable(db);
|
||||
if (!TYPE_FIXED.equals(db.getSetType()))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.fixedOnly"));
|
||||
}
|
||||
if (files == null || files.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.fileEmpty"));
|
||||
}
|
||||
if (files.size() > com.bms.price.service.support.UploadFileBatch.MAX_FILES)
|
||||
{
|
||||
throw new ServiceException("\u5355\u6b21\u6700\u591a\u5bfc\u5165 5 \u4e2a\u6587\u4ef6");
|
||||
}
|
||||
List<byte[]> bytesList = new ArrayList<byte[]>();
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (MultipartFile file : files)
|
||||
{
|
||||
if (file == null || file.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.fileEmpty"));
|
||||
}
|
||||
String filename = file.getOriginalFilename();
|
||||
if (filename == null || !(filename.toLowerCase(Locale.ROOT).endsWith(".xlsx")
|
||||
|| filename.toLowerCase(Locale.ROOT).endsWith(".xls")))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.fileType"));
|
||||
}
|
||||
bytesList.add(file.getBytes());
|
||||
names.add(filename);
|
||||
}
|
||||
if (psvId == null)
|
||||
{
|
||||
psvId = resolveCurrentPsvId(db);
|
||||
}
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
saveAsyncRunner.put(taskId, PrcPriceSetSaveTask.STATUS_PENDING, 0, "queued", priceSetId, null);
|
||||
saveAsyncRunner.runImportPrices(taskId, priceSetId, psvId, bytes, filename, operName, updateOnly,
|
||||
saveAsyncRunner.runImportPricesFiles(taskId, priceSetId, psvId, bytesList, names, operName, updateOnly,
|
||||
replaceByProductCountry, countryCode);
|
||||
return taskId;
|
||||
}
|
||||
@@ -1093,7 +1137,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
operName = null;
|
||||
}
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_SALES, new Date(), bytes, filename, operName);
|
||||
PrcPriceExcelFile.TYPE_SALES, new Date(), bytes, filename, operName, null);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
|
||||
Map<String, PrcPriceSetProduct> productMap = new LinkedHashMap<String, PrcPriceSetProduct>();
|
||||
@@ -1210,22 +1254,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noDetailWritten"));
|
||||
}
|
||||
|
||||
if (pefId != null)
|
||||
{
|
||||
for (String code : byProduct.keySet())
|
||||
{
|
||||
if (StringUtils.isEmpty(code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSetProduct sp = productMap.get(code.trim().toUpperCase(Locale.ROOT));
|
||||
if (sp != null)
|
||||
{
|
||||
sp.setPefId(pefId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (replaceScope)
|
||||
{
|
||||
for (PrcPriceSetDetail d : details)
|
||||
@@ -1253,6 +1281,46 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> previewImportDetailPricesMany(List<byte[]> bytesList, List<String> names,
|
||||
boolean updateOnly, List<PrcPriceSetDetail> existingDetails, boolean replaceByProductCountry, String countryCode)
|
||||
throws Exception
|
||||
{
|
||||
if (bytesList == null || bytesList.isEmpty())
|
||||
{
|
||||
throw new ServiceException("file required");
|
||||
}
|
||||
if (bytesList.size() > com.bms.price.service.support.UploadFileBatch.MAX_FILES)
|
||||
{
|
||||
throw new ServiceException("\u5355\u6b21\u6700\u591a\u5bfc\u5165 5 \u4e2a\u6587\u4ef6");
|
||||
}
|
||||
List<PrcPriceSetDetail> existing = existingDetails;
|
||||
Map<String, Object> last = null;
|
||||
List<Long> pefIds = new ArrayList<Long>();
|
||||
for (int i = 0; i < bytesList.size(); i++)
|
||||
{
|
||||
String filename = names != null && i < names.size() ? names.get(i) : null;
|
||||
last = previewImportDetailPrices(bytesList.get(i), filename, updateOnly, existing, replaceByProductCountry,
|
||||
countryCode);
|
||||
Object details = last == null ? null : last.get("details");
|
||||
if (details instanceof List)
|
||||
{
|
||||
existing = (List<PrcPriceSetDetail>) details;
|
||||
}
|
||||
Object pefId = last == null ? null : last.get("pefId");
|
||||
if (pefId instanceof Number)
|
||||
{
|
||||
pefIds.add(Long.valueOf(((Number) pefId).longValue()));
|
||||
}
|
||||
}
|
||||
if (last != null)
|
||||
{
|
||||
last.put("pefIds", pefIds);
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
private Map<String, List<BmsProductCountry>> parseSetImportExcel(byte[] bytes, String filename, String countryCode)
|
||||
throws Exception
|
||||
{
|
||||
@@ -1385,7 +1453,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
|
||||
PrcPriceSetDetail q = new PrcPriceSetDetail();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
q.setPsvId(psvId);
|
||||
List<PrcPriceSetDetail> details = detailMapper.selectPrcPriceSetDetailList(q);
|
||||
if (details == null)
|
||||
@@ -1424,9 +1492,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
PrcPriceSetVersion verForFile = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
Date excelEff = verForFile != null && verForFile.getEffectiveTime() != null
|
||||
? verForFile.getEffectiveTime() : new Date();
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_SALES, excelEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_SALES, excelEff, bytes, filename, operName, psvId);
|
||||
|
||||
int updated = 0;
|
||||
int inserted = 0;
|
||||
@@ -1461,7 +1528,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
Long pspId = null;
|
||||
if (!updateOnly)
|
||||
{
|
||||
pspId = ensureSetProduct(priceSetId, psvId, productCode, productName, pefId);
|
||||
pspId = ensureSetProduct(priceSetId, psvId, productCode, productName);
|
||||
}
|
||||
for (BmsProductCountry row : e.getValue())
|
||||
{
|
||||
@@ -1525,11 +1592,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
setProductMapper.bindDetailPspByPsvId(psvId);
|
||||
}
|
||||
if (pefId != null && (inserted > 0 || updated > 0))
|
||||
{
|
||||
List<String> codes = new ArrayList<String>(byProduct.keySet());
|
||||
setProductMapper.updatePefIdByPsvId(psvId, pefId, codes);
|
||||
}
|
||||
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
||||
if (inserted == 0 && updated == 0)
|
||||
{
|
||||
@@ -1576,7 +1638,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
String productName, BmsProductCountry row, BigDecimal freight, BigDecimal registration)
|
||||
{
|
||||
PrcPriceSetDetail d = new PrcPriceSetDetail();
|
||||
d.setPriceSetId(priceSetId);
|
||||
d.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
d.setPsvId(psvId);
|
||||
d.setProductCode(trimToLen(productCode, 20));
|
||||
d.setProductName(trimToLen(productName, 50));
|
||||
@@ -1636,6 +1698,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(set.getPriceSetId());
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
versions = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
}
|
||||
if (versions == null || versions.isEmpty())
|
||||
@@ -2011,13 +2074,14 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.detail.notExists"));
|
||||
}
|
||||
PrcPriceSet db = requireSet(old.getPriceSetId());
|
||||
Long priceSetId = resolvePriceSetIdByPsvId(old.getPsvId());
|
||||
PrcPriceSet db = requireSet(priceSetId);
|
||||
assertChildEditable(db);
|
||||
detail.setPriceSetId(old.getPriceSetId());
|
||||
if (detail.getPsvId() == null)
|
||||
{
|
||||
detail.setPsvId(old.getPsvId());
|
||||
}
|
||||
detail.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
validateDetail(db, detail);
|
||||
Long pspId = lookupSetProductId(detail.getPsvId(), detail.getProductCode());
|
||||
if (pspId == null)
|
||||
@@ -2045,7 +2109,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSet db = requireSet(old.getPriceSetId());
|
||||
PrcPriceSet db = requireSet(resolvePriceSetIdByPsvId(old.getPsvId()));
|
||||
assertChildEditable(db);
|
||||
if (old.getPsvId() != null && StringUtils.isNotEmpty(old.getProductCode()))
|
||||
{
|
||||
@@ -2817,6 +2881,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
}
|
||||
|
||||
private Long resolvePriceSetIdByPsvId(Long psvId)
|
||||
{
|
||||
if (psvId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
PrcPriceSetVersion v = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
return v == null ? null : v.getPriceSetId();
|
||||
}
|
||||
|
||||
private PrcPriceSet requireSet(Long priceSetId)
|
||||
{
|
||||
// Header only: avoid count(detail) waiting on long pull/import locks.
|
||||
@@ -2850,6 +2924,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> versions = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
PrcPriceSetVersion current = pickCurrentVersion(versions);
|
||||
if (current == null)
|
||||
@@ -2896,6 +2971,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
Long priceSetId = set.getPriceSetId();
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> versions = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (versions == null)
|
||||
{
|
||||
@@ -2960,6 +3036,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
// Draft versions may omit effective_time; set on activate
|
||||
version.setPriceSetId(set.getPriceSetId());
|
||||
version.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
version.setCreateBy(set.getCreateBy());
|
||||
version.setUpdateBy(set.getUpdateBy());
|
||||
// Reuse existing version when client omitted psvId (avoids V2/V3 + leftover details)
|
||||
@@ -3002,6 +3079,28 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
versionMapper.updatePrcPriceSetVersion(version);
|
||||
}
|
||||
set.setVersion(version);
|
||||
if (version.getPsvId() != null)
|
||||
{
|
||||
java.util.LinkedHashSet<Long> excelIds = new java.util.LinkedHashSet<Long>();
|
||||
if (set.getPefIds() != null)
|
||||
{
|
||||
for (Long excelId : set.getPefIds())
|
||||
{
|
||||
if (excelId != null)
|
||||
{
|
||||
excelIds.add(excelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (set.getPefId() != null)
|
||||
{
|
||||
excelIds.add(set.getPefId());
|
||||
}
|
||||
for (Long excelId : excelIds)
|
||||
{
|
||||
priceExcelFileService.bindPsvId(excelId, version.getPsvId());
|
||||
}
|
||||
}
|
||||
if (detailsProvided)
|
||||
{
|
||||
upsertVersionDetails(set, version, details);
|
||||
@@ -3077,6 +3176,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (list == null || list.isEmpty())
|
||||
{
|
||||
@@ -3122,6 +3222,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (list == null || list.isEmpty())
|
||||
{
|
||||
@@ -3169,7 +3270,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
setProductMapper.deleteByPsvId(version.getPsvId());
|
||||
// clean legacy rows without psv_id under this set (would look like duplicates in UI)
|
||||
detailMapper.deleteOrphanDetailsByPriceSetId(set.getPriceSetId());
|
||||
int productRows = setProductMapper.insertFromLibrary(set.getPriceSetId(), version.getPsvId());
|
||||
int productRows = setProductMapper.insertFromLibrary(version.getPsvId());
|
||||
if (productRows <= 0)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.library.empty"));
|
||||
@@ -3178,18 +3279,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
int inserted;
|
||||
if (TYPE_DISCOUNT.equals(set.getSetType()))
|
||||
{
|
||||
inserted = detailMapper.insertDiscountDetailsFromLibrary(
|
||||
set.getPriceSetId(), version.getPsvId());
|
||||
inserted = detailMapper.insertDiscountDetailsFromLibrary(version.getPsvId());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prefer currently effective open country rows; fallback asOf window if needed
|
||||
inserted = detailMapper.insertFixedDetailsFromLibrary(
|
||||
set.getPriceSetId(), version.getPsvId(), null, true);
|
||||
inserted = detailMapper.insertFixedDetailsFromLibrary(version.getPsvId(), null, true);
|
||||
if (inserted <= 0)
|
||||
{
|
||||
inserted = detailMapper.insertFixedDetailsFromLibrary(
|
||||
set.getPriceSetId(), version.getPsvId(), new Date(), false);
|
||||
version.getPsvId(), new Date(), false);
|
||||
}
|
||||
}
|
||||
if (inserted <= 0)
|
||||
@@ -3267,10 +3366,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
for (PrcPriceSetDetail d : details)
|
||||
{
|
||||
validateDetail(set, d);
|
||||
d.setPriceSetId(set.getPriceSetId());
|
||||
d.setPsvId(version.getPsvId());
|
||||
d.setPsvId(version.getPsvId());
|
||||
d.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
Long pspId = ensureSetProduct(set.getPriceSetId(), version.getPsvId(),
|
||||
d.getProductCode(), d.getProductName(), set.getPefId());
|
||||
d.getProductCode(), d.getProductName());
|
||||
if (pspId != null)
|
||||
{
|
||||
d.setPspId(pspId);
|
||||
@@ -3391,12 +3490,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
* Uses request-scoped cache during bulk upsert to avoid N selects on same code.
|
||||
*/
|
||||
private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint)
|
||||
{
|
||||
return ensureSetProduct(priceSetId, psvId, productCode, productNameHint, null);
|
||||
}
|
||||
|
||||
private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint,
|
||||
Long pefId)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(productCode))
|
||||
{
|
||||
@@ -3407,13 +3500,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
Map<String, Long> cache = setProductCacheTl.get();
|
||||
if (cache != null && cache.containsKey(key))
|
||||
{
|
||||
if (pefId != null)
|
||||
{
|
||||
PrcPriceSetProduct pefPatch = new PrcPriceSetProduct();
|
||||
pefPatch.setPspId(cache.get(key));
|
||||
pefPatch.setPefId(pefId);
|
||||
setProductMapper.updatePrcPriceSetProduct(pefPatch);
|
||||
}
|
||||
return cache.get(key);
|
||||
}
|
||||
PrcPriceSetProduct existing = setProductMapper.selectByPsvIdAndCode(psvId, code);
|
||||
@@ -3426,11 +3512,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
existing.setProductName(productNameHint);
|
||||
needUpdate = true;
|
||||
}
|
||||
if (pefId != null && (existing.getPefId() == null || !pefId.equals(existing.getPefId())))
|
||||
{
|
||||
existing.setPefId(pefId);
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate)
|
||||
{
|
||||
setProductMapper.updatePrcPriceSetProduct(existing);
|
||||
@@ -3442,10 +3523,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return existing.getPspId();
|
||||
}
|
||||
PrcPriceSetProduct row = new PrcPriceSetProduct();
|
||||
row.setPriceSetId(priceSetId);
|
||||
row.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
row.setPsvId(psvId);
|
||||
row.setProductCode(code);
|
||||
row.setPefId(pefId);
|
||||
BmsProduct lib = null;
|
||||
try
|
||||
{
|
||||
@@ -3792,6 +3872,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (list == null)
|
||||
{
|
||||
@@ -3815,6 +3896,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
q.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||
List<PrcPriceSetVersion> versions = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (versions == null || versions.isEmpty())
|
||||
{
|
||||
@@ -3914,15 +3996,18 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
public int refreshAllVersionStatus()
|
||||
{
|
||||
List<Long> ids = versionMapper.selectPriceSetIds();
|
||||
if (ids == null || ids.isEmpty())
|
||||
int n = 0;
|
||||
if (ids != null)
|
||||
{
|
||||
return 0;
|
||||
for (Long id : ids)
|
||||
{
|
||||
refreshVersionStatus(id);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
for (Long id : ids)
|
||||
{
|
||||
refreshVersionStatus(id);
|
||||
}
|
||||
return ids.size();
|
||||
n += versionMapper.refreshCatalogStatusByDates(com.bms.price.domain.PriceType.PRODUCT);
|
||||
n += versionMapper.refreshCatalogStatusByDates(com.bms.price.domain.PriceType.REDLINE);
|
||||
return n;
|
||||
}
|
||||
|
||||
private static boolean sameTime(Date a, Date b)
|
||||
|
||||
+2
-2
@@ -923,7 +923,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
}
|
||||
|
||||
PrcPriceSetDetail detail = detailMapper.selectFixedMatch(
|
||||
set.getPriceSetId(), psvId, productCode,
|
||||
psvId, productCode,
|
||||
countryCode, countryName, zoneCode, weight);
|
||||
if (detail == null)
|
||||
{
|
||||
@@ -954,7 +954,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
PrcPriceSetDetail detail = null;
|
||||
if (detailCount > 0)
|
||||
{
|
||||
detail = detailMapper.selectDiscountMatch(set.getPriceSetId(), psvId, productCode);
|
||||
detail = detailMapper.selectDiscountMatch(psvId, productCode);
|
||||
if (detail == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
package com.bms.price.service.support;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.price.domain.PriceType;
|
||||
import com.bms.price.domain.PrcPriceSetVersion;
|
||||
import com.bms.price.mapper.PrcPriceExcelFileMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetProductMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetDetailMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetVersionMapper;
|
||||
import com.bms.price.mapper.PrcSurchargeRelMapper;
|
||||
|
||||
/**
|
||||
* Resolve / create global PRODUCT or REDLINE versions (price_set_id null).
|
||||
*/
|
||||
@Component
|
||||
public class CatalogVersionSupport
|
||||
{
|
||||
public static final String STATUS_DRAFT = "0";
|
||||
public static final String STATUS_EFFECT = "1";
|
||||
public static final String STATUS_EXPIRED = "2";
|
||||
public static final String STATUS_PENDING = "3";
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetVersionMapper versionMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetProductMapper productMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetDetailMapper detailMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcSurchargeRelMapper surchargeRelMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceExcelFileMapper excelFileMapper;
|
||||
|
||||
public void assertCatalogType(String priceType)
|
||||
{
|
||||
if (!PriceType.isCatalog(priceType))
|
||||
{
|
||||
throw new ServiceException("priceType must be PRODUCT or REDLINE");
|
||||
}
|
||||
}
|
||||
|
||||
/** Latest version for list: prefer EFFECT, else max version_seq. */
|
||||
public PrcPriceSetVersion resolveLatestForList(String priceType)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(priceType);
|
||||
q.setStatus(STATUS_EFFECT);
|
||||
List<PrcPriceSetVersion> effect = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (effect != null && !effect.isEmpty())
|
||||
{
|
||||
return effect.get(0);
|
||||
}
|
||||
q.setStatus(null);
|
||||
List<PrcPriceSetVersion> all = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
return (all == null || all.isEmpty()) ? null : all.get(0);
|
||||
}
|
||||
|
||||
public PrcPriceSetVersion selectActiveAt(String priceType, Date asOf)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
return versionMapper.selectActiveByTypeAt(priceType, asOf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a draft version for import/manual edit at effectiveTime.
|
||||
* If an open draft exists, reuse it; else create next seq and optionally copy from latest.
|
||||
*/
|
||||
public PrcPriceSetVersion ensureDraftVersion(String priceType, Date effectiveTime, boolean copyFromLatest)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(priceType);
|
||||
q.setStatus(STATUS_DRAFT);
|
||||
List<PrcPriceSetVersion> drafts = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (drafts != null && !drafts.isEmpty())
|
||||
{
|
||||
PrcPriceSetVersion draft = drafts.get(0);
|
||||
if (effectiveTime != null)
|
||||
{
|
||||
draft.setEffectiveTime(effectiveTime);
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
}
|
||||
return draft;
|
||||
}
|
||||
|
||||
Integer max = versionMapper.selectMaxVersionSeqByType(priceType, null);
|
||||
int next = max == null ? 1 : max + 1;
|
||||
PrcPriceSetVersion nv = new PrcPriceSetVersion();
|
||||
nv.setPriceSetId(null);
|
||||
nv.setPriceType(priceType);
|
||||
nv.setVersionSeq(next);
|
||||
nv.setVersionNo("V" + next);
|
||||
nv.setEffectiveTime(effectiveTime);
|
||||
nv.setStatus(STATUS_DRAFT);
|
||||
nv.setPriceBuildMode("IMPORT");
|
||||
nv.setCreateBy("system");
|
||||
versionMapper.insertPrcPriceSetVersion(nv);
|
||||
|
||||
if (copyFromLatest)
|
||||
{
|
||||
PrcPriceSetVersion src = resolveLatestForList(priceType);
|
||||
if (src != null && src.getPsvId() != null && !src.getPsvId().equals(nv.getPsvId()))
|
||||
{
|
||||
productMapper.copyFromVersion(src.getPsvId(), nv.getPsvId());
|
||||
detailMapper.copyDetailsFromVersion(src.getPsvId(), nv.getPsvId());
|
||||
productMapper.bindDetailPspByPsvId(nv.getPsvId());
|
||||
// stamp price_type on copied rows
|
||||
productMapper.updatePriceTypeByPsvId(nv.getPsvId(), priceType);
|
||||
detailMapper.updatePriceTypeByPsvId(nv.getPsvId(), priceType);
|
||||
}
|
||||
}
|
||||
return nv;
|
||||
}
|
||||
|
||||
/** Activate draft: set EFFECT, chain expire on previous EFFECT versions of same type. */
|
||||
public void activateVersion(Long psvId, Date effectiveTime)
|
||||
{
|
||||
PrcPriceSetVersion ver = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
if (ver == null)
|
||||
{
|
||||
throw new ServiceException("版本不存在");
|
||||
}
|
||||
assertCatalogType(ver.getPriceType());
|
||||
if (!STATUS_DRAFT.equals(ver.getStatus()) && !STATUS_PENDING.equals(ver.getStatus()))
|
||||
{
|
||||
throw new ServiceException("仅草稿或待生效版本可以启动");
|
||||
}
|
||||
Date eff = effectiveTime != null ? effectiveTime : ver.getEffectiveTime();
|
||||
if (eff == null)
|
||||
{
|
||||
throw new ServiceException("请选择生效时间");
|
||||
}
|
||||
Date prevEff = findPrevEffectiveTime(ver);
|
||||
if (prevEff != null && eff.before(prevEff))
|
||||
{
|
||||
throw new ServiceException("生效时间不能早于上一版本");
|
||||
}
|
||||
|
||||
boolean future = eff.after(new Date());
|
||||
if (!future)
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(ver.getPriceType());
|
||||
q.setStatus(STATUS_EFFECT);
|
||||
List<PrcPriceSetVersion> prevList = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (prevList != null)
|
||||
{
|
||||
for (PrcPriceSetVersion prev : prevList)
|
||||
{
|
||||
if (prev.getPsvId().equals(psvId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
prev.setExpireTime(eff);
|
||||
prev.setStatus(STATUS_EXPIRED);
|
||||
versionMapper.updatePrcPriceSetVersion(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ver.setEffectiveTime(eff);
|
||||
ver.setExpireTime(null);
|
||||
ver.setStatus(future ? STATUS_PENDING : STATUS_EFFECT);
|
||||
versionMapper.updatePrcPriceSetVersion(ver);
|
||||
refreshCatalogStatuses(ver.getPriceType());
|
||||
}
|
||||
|
||||
public List<PrcPriceSetVersion> listVersions(String priceType, String status)
|
||||
{
|
||||
return listVersions(priceType, status, null);
|
||||
}
|
||||
|
||||
public List<PrcPriceSetVersion> listVersions(String priceType, String status, String statusGroup)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
refreshCatalogStatuses(priceType);
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(priceType);
|
||||
q.setStatus(status);
|
||||
q.setStatusGroup(statusGroup);
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectCatalogVersionList(q);
|
||||
return list == null ? java.util.Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard-delete draft/pending catalog version with products, details,
|
||||
* surcharge links and excel attachments.
|
||||
*/
|
||||
public int deleteDraftVersion(Long psvId)
|
||||
{
|
||||
PrcPriceSetVersion ver = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
if (ver == null)
|
||||
{
|
||||
throw new ServiceException("\u7248\u672c\u4e0d\u5b58\u5728");
|
||||
}
|
||||
assertCatalogType(ver.getPriceType());
|
||||
if (!STATUS_DRAFT.equals(ver.getStatus()) && !STATUS_PENDING.equals(ver.getStatus()))
|
||||
{
|
||||
throw new ServiceException("\u4ec5\u8349\u7a3f\u6216\u5f85\u751f\u6548\u7248\u672c\u53ef\u4ee5\u5220\u9664");
|
||||
}
|
||||
surchargeRelMapper.physicalDeleteByPsvId(psvId);
|
||||
detailMapper.physicalDeleteByPsvId(psvId);
|
||||
productMapper.physicalDeleteByPsvId(psvId);
|
||||
excelFileMapper.physicalDeleteByPsvId(psvId);
|
||||
return versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
|
||||
}
|
||||
|
||||
/** Current draft version (status=0), or null. */
|
||||
public PrcPriceSetVersion currentDraft(String priceType)
|
||||
{
|
||||
List<PrcPriceSetVersion> drafts = listVersions(priceType, STATUS_DRAFT);
|
||||
return drafts.isEmpty() ? null : drafts.get(0);
|
||||
}
|
||||
|
||||
/** Draft/pending catalog version that can accept preview apply / edit. */
|
||||
public PrcPriceSetVersion requireEditableCatalogVersion(Long psvId, String priceType)
|
||||
{
|
||||
PrcPriceSetVersion ver = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
if (ver == null)
|
||||
{
|
||||
throw new ServiceException("\u7248\u672c\u4e0d\u5b58\u5728");
|
||||
}
|
||||
assertCatalogType(ver.getPriceType());
|
||||
if (StringUtils.isNotEmpty(priceType) && !priceType.equals(ver.getPriceType()))
|
||||
{
|
||||
throw new ServiceException("\u7248\u672c\u7c7b\u578b\u4e0d\u5339\u914d");
|
||||
}
|
||||
if (!STATUS_DRAFT.equals(ver.getStatus()) && !STATUS_PENDING.equals(ver.getStatus()))
|
||||
{
|
||||
throw new ServiceException("\u4ec5\u8349\u7a3f\u6216\u5f85\u751f\u6548\u7248\u672c\u53ef\u4ee5\u7f16\u8f91");
|
||||
}
|
||||
return ver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare draft for catalog price create page.
|
||||
* copyFromLatest=true: copy from current EFFECT version when draft is empty.
|
||||
* copyFromLatest=false: empty draft (Excel import / rebuild).
|
||||
*/
|
||||
public PrcPriceSetVersion prepareDraftVersion(String priceType, boolean copyFromLatest)
|
||||
{
|
||||
assertCatalogType(priceType);
|
||||
List<PrcPriceSetVersion> drafts = listVersions(priceType, STATUS_DRAFT);
|
||||
PrcPriceSetVersion draft;
|
||||
if (drafts.isEmpty())
|
||||
{
|
||||
if (copyFromLatest)
|
||||
{
|
||||
PrcPriceSetVersion src = findEffectVersion(priceType);
|
||||
if (src == null)
|
||||
{
|
||||
throw new ServiceException("\u5f53\u524d\u6ca1\u6709\u5df2\u751f\u6548\u7248\u672c\u53ef\u4ee5\u6cbf\u7528");
|
||||
}
|
||||
}
|
||||
draft = ensureDraftVersion(priceType, null, copyFromLatest);
|
||||
draft.setPriceBuildMode(copyFromLatest ? "COPY" : "IMPORT");
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
return reloadCatalogVersion(draft.getPsvId(), priceType);
|
||||
}
|
||||
|
||||
draft = drafts.get(0);
|
||||
int count = draft.getProductCount() == null ? 0 : draft.getProductCount().intValue();
|
||||
if (count == 0 && copyFromLatest)
|
||||
{
|
||||
PrcPriceSetVersion src = findEffectVersion(priceType);
|
||||
if (src == null)
|
||||
{
|
||||
throw new ServiceException("\u5f53\u524d\u6ca1\u6709\u5df2\u751f\u6548\u7248\u672c\u53ef\u4ee5\u6cbf\u7528");
|
||||
}
|
||||
if (!src.getPsvId().equals(draft.getPsvId()))
|
||||
{
|
||||
productMapper.copyFromVersion(src.getPsvId(), draft.getPsvId());
|
||||
detailMapper.copyDetailsFromVersion(src.getPsvId(), draft.getPsvId());
|
||||
productMapper.bindDetailPspByPsvId(draft.getPsvId());
|
||||
productMapper.updatePriceTypeByPsvId(draft.getPsvId(), priceType);
|
||||
detailMapper.updatePriceTypeByPsvId(draft.getPsvId(), priceType);
|
||||
}
|
||||
draft.setPriceBuildMode("COPY");
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
}
|
||||
else if (count == 0)
|
||||
{
|
||||
draft.setPriceBuildMode("IMPORT");
|
||||
versionMapper.updatePrcPriceSetVersion(draft);
|
||||
}
|
||||
return reloadCatalogVersion(draft.getPsvId(), priceType);
|
||||
}
|
||||
|
||||
private PrcPriceSetVersion findEffectVersion(String priceType)
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(priceType);
|
||||
q.setStatus(STATUS_EFFECT);
|
||||
List<PrcPriceSetVersion> effect = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
return (effect == null || effect.isEmpty()) ? null : effect.get(0);
|
||||
}
|
||||
|
||||
private PrcPriceSetVersion reloadCatalogVersion(Long psvId, String priceType)
|
||||
{
|
||||
List<PrcPriceSetVersion> list = listVersions(priceType, null);
|
||||
for (PrcPriceSetVersion v : list)
|
||||
{
|
||||
if (v != null && psvId.equals(v.getPsvId()))
|
||||
{
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
}
|
||||
|
||||
private Date findPrevEffectiveTime(PrcPriceSetVersion version)
|
||||
{
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceType(version.getPriceType());
|
||||
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (list == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int cur = version.getVersionSeq() == null ? 0 : version.getVersionSeq().intValue();
|
||||
Date prev = null;
|
||||
for (PrcPriceSetVersion v : list)
|
||||
{
|
||||
if (v == null || v.getEffectiveTime() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (version.getPsvId() != null && version.getPsvId().equals(v.getPsvId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int seq = v.getVersionSeq() == null ? 0 : v.getVersionSeq().intValue();
|
||||
if (seq >= cur)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (prev == null || v.getEffectiveTime().after(prev))
|
||||
{
|
||||
prev = v.getEffectiveTime();
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
public PrcPriceSetVersion findVersion(Long psvId)
|
||||
{
|
||||
if (psvId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
}
|
||||
|
||||
public void refreshCatalogStatuses(String priceType)
|
||||
{
|
||||
versionMapper.refreshCatalogStatusByDates(priceType);
|
||||
productMapper.syncEffectByPriceType(priceType);
|
||||
detailMapper.syncEffectByPriceType(priceType);
|
||||
}
|
||||
|
||||
public Long requirePsvId(String priceType, Long psvId, Date effectiveTime)
|
||||
{
|
||||
if (psvId != null)
|
||||
{
|
||||
return psvId;
|
||||
}
|
||||
PrcPriceSetVersion latest = resolveLatestForList(priceType);
|
||||
if (latest != null && STATUS_DRAFT.equals(latest.getStatus()))
|
||||
{
|
||||
if (effectiveTime != null)
|
||||
{
|
||||
latest.setEffectiveTime(effectiveTime);
|
||||
versionMapper.updatePrcPriceSetVersion(latest);
|
||||
}
|
||||
return latest.getPsvId();
|
||||
}
|
||||
if (latest != null && STATUS_EFFECT.equals(latest.getStatus()))
|
||||
{
|
||||
if (effectiveTime == null || sameTime(latest.getEffectiveTime(), effectiveTime))
|
||||
{
|
||||
return latest.getPsvId();
|
||||
}
|
||||
}
|
||||
PrcPriceSetVersion draft = ensureDraftVersion(priceType, effectiveTime, true);
|
||||
return draft.getPsvId();
|
||||
}
|
||||
|
||||
private static boolean sameTime(Date a, Date b)
|
||||
{
|
||||
if (a == null && b == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (a == null || b == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return a.getTime() == b.getTime();
|
||||
}
|
||||
|
||||
public static String normalizeType(String priceType, String defaultType)
|
||||
{
|
||||
return StringUtils.isEmpty(priceType) ? defaultType : priceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.bms.price.service.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
|
||||
/** Collect multipart parts named files and/or file. At most 5. */
|
||||
public final class UploadFileBatch
|
||||
{
|
||||
public static final int MAX_FILES = 5;
|
||||
|
||||
private UploadFileBatch()
|
||||
{
|
||||
}
|
||||
|
||||
public static List<MultipartFile> collect(MultipartFile[] files, MultipartFile file)
|
||||
{
|
||||
List<MultipartFile> list = new ArrayList<MultipartFile>();
|
||||
if (files != null)
|
||||
{
|
||||
for (MultipartFile item : files)
|
||||
{
|
||||
if (item != null && !item.isEmpty())
|
||||
{
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (file != null && !file.isEmpty())
|
||||
{
|
||||
list.add(file);
|
||||
}
|
||||
if (list.isEmpty())
|
||||
{
|
||||
throw new ServiceException("file required");
|
||||
}
|
||||
if (list.size() > MAX_FILES)
|
||||
{
|
||||
throw new ServiceException("\u5355\u6b21\u6700\u591a\u5bfc\u5165 5 \u4e2a\u6587\u4ef6");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user