From 99a88a86a95e53578e6deb1a756bd623db02e7bf Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 5 Aug 2025 10:43:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=95=86=E5=93=81=EF=BC=8C=E5=93=81=E7=89=8C?= =?UTF-8?q?=EF=BC=8C=E5=95=86=E5=93=81=E5=88=86=E7=B1=BBexcle=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=96=B0=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=9C=AA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ShopSyncImportController.java | 92 ++++++++ .../mall/shop/sync/dto/SxCategoryModel.java | 3 +- .../shop/sync/exelModel/BrandModelExcel.java | 23 ++ .../sync/exelModel/SxCategoryModelExcel.java | 44 ++++ .../shop/sync/exelModel/SxGoosModelExcel.java | 52 +++++ .../sync/listen/ShopBatchSubmitListener.java | 118 ++++++++++ .../sync/service/ShopSyncImportService.java | 30 +++ .../sync/service/SyncThirdDataService.java | 28 +++ .../impl/ShopSyncImportServiceImpl.java | 215 ++++++++++++++++++ 9 files changed, 604 insertions(+), 1 deletion(-) create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ShopSyncImportController.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxCategoryModelExcel.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ShopSyncImportService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ShopSyncImportServiceImpl.java diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ShopSyncImportController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ShopSyncImportController.java new file mode 100644 index 00000000..d2822635 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ShopSyncImportController.java @@ -0,0 +1,92 @@ +package com.suisung.mall.shop.sync.controller; + +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.service.impl.BaseControllerImpl; +import com.suisung.mall.shop.sync.service.ShopSyncImportService; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; + +/** + * 商品数据导入, + * 包括,分类,和商品的 导入,导入之后自动生成分类的其他属性,由于系统的局限性,品牌不做导入,默认生成其他匹配 + */ +@RestController +@RequestMapping("/admin/shop/shop-sync-import") +@lombok.extern.slf4j.Slf4j +public class ShopSyncImportController extends BaseControllerImpl { + + @Autowired + private ShopSyncImportService shopSyncImportService; + + /** + * 品牌导入模板下载 + * @return + */ + @ApiOperation(value = "品牌导入模板下载", notes = "模板下载") + @RequestMapping(value = "/brandTemplate", method = RequestMethod.GET) + public void brandTemplate(HttpServletResponse response) { + shopSyncImportService.downloadBrandTemplate(response); + } + + /** + * 商品分类导入模板下载 + * @return + */ + @ApiOperation(value = "商品分类导入模板下载", notes = "模板下载") + @RequestMapping(value = "/categoryTemplate", method = RequestMethod.GET) + public void categoryTemplate(HttpServletResponse response) { + shopSyncImportService.downloadCategoryTemplate(response); + } + + /** + * 商品导入模板下载 + * @return + */ + @ApiOperation(value = "商品导入模板下载", notes = "模板下载") + @RequestMapping(value = "/shopTemplate", method = RequestMethod.GET) + public void shopTemplate(HttpServletResponse response) { + shopSyncImportService.downloadShopsTemplate(response); + } + + /** + * 品牌数据导入 + * @return + */ + @ApiOperation(value = "品牌数据导入", notes = "品牌数据导入") + @RequestMapping(value = "/brandImportData", method = RequestMethod.POST) + public CommonResult brandImportData(@RequestParam("file") MultipartFile file,@RequestParam("storeId")String storeId) { + // ImportResult result = productMappingService.importData(file); + // return !result.getErrorMessages().isEmpty() ?CommonResult.failed((IErrorCode) result.getErrorMessages()):CommonResult.success(result); + return shopSyncImportService.importBrandData(file,storeId); + } + + /** + * 商品分类导入数据 + * @return + */ + @ApiOperation(value = "商品分类数据导入", notes = "分类数据导入") + @RequestMapping(value = "/categoryImportData", method = RequestMethod.POST) + public CommonResult categoryImportData(@RequestParam("file") MultipartFile file,@RequestParam("storeId")String storeId) { + // ImportResult result = productMappingService.importData(file); + // return !result.getErrorMessages().isEmpty() ?CommonResult.failed((IErrorCode) result.getErrorMessages()):CommonResult.success(result); + return shopSyncImportService.importCategoryData(file,storeId); + } + + /** + * 商品导入数据 + * @return + */ + @ApiOperation(value = "商品数据导入", notes = "分类数据导入") + @RequestMapping(value = "/shopImportData", method = RequestMethod.POST) + public CommonResult shopImportData(@RequestParam("file") MultipartFile file,@RequestParam("storeId")String storeId) { + // ImportResult result = productMappingService.importData(file); + // return !result.getErrorMessages().isEmpty() ?CommonResult.failed((IErrorCode) result.getErrorMessages()):CommonResult.success(result); + shopSyncImportService.importShopsData(file,storeId); + return CommonResult.success("服务器正则处理文件,稍后查看商品列表"); + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/dto/SxCategoryModel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/dto/SxCategoryModel.java index 9f5e8660..873712f3 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/dto/SxCategoryModel.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/dto/SxCategoryModel.java @@ -28,5 +28,6 @@ public class SxCategoryModel { private String first_category_name; @ApiModelProperty(value = "第二级父类") private String second_category_name; - + @ApiModelProperty(value = "品牌名称") + private String brandName; } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java new file mode 100644 index 00000000..c25a02ae --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java @@ -0,0 +1,23 @@ +package com.suisung.mall.shop.sync.exelModel; + + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class BrandModelExcel{ + public static final String TEMPLATE_NAME = "品牌导入模板.xlsx"; + @ApiModelProperty("品牌名称") + @ExcelProperty(value = "商品名称", index = 0) + private String brand_name; + + @ApiModelProperty("品牌描述") + @ExcelProperty(value = "品牌描述", index = 1) + private String brand_desc; + + @ApiModelProperty("是否推荐") + @ExcelIgnore + private String brand_recommend="0"; +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxCategoryModelExcel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxCategoryModelExcel.java new file mode 100644 index 00000000..b60450df --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxCategoryModelExcel.java @@ -0,0 +1,44 @@ +package com.suisung.mall.shop.sync.exelModel; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * 模型对应 + */ +@Data +public class SxCategoryModelExcel { + public static final String TEMPLATE_NAME = "商品分类导入模板.xlsx"; + @ApiModelProperty(value = "商品分类名称") + @ExcelProperty(value = "商品分类名称", index = 0) + private String category_name; + @ApiModelProperty(value = "分类图片") + @ExcelIgnore + private String category_image="https://digitalassets.tesla.com/tesla-contents/image/upload/f_auto,q_auto/Homepage-Model-Y-2-Promo-Hero-Tablet-CN.png"; + @ApiModelProperty(value = "是否允许虚拟商品(ENUM):1-是; 0-否") + @ExcelIgnore + private Integer category_virtual_enable=0; + /** + * 产品类型=商品分类名称 + */ + @ApiModelProperty(value = "产品类型") + @ExcelIgnore + private String product_type=category_name; + /** + * 第一级分类 当前分类的最顶层,如 生鲜->蔬菜->菜苗 如果当前分类(category_name)为菜苗,则第一级分类是”生鲜“,第二级分类是”蔬菜“,如果当前分类是蔬菜,则第一级分类是生鲜,第二级分类为空 + * 第二级分类 当前分类最顶层数的第二层 + */ + @ApiModelProperty(value = "第一级分类") + @ExcelProperty(value = "第一级分类", index = 1) + private String first_category_name; + @ApiModelProperty(value = "第二级分类") + @ExcelProperty(value = "第二级分类", index =2) + private String second_category_name; + + @ApiModelProperty(value = "品牌名称") + @ExcelIgnore + private String brandName="其他品牌"; +} \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java new file mode 100644 index 00000000..d1e291d0 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java @@ -0,0 +1,52 @@ +package com.suisung.mall.shop.sync.exelModel; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 思迅同步商品数据入口数据 + */ +@Data +public class SxGoosModelExcel { + public static final String TEMPLATE_NAME = "商品导入模板.xlsx"; + @ApiModelProperty("商品名称") + @ExcelProperty(value = "商品名称", index = 0) + private String product_name; + + @ApiModelProperty("商品货号") + @ExcelProperty(value = "商品货号", index = 1) + private String product_number; + + @ApiModelProperty("商品条形码") + @ExcelProperty(value = "商品条形码", index = 2) + private String product_barcode; + + @ApiModelProperty("所属分类") + @ExcelProperty(value = "所属分类", index = 3) + private String first_category_name; + + @ApiModelProperty("零售价") + @ExcelProperty(value = "零售价", index = 4) + private BigDecimal retail_price; + + @ApiModelProperty("原价") + @ExcelIgnore + private BigDecimal original_price=retail_price; + + @ApiModelProperty("库存") + @ExcelProperty(value = "库存", index = 5) + private BigDecimal stock; + + @ApiModelProperty("规格单位") + @ExcelProperty(value = "规格单位", index = 6) + private String unit; + + @ApiModelProperty("最大购买商品量") + @ExcelProperty(value = "最大购买商品量", index = 7) + private Integer buy_limit; + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java new file mode 100644 index 00000000..33d44387 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java @@ -0,0 +1,118 @@ +package com.suisung.mall.shop.sync.listen; + +import cn.hutool.json.JSONArray; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.google.gson.Gson; +import com.suisung.mall.shop.sync.exelModel.SxGoosModelExcel; +import com.suisung.mall.shop.sync.service.SyncThirdDataService; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +@Component +@Slf4j +public class ShopBatchSubmitListener extends AnalysisEventListener { + // 批处理阈值 + private static final int BATCH_SIZE = 10; + // 数据缓存 + private List cachedDataList = new ArrayList<>(BATCH_SIZE); + + private SyncThirdDataService syncThirdDataService; + + // 线程池配置 + private final ExecutorService executorService; + + private List> futures ; + private AtomicInteger success; + private AtomicInteger fails; + private AtomicInteger batchSize; + @Setter + @Getter + private String storeId; + + @Setter + @Getter + private String isNegativeAllowed; + + @Setter + @Getter + private Map brandMaps; + + + public ShopBatchSubmitListener(SyncThirdDataService syncThirdDataService) { + this.syncThirdDataService = syncThirdDataService; + // 创建线程池(根据CPU核心数优化) + int corePoolSize = Runtime.getRuntime().availableProcessors(); + this.executorService = Executors.newFixedThreadPool(corePoolSize); + this.futures = new ArrayList<>(); + this.success = new AtomicInteger(); + this.fails = new AtomicInteger(); + this.batchSize= new AtomicInteger(); + } + + @Override + public void invoke(SxGoosModelExcel sxGoosModelExcel, AnalysisContext analysisContext) { + synchronized (cachedDataList) { + cachedDataList.add(sxGoosModelExcel); + // 达到批处理阈值时提交 + if (cachedDataList.size() >= BATCH_SIZE) { + batchSize.incrementAndGet(); + submitBatch(); + // 提交后清空缓存 + cachedDataList.clear(); + } + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + synchronized (cachedDataList) { + // 处理最后一批不足BATCH_SIZE的数据 + if (!cachedDataList.isEmpty()) { + batchSize.incrementAndGet(); + submitBatch(); + cachedDataList.clear(); + } + } + // 等待所有任务完成 + for (Future future : futures) { + try { + log.info("任务结果:{}" ,future.get()); + } catch (Exception e) { + log.info("任务执行异常: {}", e.getMessage()); + } + } + log.info("Excel解析完成,总处理条数: {}" , context.readSheetHolder().getTotal()); + log.info("成功数量:{};失败数量:{}",success.get(),fails.get()); + // 关闭线程池 + executorService.shutdown(); + } + + private void submitBatch() { + // 复制当前批次数据(避免异步修改) + List batchCopy = new ArrayList<>(cachedDataList); + futures.add(executorService.submit(()->{ + try { + Gson gson=new Gson(); + String jsonShops=gson.toJson(batchCopy); + JSONArray jsonArray=new JSONArray(jsonShops); + syncThirdDataService.baseSaveOrUpdateGoodsBatch(jsonArray,storeId,isNegativeAllowed,brandMaps); + log.info("已提交批次: {} 条", cachedDataList.size()); + success.getAndIncrement(); + return "完成"+batchSize.get(); + } catch (Exception e) { + fails.getAndIncrement(); + return "失败:"+batchSize.get()+";失败原因:"+e.getMessage(); + } + })); + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ShopSyncImportService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ShopSyncImportService.java new file mode 100644 index 00000000..f75fed22 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ShopSyncImportService.java @@ -0,0 +1,30 @@ +package com.suisung.mall.shop.sync.service; + +import com.suisung.mall.common.api.CommonResult; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; + + +public interface ShopSyncImportService{ + + + // 下载品牌导入模板 + void downloadBrandTemplate(HttpServletResponse response); + + // 下载商品分类导入模板 + void downloadCategoryTemplate(HttpServletResponse response); + + // 下载商品导入模板 + void downloadShopsTemplate(HttpServletResponse response); + + // 导入品牌Excel数据 + CommonResult importBrandData(MultipartFile file,String storeId); + + + // 导入商品分类Excel数据 + CommonResult importCategoryData(MultipartFile file,String storeId); + + // 导入商品Excel数据 + void importShopsData(MultipartFile file,String storeId); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java index c6be1447..4515d8a0 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java @@ -10,6 +10,8 @@ package com.suisung.mall.shop.sync.service; import cn.hutool.json.JSONArray; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.base.ShopBaseProductBrand; +import com.suisung.mall.common.modules.base.ShopBaseProductCategory; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; import org.springframework.core.io.Resource; @@ -169,4 +171,30 @@ public interface SyncThirdDataService { ThirdApiRes syncRefreshTime(@RequestParam String appKey, @RequestParam String sign); CommonResult importLibProductImg(String updateTime); + + + /** + * 保存品牌接口 + * 内部使用 + * @return + */ + int baseSaveOrUpdateShopBaseProductBrandBatch(List goodBrandList,String storeId,JSONArray brandListJSON); + + /** + * 保存商品分类接口 + * 内部使用 + * @return + */ + int baseSaveOrUpdateShopBaseProductCategoryBatch(List list , JSONArray categoryListJSON, + String storeId); + + + /** + * 保存商品接口 + * 内部使用 + * @return + */ + int baseSaveOrUpdateGoodsBatch(JSONArray goodsListJSON,String storeId,String isNegativeAllowed, + Map brandMaps); + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ShopSyncImportServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ShopSyncImportServiceImpl.java new file mode 100644 index 00000000..e861a53d --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ShopSyncImportServiceImpl.java @@ -0,0 +1,215 @@ +package com.suisung.mall.shop.sync.service.impl; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.google.gson.Gson; +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.exception.ApiException; +import com.suisung.mall.common.modules.base.ShopBaseProductBrand; +import com.suisung.mall.common.modules.base.ShopBaseProductCategory; +import com.suisung.mall.common.modules.sync.StoreDbConfig; +import com.suisung.mall.shop.base.service.ShopBaseProductBrandService; +import com.suisung.mall.shop.sync.excleHandle.TemplateStyleHandler; +import com.suisung.mall.shop.sync.exelModel.*; +import com.suisung.mall.shop.sync.listen.ShopBatchSubmitListener; +import com.suisung.mall.shop.sync.service.ShopSyncImportService; +import com.suisung.mall.shop.sync.service.StoreDbConfigService; +import com.suisung.mall.shop.sync.service.SyncThirdDataService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class ShopSyncImportServiceImpl implements ShopSyncImportService { + @Value("${file.upload-dir}") + private String uploadDir; + + @Autowired + private SyncThirdDataService syncThirdDataService; + @Autowired + private ShopBaseProductBrandService productBrandService; + @Autowired + private StoreDbConfigService storeDbConfigService; + private final int limitCnt = 100; + @Override + public void downloadBrandTemplate(HttpServletResponse response) { + try { + // 设置响应头 + setExcelResponseHeader(response, BrandModelExcel.TEMPLATE_NAME); + // 创建空模板 + EasyExcel.write(response.getOutputStream(), BrandModelExcel.class) + .sheet("商品品牌") + .registerWriteHandler(new TemplateStyleHandler()) + .doWrite(new ArrayList<>()); + } catch (IOException e) { + log.error("商品品牌下载模板失败", e); + throw new RuntimeException("商品品牌下载模板失败"); + } + } + + @Override + public void downloadCategoryTemplate(HttpServletResponse response) { + try { + // 设置响应头 + setExcelResponseHeader(response, SxCategoryModelExcel.TEMPLATE_NAME); + // 创建空模板 + EasyExcel.write(response.getOutputStream(), SxCategoryModelExcel.class) + .sheet("商品分类") + .registerWriteHandler(new TemplateStyleHandler()) + .doWrite(new ArrayList<>()); + } catch (IOException e) { + log.error("商品分类下载模板失败", e); + throw new RuntimeException("商品分类下载模板失败"); + } + } + + @Override + public void downloadShopsTemplate(HttpServletResponse response) { + try { + // 设置响应头 + setExcelResponseHeader(response, SxGoosModelExcel.TEMPLATE_NAME); + // 创建空模板 + EasyExcel.write(response.getOutputStream(), SxGoosModelExcel.class) + .sheet("商品") + .registerWriteHandler(new TemplateStyleHandler()) + .doWrite(new ArrayList<>()); + } catch (IOException e) { + log.error("商品下载模板失败", e); + throw new RuntimeException("商品下载模板失败"); + } + } + + @Override + public CommonResult importBrandData(MultipartFile file,String storeId) { + String fileName = storeUploadedFile(file); + try { + List excelList = readBrandExcelData(fileName); + if(excelList.isEmpty()) { + return CommonResult.failed("品牌数据为空"); + } + if(excelList.size()>limitCnt){ + return CommonResult.failed("导入品牌数据超过"+limitCnt+"条"); + } + Gson gson=new Gson(); + String brandJsonStr=gson.toJson(excelList); + JSONArray jsonArray=new JSONArray(brandJsonStr); + List goodBrandList = JSONUtil.toList(jsonArray, ShopBaseProductBrand.class); + syncThirdDataService.baseSaveOrUpdateShopBaseProductBrandBatch(goodBrandList,storeId,jsonArray); + return CommonResult.success(); + } catch (Exception e) { + log.error("导入品牌数据失败", e); + throw new RuntimeException("导入品牌数据失败: " + e.getMessage()); + } + } + + @Override + public CommonResult importCategoryData(MultipartFile file,String storeId) { + String fileName = storeUploadedFile(file); + try { + List excelList = readCategoryExcelData(fileName); + if(excelList.isEmpty()) { + return CommonResult.failed("商品分类数据为空"); + } + if(excelList.size()>limitCnt){ + return CommonResult.failed("导入商品分类数据超过"+limitCnt+"条"); + } + Gson gson=new Gson(); + String brandJsonStr=gson.toJson(excelList); + JSONArray jsonArray=new JSONArray(brandJsonStr); + List goodBrandList = JSONUtil.toList(jsonArray, ShopBaseProductCategory.class); + syncThirdDataService.baseSaveOrUpdateShopBaseProductCategoryBatch(goodBrandList,jsonArray,storeId); + return CommonResult.success(); + } catch (Exception e) { + log.error("导入数据失败", e); + throw new ApiException("导入数据失败: " + e.getMessage()); + } + } + + @Override + @Async + public void importShopsData(MultipartFile file,String storeId) { + String fileName = storeUploadedFile(file); + readAndImportShopsExcelData(fileName,storeId); + } + + // 存储上传文件 + private String storeUploadedFile(MultipartFile file) { + if (file.isEmpty()) { + throw new RuntimeException("上传文件为空"); + } + try { + String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename(); + String prexfix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();//后缀名称 + if(!prexfix.contains("xlsx")||!prexfix.contains("xls")) { + throw new ApiException("必须为excel文件"); + } + Path filePath = Paths.get(uploadDir, fileName); + Files.createDirectories(filePath.getParent()); + Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); + return filePath.toString(); + } catch (IOException e) { + log.error("存储上传文件失败", e); + throw new RuntimeException("存储上传文件失败"); + } + } + + + // 设置Excel响应头 + private void setExcelResponseHeader(HttpServletResponse response, String fileName) { + try { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("文件名编码失败"); + } + } + + // 读取品牌Excel数据 + private List readBrandExcelData(String filePath) { + return EasyExcel.read(filePath) + .head(BrandModelExcel.class) + .sheet() + .doReadSync(); + } + // 读取商品分类Excel数据 + private List readCategoryExcelData(String filePath) { + return EasyExcel.read(filePath) + .head(SxCategoryModelExcel.class) + .sheet() + .doReadSync(); + } + + // 读取商品Excel数据 + private void readAndImportShopsExcelData(String filePath,String storeId) { + Map brandMaps = productBrandService.getBrandMapByStoreId(storeId); + QueryWrapper storeDbConfigQueryWrapper = new QueryWrapper<>(); + storeDbConfigQueryWrapper.eq("store_id", storeId); + StoreDbConfig storeDbConfig = storeDbConfigService.getOne(storeDbConfigQueryWrapper); + String isNegativeAllowed = storeDbConfig.getIsNegativeAllowed(); + ShopBatchSubmitListener shopBatchSubmitListener=new ShopBatchSubmitListener(syncThirdDataService); + shopBatchSubmitListener.setStoreId(storeId); + shopBatchSubmitListener.setBrandMaps(brandMaps); + shopBatchSubmitListener.setIsNegativeAllowed(isNegativeAllowed); + EasyExcel.read(filePath,SxGoosModelExcel.class,shopBatchSubmitListener).sheet().doRead(); + } +}