diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index ccf8d31..fe816b5 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,8 +4,13 @@
-
+
+
+
+
+
+
@@ -15,6 +20,9 @@
+
+
+
@@ -81,6 +89,42 @@
1766987144189
+
+
+ 1766992044615
+
+
+
+ 1766992044615
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mvnw b/mvnw
old mode 100644
new mode 100755
diff --git a/src/main/java/com/gxwebsoft/cms/vo/SideItemVo.java b/src/main/java/com/gxwebsoft/cms/vo/SideItemVo.java
index 169a1cd..2779d23 100644
--- a/src/main/java/com/gxwebsoft/cms/vo/SideItemVo.java
+++ b/src/main/java/com/gxwebsoft/cms/vo/SideItemVo.java
@@ -10,7 +10,7 @@ import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
-@ApiModel(value = "幻灯片广告", description = "幻灯片广告")
+@ApiModel(value = "幻灯片广告项", description = "幻灯片广告项")
public class SideItemVo implements Serializable {
@ApiModelProperty("ID")
diff --git a/src/main/java/com/gxwebsoft/common/system/entity/Cache.java b/src/main/java/com/gxwebsoft/common/system/entity/Cache.java
index f0eabcc..1434b02 100644
--- a/src/main/java/com/gxwebsoft/common/system/entity/Cache.java
+++ b/src/main/java/com/gxwebsoft/common/system/entity/Cache.java
@@ -15,7 +15,7 @@ import java.io.Serializable;
*/
@Data
@EqualsAndHashCode(callSuper = false)
-@ApiModel(value = "Setting对象", description = "缓存管理")
+@ApiModel(value = "Cache对象", description = "缓存管理")
public class Cache implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gxwebsoft/oa/param/CompanyParam.java b/src/main/java/com/gxwebsoft/oa/param/CompanyParam.java
index 39264d3..ead7b75 100644
--- a/src/main/java/com/gxwebsoft/oa/param/CompanyParam.java
+++ b/src/main/java/com/gxwebsoft/oa/param/CompanyParam.java
@@ -21,7 +21,7 @@ import java.util.Set;
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
-@ApiModel(value = "CompanyParam对象", description = "企业信息查询参数")
+@ApiModel(value = "OaCompanyParam对象", description = "企业信息查询参数(OA)")
public class CompanyParam extends BaseParam {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java b/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java
index 950c988..cbab101 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java
@@ -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 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 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 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 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 loadCategoryMap(Integer merchantId) {
List categories = goodsCategoryService.list(new LambdaQueryWrapper()
.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 = 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 getGoods() {
- return goods;
- }
-
- public void setGoods(List goods) {
- this.goods = goods;
- }
- }
-
@ApiOperation("生成海报")
@PostMapping("/make-goods-poster/{goodsId}")
public ApiResult> makePoster(@PathVariable Integer goodsId) throws Exception {
diff --git a/src/main/java/com/gxwebsoft/shop/vo/CartVo.java b/src/main/java/com/gxwebsoft/shop/vo/CartVo.java
index b60c4b0..a3acc48 100644
--- a/src/main/java/com/gxwebsoft/shop/vo/CartVo.java
+++ b/src/main/java/com/gxwebsoft/shop/vo/CartVo.java
@@ -15,7 +15,7 @@ import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
-@ApiModel(value = "Cart对象", description = "购物车")
+@ApiModel(value = "CartVo对象", description = "购物车(返回结构)")
@TableName("shop_cart")
public class CartVo implements Serializable {
diff --git a/src/main/java/com/gxwebsoft/shop/vo/GoodsExcelImportResult.java b/src/main/java/com/gxwebsoft/shop/vo/GoodsExcelImportResult.java
new file mode 100644
index 0000000..40b21da
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/vo/GoodsExcelImportResult.java
@@ -0,0 +1,36 @@
+package com.gxwebsoft.shop.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel(value = "GoodsExcelImportResult", description = "商品Excel导入结果")
+public class GoodsExcelImportResult implements Serializable {
+ @ApiModelProperty("Excel文件路径")
+ private String excelPath;
+
+ @ApiModelProperty("工作表名称")
+ private String sheetName;
+
+ @ApiModelProperty("导入到的商户ID")
+ private Integer merchantId;
+
+ @ApiModelProperty("扫描到的有效行数(有品种/商品名)")
+ private int totalRows;
+
+ @ApiModelProperty("成功入库数量")
+ private int inserted;
+
+ @ApiModelProperty("跳过(已存在同名商品)")
+ private int skippedExists;
+
+ @ApiModelProperty("自动创建的分类数量")
+ private int createdCategories;
+
+ @ApiModelProperty("成功导出的图片数量")
+ private int savedImages;
+}
+