|
|
|
|
@@ -26,6 +26,7 @@ import com.gxwebsoft.common.core.web.PageParam;
|
|
|
|
|
import com.gxwebsoft.common.core.web.BatchParam;
|
|
|
|
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
|
|
|
|
import com.gxwebsoft.common.system.entity.User;
|
|
|
|
|
import com.gxwebsoft.shop.vo.GoodsExcelImportResult;
|
|
|
|
|
import io.swagger.annotations.Api;
|
|
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
|
|
@@ -45,6 +46,7 @@ import java.io.File;
|
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.net.URI;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
@@ -297,17 +299,25 @@ public class GoodsController extends BaseController {
|
|
|
|
|
|
|
|
|
|
@ApiOperation("批量导入商品(Excel)")
|
|
|
|
|
@PostMapping("/import-excel")
|
|
|
|
|
public ApiResult<GoodsImportResult> importExcel(@RequestParam(value = "path", required = false) String path,
|
|
|
|
|
@RequestParam(value = "sheetName", required = false) String sheetName,
|
|
|
|
|
@RequestParam(value = "skipExisting", required = false, defaultValue = "true") Boolean skipExisting,
|
|
|
|
|
@RequestParam(value = "createCategory", required = false, defaultValue = "true") Boolean createCategory,
|
|
|
|
|
@RequestParam(value = "merchantId", required = false) Integer merchantId,
|
|
|
|
|
@RequestParam(value = "defaultIsShow", required = false) Integer defaultIsShow,
|
|
|
|
|
@RequestParam(value = "defaultStock", required = false) Integer defaultStock) {
|
|
|
|
|
public ApiResult<GoodsExcelImportResult> importExcel(@RequestParam(value = "path", required = false) String path,
|
|
|
|
|
@RequestParam(value = "sheetName", required = false) String sheetName,
|
|
|
|
|
@RequestParam(value = "skipExisting", required = false, defaultValue = "true") Boolean skipExisting,
|
|
|
|
|
@RequestParam(value = "createCategory", required = false, defaultValue = "true") Boolean createCategory,
|
|
|
|
|
@RequestParam(value = "merchantId", required = false) Integer merchantId,
|
|
|
|
|
@RequestParam(value = "defaultIsShow", required = false) Integer defaultIsShow,
|
|
|
|
|
@RequestParam(value = "defaultStock", required = false) Integer defaultStock) {
|
|
|
|
|
String excelPath = StrUtil.blankToDefault(path, DEFAULT_IMPORT_EXCEL_PATH);
|
|
|
|
|
String targetSheetName = StrUtil.blankToDefault(sheetName, "Sheet1");
|
|
|
|
|
if (!FileUtil.exist(excelPath)) {
|
|
|
|
|
return fail("Excel文件不存在: " + excelPath,null);
|
|
|
|
|
GoodsExcelImportResult result = new GoodsExcelImportResult();
|
|
|
|
|
result.setExcelPath(excelPath);
|
|
|
|
|
result.setSheetName(targetSheetName);
|
|
|
|
|
ResolvedExcelFile resolvedExcelFile;
|
|
|
|
|
try {
|
|
|
|
|
resolvedExcelFile = resolveExcelFile(excelPath);
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
return fail(e.getMessage(), result);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return fail("Excel文件读取失败: " + e.getMessage(), result).setError(e.toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
User loginUser = getLoginUser();
|
|
|
|
|
@@ -315,16 +325,16 @@ public class GoodsController extends BaseController {
|
|
|
|
|
Integer importMerchantId = merchantId != null ? merchantId : (loginUser == null ? 0 : (loginUser.getMerchantId() == null ? 0 : loginUser.getMerchantId()));
|
|
|
|
|
int importIsShow = defaultIsShow != null ? defaultIsShow : 0;
|
|
|
|
|
int importStock = defaultStock != null ? defaultStock : 0;
|
|
|
|
|
|
|
|
|
|
GoodsImportResult result = new GoodsImportResult();
|
|
|
|
|
result.setExcelPath(excelPath);
|
|
|
|
|
result.setSheetName(targetSheetName);
|
|
|
|
|
result.setMerchantId(importMerchantId);
|
|
|
|
|
|
|
|
|
|
DataFormatter formatter = new DataFormatter();
|
|
|
|
|
try (ZipFile zipFile = new ZipFile(excelPath); InputStream in = new FileInputStream(excelPath); Workbook workbook = new XSSFWorkbook(in)) {
|
|
|
|
|
List<Goods> toInsert = new java.util.ArrayList<>();
|
|
|
|
|
try (ZipFile zipFile = new ZipFile(resolvedExcelFile.file);
|
|
|
|
|
InputStream in = new FileInputStream(resolvedExcelFile.file);
|
|
|
|
|
Workbook workbook = new XSSFWorkbook(in)) {
|
|
|
|
|
Sheet sheet = workbook.getSheet(targetSheetName);
|
|
|
|
|
if (sheet == null) {
|
|
|
|
|
return fail("未找到工作表: " + targetSheetName,null);
|
|
|
|
|
return fail("未找到工作表: " + targetSheetName, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Map<String, String> cellImageIdToMediaEntry = buildCellImageIdToMediaEntry(zipFile);
|
|
|
|
|
@@ -393,6 +403,7 @@ public class GoodsController extends BaseController {
|
|
|
|
|
if (category == null && createCategory) {
|
|
|
|
|
category = createCategory(importMerchantId, categoryName.trim());
|
|
|
|
|
categoryByTitle.put(category.getTitle(), category);
|
|
|
|
|
result.setCreatedCategories(result.getCreatedCategories() + 1);
|
|
|
|
|
}
|
|
|
|
|
if (category != null) {
|
|
|
|
|
goods.setCategoryId(category.getCategoryId());
|
|
|
|
|
@@ -406,26 +417,82 @@ public class GoodsController extends BaseController {
|
|
|
|
|
String url = saveCellImage(zipFile, mediaEntry, imageId);
|
|
|
|
|
if (StrUtil.isNotBlank(url)) {
|
|
|
|
|
goods.setImage(url);
|
|
|
|
|
result.setSavedImages(result.getSavedImages() + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.getGoods().add(goods);
|
|
|
|
|
toInsert.add(goods);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.getGoods().isEmpty()) {
|
|
|
|
|
if (toInsert.isEmpty()) {
|
|
|
|
|
return success("未读取到可导入的数据", result);
|
|
|
|
|
}
|
|
|
|
|
if (goodsService.saveBatch(result.getGoods())) {
|
|
|
|
|
result.setInserted(result.getGoods().size());
|
|
|
|
|
if (goodsService.saveBatch(toInsert)) {
|
|
|
|
|
result.setInserted(toInsert.size());
|
|
|
|
|
return success("导入成功", result);
|
|
|
|
|
}
|
|
|
|
|
return fail("导入失败", result);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return fail("导入异常: " + e.getMessage(), result).setError(e.toString());
|
|
|
|
|
} finally {
|
|
|
|
|
resolvedExcelFile.cleanupQuietly();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static final class ResolvedExcelFile {
|
|
|
|
|
private final File file;
|
|
|
|
|
private final boolean deleteOnClose;
|
|
|
|
|
|
|
|
|
|
private ResolvedExcelFile(File file, boolean deleteOnClose) {
|
|
|
|
|
this.file = file;
|
|
|
|
|
this.deleteOnClose = deleteOnClose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void cleanupQuietly() {
|
|
|
|
|
if (!deleteOnClose) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
FileUtil.del(file);
|
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ResolvedExcelFile resolveExcelFile(String excelPath) throws Exception {
|
|
|
|
|
if (StrUtil.isBlank(excelPath)) {
|
|
|
|
|
throw new IllegalArgumentException("Excel路径为空");
|
|
|
|
|
}
|
|
|
|
|
String trimmed = excelPath.trim();
|
|
|
|
|
if (StrUtil.startWithIgnoreCase(trimmed, "http://") || StrUtil.startWithIgnoreCase(trimmed, "https://")) {
|
|
|
|
|
String suffix = ".xlsx";
|
|
|
|
|
String withoutQuery = StrUtil.subBefore(trimmed, "?", false);
|
|
|
|
|
int dot = withoutQuery.lastIndexOf('.');
|
|
|
|
|
if (dot > 0 && dot < withoutQuery.length() - 1) {
|
|
|
|
|
suffix = "." + withoutQuery.substring(dot + 1);
|
|
|
|
|
}
|
|
|
|
|
File tmp = File.createTempFile("goods-import-", suffix);
|
|
|
|
|
HttpUtil.downloadFile(trimmed, tmp);
|
|
|
|
|
if (!tmp.exists() || tmp.length() == 0) {
|
|
|
|
|
FileUtil.del(tmp);
|
|
|
|
|
throw new IllegalStateException("Excel下载失败或文件为空");
|
|
|
|
|
}
|
|
|
|
|
return new ResolvedExcelFile(tmp, true);
|
|
|
|
|
}
|
|
|
|
|
if (StrUtil.startWithIgnoreCase(trimmed, "file:")) {
|
|
|
|
|
File f = new File(URI.create(trimmed));
|
|
|
|
|
if (!f.exists()) {
|
|
|
|
|
throw new IllegalArgumentException("Excel文件不存在: " + excelPath);
|
|
|
|
|
}
|
|
|
|
|
return new ResolvedExcelFile(f, false);
|
|
|
|
|
}
|
|
|
|
|
if (!FileUtil.exist(trimmed)) {
|
|
|
|
|
throw new IllegalArgumentException("Excel文件不存在: " + excelPath);
|
|
|
|
|
}
|
|
|
|
|
return new ResolvedExcelFile(FileUtil.file(trimmed), false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Map<String, GoodsCategory> loadCategoryMap(Integer merchantId) {
|
|
|
|
|
List<GoodsCategory> categories = goodsCategoryService.list(new LambdaQueryWrapper<GoodsCategory>()
|
|
|
|
|
.eq(GoodsCategory::getMerchantId, merchantId)
|
|
|
|
|
@@ -597,63 +664,6 @@ public class GoodsController extends BaseController {
|
|
|
|
|
return idToMedia;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class GoodsImportResult {
|
|
|
|
|
private String excelPath;
|
|
|
|
|
private String sheetName;
|
|
|
|
|
private int totalRows;
|
|
|
|
|
private int inserted;
|
|
|
|
|
private int skippedExists;
|
|
|
|
|
private List<Goods> goods = new java.util.ArrayList<>();
|
|
|
|
|
|
|
|
|
|
public String getExcelPath() {
|
|
|
|
|
return excelPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setExcelPath(String excelPath) {
|
|
|
|
|
this.excelPath = excelPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getSheetName() {
|
|
|
|
|
return sheetName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSheetName(String sheetName) {
|
|
|
|
|
this.sheetName = sheetName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getTotalRows() {
|
|
|
|
|
return totalRows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setTotalRows(int totalRows) {
|
|
|
|
|
this.totalRows = totalRows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getInserted() {
|
|
|
|
|
return inserted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setInserted(int inserted) {
|
|
|
|
|
this.inserted = inserted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getSkippedExists() {
|
|
|
|
|
return skippedExists;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSkippedExists(int skippedExists) {
|
|
|
|
|
this.skippedExists = skippedExists;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<Goods> getGoods() {
|
|
|
|
|
return goods;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setGoods(List<Goods> goods) {
|
|
|
|
|
this.goods = goods;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ApiOperation("生成海报")
|
|
|
|
|
@PostMapping("/make-goods-poster/{goodsId}")
|
|
|
|
|
public ApiResult<?> makePoster(@PathVariable Integer goodsId) throws Exception {
|
|
|
|
|
|