增加上传附件到oss
This commit is contained in:
+109
-13
@@ -22,6 +22,8 @@ import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
@@ -66,6 +68,8 @@ import com.bms.system.mapper.BmsRegionMapper;
|
||||
@Service
|
||||
public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BmsProductServiceImpl.class);
|
||||
|
||||
private static final String CATALOG_SHEET = "\u4ea7\u54c1\u76ee\u5f55";
|
||||
private static final Set<String> SKIP_SHEETS = new HashSet<>(Arrays.asList(
|
||||
CATALOG_SHEET, "\u9000\u4ef6\u91cd\u6d3e", "\u7533\u62a5\u8bf4\u660e", "\u8d54\u507f\u6807\u51c6"));
|
||||
@@ -415,26 +419,16 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
index++;
|
||||
reportImportProductProgress(taskId, index, total, product.getProductCode());
|
||||
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
TransactionStatus tx = transactionManager.getTransaction(def);
|
||||
try
|
||||
{
|
||||
product.setEffectiveDate(forcedEff);
|
||||
if (importPsvId != null)
|
||||
{
|
||||
product.setPsvId(importPsvId);
|
||||
}
|
||||
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode, unmatchedCountries, forcedEff);
|
||||
transactionManager.commit(tx);
|
||||
int[] nums = importOneProductWithDeadlockRetry(workbook, product, operName, regionByNameCn,
|
||||
euRegions, zoneByCountryAndCode, unmatchedCountries, forcedEff, importPsvId);
|
||||
successNum += nums[0];
|
||||
skipNum += nums[1];
|
||||
countryNum += nums[2];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
transactionManager.rollback(tx);
|
||||
String tip = e.getMessage();
|
||||
if (StringUtils.isEmpty(tip))
|
||||
{
|
||||
@@ -1151,6 +1145,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
d.setIsDel(BmsProduct.IS_DEL_NO);
|
||||
toInsert.add(d);
|
||||
}
|
||||
sortProductCountriesForInsert(toInsert);
|
||||
insertProductCountryChunks(toInsert);
|
||||
return toInsert.size();
|
||||
}
|
||||
@@ -1169,13 +1164,36 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
return taskId;
|
||||
}
|
||||
|
||||
/** Stable insert order reduces InnoDB secondary-index lock inversion. */
|
||||
private static void sortProductCountriesForInsert(List<BmsProductCountry> rows)
|
||||
{
|
||||
if (rows == null || rows.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rows.sort((a, b) -> {
|
||||
int c = String.valueOf(a == null ? null : a.getCountryCode())
|
||||
.compareToIgnoreCase(String.valueOf(b == null ? null : b.getCountryCode()));
|
||||
if (c != 0) return c;
|
||||
c = String.valueOf(a == null ? null : a.getZoneCode())
|
||||
.compareToIgnoreCase(String.valueOf(b == null ? null : b.getZoneCode()));
|
||||
if (c != 0) return c;
|
||||
java.math.BigDecimal aw = a == null ? null : a.getWeightMin();
|
||||
java.math.BigDecimal bw = b == null ? null : b.getWeightMin();
|
||||
if (aw == null && bw == null) return 0;
|
||||
if (aw == null) return -1;
|
||||
if (bw == null) return 1;
|
||||
return aw.compareTo(bw);
|
||||
});
|
||||
}
|
||||
|
||||
private void insertProductCountryChunks(List<BmsProductCountry> rows)
|
||||
{
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int size = 200;
|
||||
int size = 100;
|
||||
for (int i = 0; i < rows.size(); i += size)
|
||||
{
|
||||
int end = Math.min(i + size, rows.size());
|
||||
@@ -1183,6 +1201,84 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* One-product import in REQUIRES_NEW; retry whole txn on InnoDB deadlock.
|
||||
*/
|
||||
private int[] importOneProductWithDeadlockRetry(Workbook workbook, BmsProduct product, String operName,
|
||||
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
|
||||
Map<String, PrcZone> zoneByCountryAndCode, Set<String> unmatchedCountries, Date forcedEff, Long importPsvId)
|
||||
throws Exception
|
||||
{
|
||||
int max = 8;
|
||||
Exception last = null;
|
||||
for (int attempt = 1; attempt <= max; attempt++)
|
||||
{
|
||||
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
TransactionStatus tx = transactionManager.getTransaction(def);
|
||||
try
|
||||
{
|
||||
product.setEffectiveDate(forcedEff);
|
||||
if (importPsvId != null)
|
||||
{
|
||||
product.setPsvId(importPsvId);
|
||||
}
|
||||
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode, unmatchedCountries, forcedEff);
|
||||
transactionManager.commit(tx);
|
||||
return nums;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try { transactionManager.rollback(tx); } catch (Exception ignore) { }
|
||||
last = e;
|
||||
if (attempt >= max || !isImportDeadlock(e))
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
log.warn("product import deadlock, code={}, retry {}/{}", product.getProductCode(),
|
||||
Integer.valueOf(attempt), Integer.valueOf(max));
|
||||
try
|
||||
{
|
||||
Thread.sleep(80L * attempt + (long) (Math.random() * 40));
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw last != null ? last : new ServiceException("product import retry exhausted");
|
||||
}
|
||||
|
||||
private static boolean isImportDeadlock(Throwable ex)
|
||||
{
|
||||
Throwable cur = ex;
|
||||
while (cur != null)
|
||||
{
|
||||
if (cur instanceof org.springframework.dao.PessimisticLockingFailureException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
String name = cur.getClass().getName();
|
||||
if (name.indexOf("MySQLTransactionRollbackException") >= 0
|
||||
|| name.indexOf("DeadlockLoser") >= 0
|
||||
|| name.indexOf("CannotAcquireLock") >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
String msg = cur.getMessage();
|
||||
if (msg != null && (msg.indexOf("Deadlock found when trying to get lock") >= 0
|
||||
|| msg.indexOf("try restarting transaction") >= 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
cur = cur.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String resolveCountryLabel(String countryCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(countryCode))
|
||||
|
||||
+25
-1
@@ -996,6 +996,7 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
d.setIsDel(BmsRedlineProduct.IS_DEL_NO);
|
||||
toInsert.add(d);
|
||||
}
|
||||
sortRedlineCountriesForInsert(toInsert);
|
||||
insertRedlineCountryChunks(toInsert);
|
||||
return toInsert.size();
|
||||
}
|
||||
@@ -1014,13 +1015,36 @@ public class BmsRedlineProductServiceImpl implements IBmsRedlineProductService
|
||||
return taskId;
|
||||
}
|
||||
|
||||
/** Stable insert order reduces InnoDB secondary-index lock inversion. */
|
||||
private static void sortRedlineCountriesForInsert(List<BmsRedlineProductCountry> rows)
|
||||
{
|
||||
if (rows == null || rows.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rows.sort((a, b) -> {
|
||||
int c = String.valueOf(a == null ? null : a.getCountryCode())
|
||||
.compareToIgnoreCase(String.valueOf(b == null ? null : b.getCountryCode()));
|
||||
if (c != 0) return c;
|
||||
c = String.valueOf(a == null ? null : a.getZoneCode())
|
||||
.compareToIgnoreCase(String.valueOf(b == null ? null : b.getZoneCode()));
|
||||
if (c != 0) return c;
|
||||
java.math.BigDecimal aw = a == null ? null : a.getWeightMin();
|
||||
java.math.BigDecimal bw = b == null ? null : b.getWeightMin();
|
||||
if (aw == null && bw == null) return 0;
|
||||
if (aw == null) return -1;
|
||||
if (bw == null) return 1;
|
||||
return aw.compareTo(bw);
|
||||
});
|
||||
}
|
||||
|
||||
private void insertRedlineCountryChunks(List<BmsRedlineProductCountry> rows)
|
||||
{
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int size = 200;
|
||||
int size = 100;
|
||||
for (int i = 0; i < rows.size(); i += size)
|
||||
{
|
||||
int end = Math.min(i + size, rows.size());
|
||||
|
||||
+23
-19
@@ -1,36 +1,38 @@
|
||||
package com.bms.price.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.common.config.BmsConfig;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.DateUtils;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.common.utils.file.FileUploadUtils;
|
||||
import com.bms.common.utils.file.OssStorage;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
import com.bms.price.mapper.PrcPriceExcelFileMapper;
|
||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
|
||||
/**
|
||||
* Persist import Excel bytes and metadata
|
||||
* Persist import Excel bytes and metadata (OSS, same pattern as AR bill export).
|
||||
*/
|
||||
@Service
|
||||
public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
{
|
||||
private static final String SUB_DIR = "price/excel";
|
||||
/** Object key prefix under configured OSS pathPrefix. */
|
||||
private static final String OBJECT_DIR = "price/excel";
|
||||
|
||||
@Autowired
|
||||
private PrcPriceExcelFileMapper excelFileMapper;
|
||||
|
||||
@Autowired
|
||||
private OssStorage ossStorage;
|
||||
|
||||
@Override
|
||||
public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId)
|
||||
{
|
||||
@@ -71,30 +73,32 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
ext = "xlsx";
|
||||
}
|
||||
ext = ext.toLowerCase(Locale.ROOT);
|
||||
String type = excelType.trim().toUpperCase(Locale.ROOT);
|
||||
String contentType = resolveContentType(ext);
|
||||
|
||||
String uploadDir = BmsConfig.getUploadPath() + File.separator + SUB_DIR;
|
||||
String pathName = DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + "." + ext;
|
||||
File dest = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
|
||||
FileOutputStream fos = null;
|
||||
String objectKey = OBJECT_DIR + "/" + type.toLowerCase(Locale.ROOT) + "/"
|
||||
+ DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + "." + ext;
|
||||
Path temp = Files.createTempFile("price-excel-", "." + ext);
|
||||
String storageKey;
|
||||
try
|
||||
{
|
||||
fos = new FileOutputStream(dest);
|
||||
fos.write(bytes);
|
||||
Files.write(temp, bytes);
|
||||
storageKey = ossStorage.upload(temp, objectKey, contentType);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.close(fos);
|
||||
try { Files.deleteIfExists(temp); } catch (Exception ignore) { }
|
||||
}
|
||||
String storedPath = FileUploadUtils.getPathFileName(uploadDir, pathName);
|
||||
|
||||
PrcPriceExcelFile row = new PrcPriceExcelFile();
|
||||
row.setExcelType(excelType.trim().toUpperCase(Locale.ROOT));
|
||||
row.setExcelType(type);
|
||||
row.setPsvId(psvId);
|
||||
row.setEffectiveTime(effectiveTime);
|
||||
row.setFileName(originalName);
|
||||
row.setFilePath(storedPath);
|
||||
// Persist oss://bucket/key like AR bill storageKey (download via /common/download/resource).
|
||||
row.setFilePath(storageKey);
|
||||
row.setFileSize(Long.valueOf(bytes.length));
|
||||
row.setContentType(resolveContentType(ext));
|
||||
row.setContentType(contentType);
|
||||
row.setFileExtension(ext);
|
||||
row.setCreateBy(operName);
|
||||
excelFileMapper.insertPrcPriceExcelFile(row);
|
||||
@@ -124,6 +128,6 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
{
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
}
|
||||
return "";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user