From 6b4372838538832b5f768ae4a46a2a249e564be0 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 28 Feb 2025 15:28:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E5=AF=BC?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/supplier/SmErpSupplierImportDto.java | 6 +- .../entity/SmErpSupplierEntity.java | 2 +- .../validarot/SupplierValidator.java | 22 ++ .../controller/SmErpSupplierController.java | 18 ++ .../service/SmErpSupplierService.java | 12 + .../impl/SmErpSupplierServiceImpl.java | 212 +++++++++++++++++- 6 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/SupplierValidator.java diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/supplier/SmErpSupplierImportDto.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/supplier/SmErpSupplierImportDto.java index a0fda3c23..3e539cd5b 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/supplier/SmErpSupplierImportDto.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/supplier/SmErpSupplierImportDto.java @@ -33,7 +33,7 @@ public class SmErpSupplierImportDto { @ExcelProperty("开户行及帐号") private String bankAccount; - @ExcelProperty("货商类别") + @ExcelProperty("货商类") private String supplierCategory; @ExcelProperty("货商地区") @@ -42,8 +42,8 @@ public class SmErpSupplierImportDto { @ExcelProperty("建档人") private String creator; - @ExcelProperty("状态") - private String status; +// @ExcelProperty("状态") +// private String status; @ExcelProperty("期初应付款") private Double initialPayable; diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpSupplierEntity.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpSupplierEntity.java index 20db6a875..1a81d0e56 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpSupplierEntity.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpSupplierEntity.java @@ -81,7 +81,7 @@ public class SmErpSupplierEntity extends Model { * 货商类别 */ @Schema(description="货商类别") - private String supplierType; + private Long supplierType; /** * 货商地区 diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/SupplierValidator.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/SupplierValidator.java new file mode 100644 index 000000000..6ebf15230 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/SupplierValidator.java @@ -0,0 +1,22 @@ +package com.bsd.pjcloud.supermarket.validarot; + + +import com.bsd.pjcloud.supermarket.dto.supplier.SmErpSupplierImportDto; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +public class SupplierValidator { + public static List validate(SmErpSupplierImportDto entity, int rowIndex) { + List errors = new ArrayList<>(); + + if (entity.getSupplierName() == null || entity.getSupplierName().isEmpty()) { + errors.add("第" + (rowIndex + 1) + "行: 货商名称不能为空"); + } +// if (entity.getPhone()== null || entity.getPhone().isEmpty()) { +// errors.add("第" + (rowIndex + 1) + "行: 电话不能为空"); +// } + return errors; + } +} diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSupplierController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSupplierController.java index 25dcd5c23..6b3f4b285 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSupplierController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSupplierController.java @@ -6,10 +6,14 @@ import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.admin.api.vo.UserExcelVO; import com.bsd.pjcloud.common.core.util.BeanUtils; import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.excel.annotation.RequestExcel; import com.bsd.pjcloud.common.log.annotation.SysLog; +import com.bsd.pjcloud.common.security.annotation.HasPermission; import com.bsd.pjcloud.common.security.util.SecurityUtils; +import com.bsd.pjcloud.supermarket.dto.supplier.SmErpSupplierImportDto; import com.bsd.pjcloud.supermarket.entity.SmErpSupplierEntity; import com.bsd.pjcloud.supermarket.service.SmErpSupplierService; import com.bsd.pjcloud.supermarket.vo.erp.SmErpSupplierSimpleVo; @@ -21,6 +25,7 @@ import org.springframework.http.HttpHeaders; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -135,4 +140,17 @@ public class SmErpSupplierController { List bean = BeanUtils.toBean(list, SmErpSupplierSimpleVo.class); return R.ok(bean); } + + /** + * 导入 + * + * @param smErpSupplierImportDtos 用户列表 + * @param bindingResult 错误信息列表 + * @return R + */ + @PostMapping("/import") +// @PreAuthorize("@pms.hasPermission('supermarketerp_smErpSupplier_import')" ) + public R importUser(@RequestExcel List smErpSupplierImportDtos, BindingResult bindingResult) { + return smErpSupplierService.importSupplier(smErpSupplierImportDtos, bindingResult); + } } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSupplierService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSupplierService.java index bf7ace929..4bbd505bc 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSupplierService.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSupplierService.java @@ -3,7 +3,10 @@ package com.bsd.pjcloud.supermarket.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.supermarket.dto.supplier.SmErpSupplierImportDto; import com.bsd.pjcloud.supermarket.entity.SmErpSupplierEntity; +import org.springframework.validation.BindingResult; import java.util.List; @@ -24,4 +27,13 @@ public interface SmErpSupplierService extends IService { * @return */ Object pageScope(Page page, LambdaQueryWrapper wrapper); + + + /** + * 导入 + * @param smErpSupplierImportDtos + * @param bindingResult + * @return + */ + R importSupplier(List smErpSupplierImportDtos, BindingResult bindingResult); } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSupplierServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSupplierServiceImpl.java index 0fc8be9fc..d204933c1 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSupplierServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSupplierServiceImpl.java @@ -1,15 +1,29 @@ package com.bsd.pjcloud.supermarket.service.impl; +import cn.hutool.core.lang.Assert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.core.util.R; import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.common.security.util.SecurityUtils; +import com.bsd.pjcloud.supermarket.dto.supplier.SmErpSupplierImportDto; import com.bsd.pjcloud.supermarket.entity.SmErpSupplierEntity; +import com.bsd.pjcloud.supermarket.entity.SmErpSupplierTypeEntity; import com.bsd.pjcloud.supermarket.mapper.SmErpSupplierMapper; +import com.bsd.pjcloud.supermarket.mapper.SmErpSupplierTypeMapper; import com.bsd.pjcloud.supermarket.service.SmErpSupplierService; +import com.bsd.pjcloud.supermarket.validarot.SupplierValidator; +import lombok.AllArgsConstructor; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.BindingResult; -import java.util.List; +import java.util.*; /** * 货商管理 @@ -18,7 +32,10 @@ import java.util.List; * @date 2024-12-20 12:07:42 */ @Service +@AllArgsConstructor public class SmErpSupplierServiceImpl extends ServiceImpl implements SmErpSupplierService { + + private final SmErpSupplierTypeMapper supplierTypeMapper; @Override public List selectSimpleAllScope() { return baseMapper.selectListByScope(new LambdaQueryWrapper().eq(SmErpSupplierEntity::getStatus, "0"), DataScope.of()); @@ -28,4 +45,197 @@ public class SmErpSupplierServiceImpl extends ServiceImpl wrapper) { return baseMapper.selectPageByScope(page, wrapper, DataScope.of()); } + + /* @Override + @Transactional + public R importSupplier(List smErpSupplierImportDtos, BindingResult bindingResult) { + if (smErpSupplierImportDtos.isEmpty()) { + return R.failed("导入内容为空"); + } + + List result = new ArrayList<>(); + for (int i = 0; i < smErpSupplierImportDtos.size(); i++) { + SmErpSupplierImportDto entity = smErpSupplierImportDtos.get(i); + List errors = SupplierValidator.validate(entity, i); + + if (!errors.isEmpty()) { + for (String error : errors) { + bindingResult.rejectValue("", "", error); + } + continue; + } + SmErpSupplierEntity supplier = BeanUtils.toBean(entity, SmErpSupplierEntity.class); + //查询货商 + List smErpSupplierEntities = baseMapper.selectListByScope(new LambdaQueryWrapper<>(SmErpSupplierEntity.class) + .eq(SmErpSupplierEntity::getSupplierName, supplier.getSupplierName()), DataScope.of()); + //查询货商类型 + String supplierCategory = entity.getSupplierCategory(); + List smErpSupplierTypeEntities = supplierTypeMapper.selectListByScope(new LambdaQueryWrapper<>(SmErpSupplierTypeEntity.class) + .eq(SmErpSupplierTypeEntity::getTypeName, supplierCategory), DataScope.of()); + Long supplierTypeId = determineSupplierTypeId(supplierCategory, smErpSupplierTypeEntities); + supplier.setSupplierId(supplierTypeId); + + //如果有当前名称,则更新 + if (CollectionUtils.isEmpty(smErpSupplierEntities)) { + boolean b = updateById(supplier); + if (b) { + result.add(R.ok("更新成功")); + } else { + result.add(R.failed("更新失败")); + } + } else { + boolean save = save(supplier); + if (save) { + result.add(R.ok("新增成功")); + } else { + result.add(R.ok("新增失败")); + } + + } + } + + return R.ok(result); + } + // 新增的公共方法 + private Long determineSupplierTypeId(String category, List typeEntities) { + if (CollectionUtils.isEmpty(typeEntities)) { + SmErpSupplierTypeEntity newType = new SmErpSupplierTypeEntity(); + newType.setTypeName(category); + supplierTypeMapper.insert(newType); + return newType.getId(); + } + // 显式断言确保集合非空 + Assert.notEmpty(typeEntities, "供应商类型查询结果不应为空"); + return typeEntities.get(0).getId(); + } +*/ + + @Override + @Transactional + public R importSupplier(List smErpSupplierImportDtos, BindingResult bindingResult) { + if (CollectionUtils.isEmpty(smErpSupplierImportDtos)) { + return R.failed("导入数据不能为空"); + } + + List> errorList = new ArrayList<>(); + List successList = new ArrayList<>(); + + for (int i = 0; i < smErpSupplierImportDtos.size(); i++) { + int rowNumber = i + 1; + SmErpSupplierImportDto dto = smErpSupplierImportDtos.get(i); + List errorReasons = new ArrayList<>(); + + // 数据校验阶段 + List validationErrors = SupplierValidator.validate(dto, rowNumber); + if (!validationErrors.isEmpty()) { + errorList.add(createErrorInfo(rowNumber, dto.getSupplierName(), validationErrors)); + continue; + } + + try { + // 处理供应商类型 + SmErpSupplierTypeEntity typeEntity = resolveSupplierType(dto.getSupplierCategory()); + if (typeEntity == null) { + errorReasons.add("供应商类型不存在且创建失败"); + errorList.add(createErrorInfo(rowNumber, dto.getSupplierName(), errorReasons)); + continue; + } + + // 转换实体 + SmErpSupplierEntity supplier = BeanUtils.toBean(dto, SmErpSupplierEntity.class); + supplier.setDeptId(SecurityUtils.getUser().getDeptId()); + supplier.setSupplierType(typeEntity.getId()); + + // 根据名称检查存在性(需确保supplier_name有唯一约束) + SmErpSupplierEntity existing = getExistingSupplier(supplier.getSupplierName()); + + boolean operationResult; + if (existing != null) { + // 更新逻辑:保持原有ID + supplier.setSupplierId(existing.getSupplierId()); + operationResult = updateById(supplier); + } else { + // 新增逻辑 + operationResult = save(supplier); + } + + if (operationResult) { + successList.add(supplier); + } else { + errorReasons.add(existing != null ? "更新失败" : "新增失败"); + errorList.add(createErrorInfo(rowNumber, dto.getSupplierName(), errorReasons)); + } + } catch (DuplicateKeyException e) { + errorReasons.add("供应商名称已存在"); + errorList.add(createErrorInfo(rowNumber, dto.getSupplierName(), errorReasons)); + } catch (Exception e) { + errorReasons.add("系统异常: " + e.getMessage()); + errorList.add(createErrorInfo(rowNumber, dto.getSupplierName(), errorReasons)); + } + } + + return buildFinalResult(smErpSupplierImportDtos.size(), successList.size(), errorList); + } + + // 辅助方法:获取已存在的供应商 + private SmErpSupplierEntity getExistingSupplier(String supplierName) { + List list = baseMapper.selectListByScope( + new LambdaQueryWrapper() + .eq(SmErpSupplierEntity::getSupplierName, supplierName) + .last("LIMIT 1"), + DataScope.of() + ); + return CollectionUtils.isEmpty(list) ? null : list.get(0); + } + + // 构建最终返回结果 + private R buildFinalResult(int total, int successCount, List> errorList) { + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("successCount", successCount); + result.put("errorCount", errorList.size()); + + if (!errorList.isEmpty()) { + result.put("errors", errorList); + return R.failed( result,"部分数据导入失败"); + } + return R.ok( result,"成功导入所有" + successCount + "条数据"); + } + + // 类型解析方法(带事务传播调整) + @Transactional(propagation = Propagation.REQUIRES_NEW) // 新事务避免主事务回滚影响 + public SmErpSupplierTypeEntity resolveSupplierType(String typeName) { + // 先尝试查询 + SmErpSupplierTypeEntity type = supplierTypeMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpSupplierTypeEntity::getTypeName, typeName) + ); + + if (type != null) return type; + + // 不存在则尝试插入 + try { + SmErpSupplierTypeEntity newType = new SmErpSupplierTypeEntity(); + newType.setTypeName(typeName); + if (supplierTypeMapper.insert(newType) > 0) { + return newType; + } + } catch (DuplicateKeyException e) { + // 并发情况下其他事务已创建 + return supplierTypeMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpSupplierTypeEntity::getTypeName, typeName) + ); + } + return null; + } + + // 错误信息构建方法 + private Map createErrorInfo(int row, String name, List reasons) { + return new HashMap() {{ + put("row", row); + put("supplierName", name); + put("reasons", reasons); + }}; + } } \ No newline at end of file From a4689b8373db7eae7f760c678e95758c4c37951e Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 28 Feb 2025 20:20:16 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E5=AF=BC?= =?UTF-8?q?=E5=85=A5-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/erpspu/SmErpSpuImportDto.java | 71 ++ .../entity/SmErpCategoryEntity.java | 1 + .../pjcloud/supermarket/task/AsyncConfig.java | 21 + .../pjcloud/supermarket/task/ImportTask.java | 20 + .../supermarket/task/TaskStoreService.java | 36 + .../validarot/ErpSpuValidator.java | 45 ++ .../controller/SmErpSpuController.java | 43 +- .../supermarket/service/SmErpSpuService.java | 17 + .../service/impl/SmErpSpuServiceImpl.java | 671 +++++++++++++++++- 9 files changed, 915 insertions(+), 10 deletions(-) create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/erpspu/SmErpSpuImportDto.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/AsyncConfig.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/ImportTask.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/TaskStoreService.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/ErpSpuValidator.java diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/erpspu/SmErpSpuImportDto.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/erpspu/SmErpSpuImportDto.java new file mode 100644 index 000000000..27cdccbd9 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/erpspu/SmErpSpuImportDto.java @@ -0,0 +1,71 @@ +package com.bsd.pjcloud.supermarket.dto.erpspu; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +@Data +public class SmErpSpuImportDto { + @ExcelProperty("货号") + private String productCode; // 货号 + + @ExcelProperty("自编码/条码") + private String barCode; // 自编码/条码 + + @ExcelProperty("商品名称") + private String productName; // 商品名称 + + @ExcelProperty("单位") + private String unit; // 单位 + + @ExcelProperty("产地") + private String origin; // 产地 + + @ExcelProperty("规格") + private String spec; // 规格 + + @ExcelProperty("库存") + private Integer stock; // 库存 + + @ExcelProperty("品牌") + private String brand; // 品牌 + + @ExcelProperty("分类") + private String classification; // 分类 + + @ExcelProperty("装数") + private Integer packageNum; // 装数 + + @ExcelProperty("进价") + private Integer purchasePrice; // 进价 + + @ExcelProperty("零售价") + private Integer retailPrice; // 零售价 + + @ExcelProperty("货商") + private String supplier; // 货商 + + @ExcelProperty("促销价") + private Double promotionPrice; // 促销价 + + @ExcelProperty("商品性质") + private String commodityNature; // 商品性质 + + @ExcelProperty("成本单价") + private Integer costPrice; // 成本单价 + + @ExcelProperty("库存上限") + private int inventoryUpperLimit; // 库存上限 + + @ExcelProperty("库存下限") + private int inventoryLowerLimit; // 库存下限 + + @ExcelProperty("毛利率%") + private Double grossProfitRate; // 毛利率% + + @ExcelProperty("辅助条码") + private String auxiliaryBarcode; // 辅助条码 + + @ExcelProperty("停销") + private Boolean discontinued; // 停销 + +} diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpCategoryEntity.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpCategoryEntity.java index 1c76e1df3..9dd58d4c2 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpCategoryEntity.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmErpCategoryEntity.java @@ -3,6 +3,7 @@ package com.bsd.pjcloud.supermarket.entity; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.extension.activerecord.Model; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import com.bsd.pjcloud.common.core.util.TenantTable; diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/AsyncConfig.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/AsyncConfig.java new file mode 100644 index 000000000..555a8251f --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/AsyncConfig.java @@ -0,0 +1,21 @@ +package com.bsd.pjcloud.supermarket.task; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +public class AsyncConfig { + @Bean("importExecutor") + public Executor asyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(4); + executor.setMaxPoolSize(8); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("import-"); + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/ImportTask.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/ImportTask.java new file mode 100644 index 000000000..e295d63d9 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/ImportTask.java @@ -0,0 +1,20 @@ +package com.bsd.pjcloud.supermarket.task; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Data +public class ImportTask { + private String taskId; + private String status; // PENDING, PROCESSING, COMPLETED, FAILED + private LocalDateTime createTime; + private LocalDateTime finishTime; + private int total; + private int success; + private int error; + private List> errors = new ArrayList<>(); +} diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/TaskStoreService.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/TaskStoreService.java new file mode 100644 index 000000000..669ed56ef --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/task/TaskStoreService.java @@ -0,0 +1,36 @@ +package com.bsd.pjcloud.supermarket.task; + +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +@Service +public class TaskStoreService { + private final Map tasks = new ConcurrentHashMap<>(); + + public String createTask(int total) { + String taskId = "TASK-" + UUID.randomUUID().toString().substring(0, 8); + ImportTask task = new ImportTask(); + task.setTaskId(taskId); + task.setStatus("PENDING"); + task.setCreateTime(LocalDateTime.now()); + task.setTotal(total); + tasks.put(taskId, task); + return taskId; + } + + public void updateTask(String taskId, Consumer updater) { + ImportTask task = tasks.get(taskId); + if (task != null) { + updater.accept(task); + } + } + + public ImportTask getTask(String taskId) { + return tasks.get(taskId); + } +} \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/ErpSpuValidator.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/ErpSpuValidator.java new file mode 100644 index 000000000..1fe62ae27 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/validarot/ErpSpuValidator.java @@ -0,0 +1,45 @@ +package com.bsd.pjcloud.supermarket.validarot; + + +import com.alibaba.excel.util.StringUtils; +import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto; +import com.bsd.pjcloud.supermarket.dto.supplier.SmErpSupplierImportDto; + +import java.util.ArrayList; +import java.util.List; + +public class ErpSpuValidator { + public static List validate(SmErpSpuImportDto entity, int rowIndex) { + List errors = new ArrayList<>(); + + if (StringUtils.isEmpty(entity.getProductCode())) { + errors.add("第" + (rowIndex + 1) + "行: 货号不能为空"); + } + if (StringUtils.isEmpty(entity.getProductName())) { + errors.add("第" + (rowIndex + 1) + "行: 商品名称不能为空"); + } + if (StringUtils.isEmpty(entity.getUnit())) { + errors.add("第" + (rowIndex + 1) + "行: 单位不能为空"); + } + if (StringUtils.isEmpty(entity.getSpec())) { + errors.add("第" + (rowIndex + 1) + "行: 规格不能为空"); + } + if (StringUtils.isEmpty(entity.getClassification())) { + errors.add("第" + (rowIndex + 1) + "行: 分类不能为空"); + } + if (StringUtils.isEmpty(entity.getCommodityNature())) { + errors.add("第" + (rowIndex + 1) + "行: 商品性质不能为空"); + } + if (StringUtils.isEmpty(entity.getSupplier())) { + errors.add("第" + (rowIndex + 1) + "行: 货商不能为空"); + } + if (null==entity.getPurchasePrice()) { + errors.add("第" + (rowIndex + 1) + "行: 进价不能为空"); + } + if (null==entity.getRetailPrice()) { + errors.add("第" + (rowIndex + 1) + "行: 零售价不能为空"); + } + + return errors; + } +} diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java index b46d5a084..6a021456d 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java @@ -1,31 +1,30 @@ package com.bsd.pjcloud.supermarket.controller; -import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.bsd.pjcloud.common.core.util.BeanUtils; import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.excel.annotation.RequestExcel; import com.bsd.pjcloud.common.log.annotation.SysLog; import com.bsd.pjcloud.common.security.util.SecurityUtils; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuAppQueryDTO; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuQueryDTO; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuUpdateStockReqDTO; +import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto; import com.bsd.pjcloud.supermarket.entity.SmErpAuxiliaryCodeEntity; import com.bsd.pjcloud.supermarket.entity.SmErpSpuEntity; import com.bsd.pjcloud.supermarket.entity.SmErpStockEntity; import com.bsd.pjcloud.supermarket.entity.SmErpUnitEntity; import com.bsd.pjcloud.supermarket.service.*; -import com.bsd.pjcloud.supermarket.vo.app.SupermarketAppSpuVo; +import com.bsd.pjcloud.supermarket.task.TaskStoreService; import com.bsd.pjcloud.supermarket.vo.erpspu.SmErpSpuSimpleVo; -import com.bsd.pjcloud.supermarket.vo.stockwarn.StockWarnVO; -import com.github.yulichang.toolkit.JoinWrappers; -import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.security.access.prepost.PreAuthorize; import com.bsd.pjcloud.common.excel.annotation.ResponseExcel; import io.swagger.v3.oas.annotations.security.SecurityRequirement; @@ -35,9 +34,14 @@ import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; /** * 商品spu @@ -62,6 +66,12 @@ public class SmErpSpuController { private final SmErpStockService smErpStockService; + + private final TaskStoreService taskStoreService; + + + + /** * 分页查询 * @param page 分页对象 @@ -211,6 +221,27 @@ public class SmErpSpuController { } + /** + * 导入 + * + * @param + * @param bindingResult 错误信息列表 + * @return R + */ + @PostMapping("/import") + @PreAuthorize("@pms.hasPermission('supermarket_supermarketSpu_export')" ) + public R importErpSpu(@RequestExcel List dtos, BindingResult bindingResult) { + List collect = dtos.stream().filter(smErpSpuImportDto -> smErpSpuImportDto.getProductCode() == null).collect(Collectors.toList()); +// String taskId = taskStoreService.createTask(dtos.size()); +// smErpSpuService.asyncExecuteImport(taskId, collect); +// return R.ok(Collections.singletonMap("taskId", taskId)); + System.out.println(collect); + List collect1 = dtos.stream().filter(smErpSpuImportDto -> StringUtils.isBlank(smErpSpuImportDto.getClassification())).collect(Collectors.toList()); + System.out.println(collect1); + return smErpSpuService.importErpSpu(dtos, bindingResult); + } + + // --------APP----------------- diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSpuService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSpuService.java index 286c7c893..fc1a83c5f 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSpuService.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpSpuService.java @@ -7,9 +7,11 @@ import com.bsd.pjcloud.common.core.util.R; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuQueryDTO; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuUpdateStockReqDTO; import com.bsd.pjcloud.supermarket.dto.cgplan.ErpCgPlanDTO; +import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto; import com.bsd.pjcloud.supermarket.entity.SmErpSpuEntity; import com.bsd.pjcloud.supermarket.vo.cgplan.ErpSpuWithUnitPageVo; import com.bsd.pjcloud.supermarket.vo.sm.smspu.SmErpPageScopeVo; +import org.springframework.validation.BindingResult; import java.util.ArrayList; import java.util.List; @@ -85,4 +87,19 @@ public interface SmErpSpuService extends IService { * @return */ List ListScope(LambdaQueryWrapper wrapper); + + + /** + * 导入 + * @param smErpSupplierImportDtos + * @param bindingResult + * @return + */ + R importErpSpu(List smErpSupplierImportDtos, BindingResult bindingResult); + + + + + + void asyncExecuteImport(String taskId, List dtos); } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSpuServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSpuServiceImpl.java index 1a7bd5fb3..9fe569873 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSpuServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpSpuServiceImpl.java @@ -13,15 +13,18 @@ import com.bsd.pjcloud.supermarket.constant.OrderTypeEnum; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuQueryDTO; import com.bsd.pjcloud.supermarket.dto.SupermarketSpuUpdateStockReqDTO; import com.bsd.pjcloud.supermarket.dto.cgplan.ErpCgPlanDTO; +import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto; import com.bsd.pjcloud.supermarket.dto.inventoryrecords.UpdateStockDTO; +import com.bsd.pjcloud.supermarket.dto.supplier.SmErpSupplierImportDto; import com.bsd.pjcloud.supermarket.entity.*; +import com.bsd.pjcloud.supermarket.mapper.SmErpCategoryMapper; import com.bsd.pjcloud.supermarket.mapper.SmErpSpuMapper; import com.bsd.pjcloud.supermarket.mapper.SmErpStockMapper; import com.bsd.pjcloud.supermarket.mapper.SmSpuMapper; -import com.bsd.pjcloud.supermarket.service.SmErpAuxiliaryCodeService; -import com.bsd.pjcloud.supermarket.service.SmErpSpuService; -import com.bsd.pjcloud.supermarket.service.SmErpStockService; -import com.bsd.pjcloud.supermarket.service.SmErpUnitService; +import com.bsd.pjcloud.supermarket.service.*; +import com.bsd.pjcloud.supermarket.task.TaskStoreService; +import com.bsd.pjcloud.supermarket.validarot.ErpSpuValidator; +import com.bsd.pjcloud.supermarket.validarot.SupplierValidator; import com.bsd.pjcloud.supermarket.vo.app.SupermarketAppSpuDetailsVo; import com.bsd.pjcloud.supermarket.vo.app.SupermarketAppUnitVo; import com.bsd.pjcloud.supermarket.vo.erp.SmErpAuxiliaryCodeVo; @@ -34,11 +37,17 @@ import lombok.AllArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.BindingResult; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** @@ -61,9 +70,21 @@ public class SmErpSpuServiceImpl extends ServiceImpl smErpSpuEntities = baseMapper.selectListByScope(wrapper, DataScope.of()); return smErpSpuEntities; } + + /* @Override + @Transactional + public R importErpSpu(List smErpSpuImportDtos, BindingResult bindingResult) { + if (CollectionUtils.isEmpty(smErpSpuImportDtos)) { + return R.failed("导入数据不能为空"); + } + List> errorList = new ArrayList<>(); + List successList = new ArrayList<>(); + for (int i = 0; i < smErpSpuImportDtos.size(); i++) { + int rowNumber = i + 1; + SmErpSpuImportDto dto = smErpSpuImportDtos.get(i); + List errorReasons = new ArrayList<>(); + // 数据校验阶段 + List validationErrors = ErpSpuValidator.validate(dto, rowNumber); + String productCode = dto.getProductCode(); + if (!validationErrors.isEmpty()) { + errorList.add(createErrorInfo(rowNumber, productCode, validationErrors)); + continue; + } + //新增商品 + //1.先查询当前货号是否存在,存在即更新,不存在即新增 + List smErpSpuEntities = baseMapper.selectListByScope(new LambdaQueryWrapper(). + eq(SmErpSpuEntity::getProductCode, productCode), DataScope.of()); + if (CollectionUtils.isEmpty(smErpSpuEntities)) { + //新增 + SmErpSpuEntity smErpSpuEntity = BeanUtils.toBean(dto, SmErpSpuEntity.class); + if (dto.getCommodityNature().equals("电子称称重商品")){ + smErpSpuEntity.setProductNature("2"); + }else { + smErpSpuEntity.setProductNature("1"); + } + //先查询分类是否有,没有自动新增 + SmErpCategoryEntity smErpCategoryEntity = smErpCategoryMapper.selectOne + (new LambdaQueryWrapper().eq(SmErpCategoryEntity::getName, dto.getClassification())); + if (smErpCategoryEntity == null) { + //新增分类 + SmErpCategoryEntity smErpCategoryEntity1 = new SmErpCategoryEntity(); + smErpCategoryEntity1.setName(dto.getClassification()); + smErpCategoryEntity1.setStatus("0"); + smErpCategoryMapper.insert(smErpCategoryEntity1); + smErpSpuEntity.setCategoryId(smErpCategoryEntity1.getId()); + }else { + smErpSpuEntity.setCategoryId(smErpCategoryEntity.getId()); + } + baseMapper.insert(smErpSpuEntity); + //新增包装单位 + SmErpUnitEntity smErpUnitEntity = BeanUtils.toBean(dto, SmErpUnitEntity.class); + smErpUnitEntity.setIsBase(true); + smErpUnitEntity.setSpuId(smErpSpuEntity.getId()); + smErpUnitService.save(smErpUnitEntity); + //新增库存 + SmErpStockEntity smErpStockEntity = new SmErpStockEntity(); + smErpStockEntity.setCurrentStock(dto.getStock()); + smErpStockEntity.setBaseStock(dto.getStock()); + smErpStockEntity.setInventoryLowerLimit(dto.getInventoryLowerLimit()); + smErpStockEntity.setInventoryUpperLimit(dto.getInventoryUpperLimit()); + smErpStockEntity.setErpSpuId(smErpSpuEntity.getId()); + smErpStockMapper.insert(smErpStockEntity); + //新增辅助条码 + SmErpAuxiliaryCodeEntity smErpAuxiliaryCodeEntity = new SmErpAuxiliaryCodeEntity(); + smErpAuxiliaryCodeEntity.setBarCode(dto.getBarCode()); + smErpAuxiliaryCodeEntity.setSpuId(smErpSpuEntity.getId()); + smErpAuxiliaryCodeEntity.setUnitId(smErpUnitEntity.getId()); + smErpAuxiliaryCodeService.save(smErpAuxiliaryCodeEntity); + }else { + //查到相同的货号,更新 + + } + + + } + return null; + }*/ +/* + @Override + @Transactional + public R importErpSpu(List smErpSpuImportDtos, BindingResult bindingResult) { + if (CollectionUtils.isEmpty(smErpSpuImportDtos)) { + return R.failed("导入数据不能为空"); + } + + List> errorList = new ArrayList<>(); + List successList = new ArrayList<>(); + + for (int i = 0; i < smErpSpuImportDtos.size(); i++) { + int rowNumber = i + 1; + SmErpSpuImportDto dto = smErpSpuImportDtos.get(i); + List errorReasons = new ArrayList<>(); + String productCode = dto.getProductCode(); + + try { + // 数据校验 + List validationErrors = ErpSpuValidator.validate(dto, rowNumber); + if (!validationErrors.isEmpty()) { + errorList.add(createErrorInfo(rowNumber, productCode, validationErrors)); + continue; + } + + // 解析商品分类 + Long categoryId = resolveCategory(dto.getClassification(), errorReasons); + if (categoryId == null) { + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + continue; + } + + // 查询现有商品 + SmErpSpuEntity existingSpu = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SmErpSpuEntity::getProductCode, productCode)); + + if (existingSpu == null) { + // 新增逻辑 + handleNewSpu(dto, categoryId, errorReasons, rowNumber, productCode, errorList); + } else { + // 更新逻辑 + handleExistingSpu(existingSpu, dto, categoryId, errorReasons, rowNumber, productCode, errorList); + } + + // 记录成功数据 + if (errorReasons.isEmpty()) { + successList.add(baseMapper.selectById(existingSpu != null ? existingSpu.getId() : null)); + } + } catch (Exception e) { + errorReasons.add("系统异常: " + e.getMessage()); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + } + } + + return buildImportResult(smErpSpuImportDtos.size(), successList.size(), errorList); + } + + + + // 处理分类(公共方法) + private Long resolveCategory(String categoryName, List errorReasons) { + try { + SmErpCategoryEntity category = smErpCategoryMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpCategoryEntity::getName, categoryName) + ); + + if (category != null) return category.getId(); + + // 新建分类 + SmErpCategoryEntity newCategory = new SmErpCategoryEntity(); + newCategory.setName(categoryName); + newCategory.setStatus("0"); // 假设0为启用状态 + if (smErpCategoryMapper.insert(newCategory) > 0) { + return newCategory.getId(); + } + errorReasons.add("分类创建失败"); + } catch (DuplicateKeyException e) { + // 处理并发创建场景 + return smErpCategoryMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpCategoryEntity::getName, categoryName) + ).getId(); + } + return null; + } + + // 处理新增商品 + private void handleNewSpu(SmErpSpuImportDto dto, Long categoryId, List errorReasons, + int rowNumber, String productCode, List> errorList) { + try { + SmErpSpuEntity newSpu = BeanUtils.toBean(dto, SmErpSpuEntity.class); + newSpu.setProductNature(dto.getCommodityNature().equals("电子称称重商品") ? "2" : "1"); + newSpu.setCategoryId(categoryId); + + if (baseMapper.insert(newSpu) <= 0) { + errorReasons.add("商品创建失败"); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + return; + } + + // 处理关联数据 + handleRelatedData(newSpu, dto, errorReasons, rowNumber, productCode, errorList); + } catch (DuplicateKeyException e) { + errorReasons.add("货号已存在"); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + } + } + + // 处理更新商品 + private void handleExistingSpu(SmErpSpuEntity existingSpu, SmErpSpuImportDto dto, Long categoryId, + List errorReasons, int rowNumber, String productCode, + List> errorList) { + try { + // 更新主数据 + org.springframework.beans.BeanUtils.copyProperties(dto, existingSpu); + existingSpu.setProductNature(dto.getCommodityNature().equals("电子称称重商品") ? "2" : "1"); + existingSpu.setCategoryId(categoryId); + + if (baseMapper.updateById(existingSpu) <= 0) { + errorReasons.add("商品更新失败"); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + return; + } + + // 处理关联数据 + handleRelatedData(existingSpu, dto, errorReasons, rowNumber, productCode, errorList); + } catch (Exception e) { + errorReasons.add("更新异常: " + e.getMessage()); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + } + } + + // 处理关联数据(单位、库存、条码) + private void handleRelatedData(SmErpSpuEntity spu, SmErpSpuImportDto dto, List errorReasons, + int rowNumber, String productCode, List> errorList) { + // 处理包装单位 + SmErpUnitEntity unit = smErpUnitService.getOne( + new LambdaQueryWrapper() + .eq(SmErpUnitEntity::getSpuId, spu.getId()) + ); + + try { + if (unit != null) { + // 更新单位 + org.springframework.beans.BeanUtils.copyProperties(dto, unit); + smErpUnitService.updateById(unit); + } else { + // 新增单位 + unit = BeanUtils.toBean(dto, SmErpUnitEntity.class); + unit.setSpuId(spu.getId()); + unit.setIsBase(true); + smErpUnitService.save(unit); + } + } catch (Exception e) { + errorReasons.add("包装单位处理失败: " + e.getMessage()); + } + + // 处理库存 + SmErpStockEntity stock = smErpStockMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpStockEntity::getErpSpuId, spu.getId()) + ); + + try { + if (stock != null) { + // 更新库存 + stock.setCurrentStock(dto.getStock()); + stock.setBaseStock(dto.getStock()); + stock.setInventoryLowerLimit(dto.getInventoryLowerLimit()); + stock.setInventoryUpperLimit(dto.getInventoryUpperLimit()); + smErpStockMapper.updateById(stock); + } else { + // 新增库存 + stock = BeanUtils.toBean(dto, SmErpStockEntity.class); + stock.setErpSpuId(spu.getId()); + smErpStockMapper.insert(stock); + } + } catch (Exception e) { + errorReasons.add("库存处理失败: " + e.getMessage()); + } + + // 处理辅助条码 + SmErpAuxiliaryCodeEntity code = smErpAuxiliaryCodeService.getOne( + new LambdaQueryWrapper() + .eq(SmErpAuxiliaryCodeEntity::getSpuId, spu.getId()) + ); + + try { + if (code != null) { + // 更新条码 + code.setBarCode(dto.getBarCode()); + code.setUnitId(unit.getId()); + smErpAuxiliaryCodeService.updateById(code); + } else { + // 新增条码 + code = new SmErpAuxiliaryCodeEntity(); + code.setBarCode(dto.getBarCode()); + code.setSpuId(spu.getId()); + code.setUnitId(unit.getId()); + smErpAuxiliaryCodeService.save(code); + } + } catch (Exception e) { + errorReasons.add("条码处理失败: " + e.getMessage()); + } + + // 记录错误 + if (!errorReasons.isEmpty()) { + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + } + } + + + // 构建返回结果(与供应商导入保持一致) + private R buildImportResult(int total, int successCount, List> errorList) { + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("successCount", successCount); + result.put("errorCount", errorList.size()); + + if (!errorList.isEmpty()) { + result.put("errors", errorList); + return R.failed(result,"部分数据导入失败"); + } + return R.ok( result,"成功导入所有" + successCount + "条数据"); + } + + // 错误信息构建方法 + private Map createErrorInfo(int row, String name, List reasons) { + return new HashMap() {{ + put("row", row); + put("supplierName", name); + put("reasons", reasons); + }}; + } +*/ + + private R buildImportResult(int total, int successCount, List> errorList) { + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("successCount", successCount); + result.put("errorCount", errorList.size()); + + if (!errorList.isEmpty()) { + result.put("errors", errorList); + return R.failed(result,"部分数据导入失败"); + } + return R.ok( result,"成功导入所有" + successCount + "条数据"); + } + // 错误信息构建方法 + private Map createErrorInfo(int row, String name, List reasons) { + return new HashMap() {{ + put("row", row); + put("supplierName", name); + put("reasons", reasons); + }}; + } + @Override + @Transactional(rollbackFor = Exception.class) + public R importErpSpu(List smErpSpuImportDtos, BindingResult bindingResult) { + if (CollectionUtils.isEmpty(smErpSpuImportDtos)) { + return R.failed("导入数据不能为空"); + } + + List> errorList = new ArrayList<>(); + List successList = new ArrayList<>(); + + // 批量处理分类 + Map categoryMap = resolveCategories(smErpSpuImportDtos, errorList); + + // 批量处理商品 + List spuBatch = new ArrayList<>(); + List unitBatch = new ArrayList<>(); + List stockBatch = new ArrayList<>(); + List codeBatch = new ArrayList<>(); + + for (int i = 0; i < smErpSpuImportDtos.size(); i++) { + int rowNumber = i + 1; + SmErpSpuImportDto dto = smErpSpuImportDtos.get(i); + List errorReasons = new ArrayList<>(); + String productCode = dto.getProductCode(); + + try { + // 数据校验 + List validationErrors = ErpSpuValidator.validate(dto, rowNumber); + if (!validationErrors.isEmpty()) { + errorList.add(createErrorInfo(rowNumber, productCode, validationErrors)); + continue; + } + + // 获取分类ID + Long categoryId = categoryMap.get(dto.getClassification()); + if (categoryId == null) { + errorReasons.add("分类不存在且创建失败"); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + continue; + } + + // 构造商品实体 + SmErpSpuEntity spu = BeanUtils.toBean(dto, SmErpSpuEntity.class); + spu.setProductNature("电子称称重商品".equals(dto.getCommodityNature()) ? "2" : "1"); + spu.setCategoryId(categoryId); + spuBatch.add(spu); + + // 构造关联数据 + buildRelatedData(dto, spu, unitBatch, stockBatch, codeBatch); + + successList.add(spu); + } catch (Exception e) { + errorReasons.add("系统异常: " + e.getMessage()); + errorList.add(createErrorInfo(rowNumber, productCode, errorReasons)); + } + } + + // 批量数据库操作 + try { + // 批量插入/更新商品 + if (!spuBatch.isEmpty()) { + saveOrUpdateBatchSpu(spuBatch); + } + + // 批量处理关联数据 + if (!unitBatch.isEmpty()) { + smErpUnitService.saveOrUpdateBatch(unitBatch); + } + if (!stockBatch.isEmpty()) { + smErpStockService.saveOrUpdateBatch(stockBatch); + } + if (!codeBatch.isEmpty()) { + smErpAuxiliaryCodeService.saveOrUpdateBatch(codeBatch); + } + } catch (Exception e) { + throw new RuntimeException("批量操作失败", e); + } + + return buildImportResult(smErpSpuImportDtos.size(), successList.size(), errorList); + } + + // 批量处理分类 + private Map resolveCategories(List dtos, List> errorList) { + // 获取所有分类名称 + Set categoryNames = dtos.stream() + .map(SmErpSpuImportDto::getClassification) + .collect(Collectors.toSet()); + + // 批量查询已有分类 + List existCategories = smErpCategoryMapper.selectList( + new LambdaQueryWrapper() + .in(SmErpCategoryEntity::getName, categoryNames)); + + Map categoryMap = existCategories.stream() + .collect(Collectors.toMap(SmErpCategoryEntity::getName, SmErpCategoryEntity::getId)); + + // 找出需要新增的分类 + List newCategories = categoryNames.stream() + .filter(name -> !categoryMap.containsKey(name)) + .map(name -> { + SmErpCategoryEntity category = new SmErpCategoryEntity(); + category.setName(name); + category.setStatus("0"); + return category; + }) + .collect(Collectors.toList()); + + // 批量插入新分类 + if (!newCategories.isEmpty()) { + smCategoryService.saveBatch(newCategories); + newCategories.forEach(cat -> categoryMap.put(cat.getName(), cat.getId())); + } + + return categoryMap; + } + + // 批量保存或更新商品 + private void saveOrUpdateBatchSpu(List spuList) { + // 按货号分组 + Map> spuMap = spuList.stream() + .collect(Collectors.groupingBy(SmErpSpuEntity::getProductCode)); + + // 批量查询已存在的商品 + List existSpus = baseMapper.selectList( + new LambdaQueryWrapper() + .in(SmErpSpuEntity::getProductCode, spuMap.keySet())); + + // 分离新增和更新数据 + List toInsert = new ArrayList<>(); + List toUpdate = new ArrayList<>(); + + spuList.forEach(spu -> { + Optional exist = existSpus.stream() + .filter(e -> e.getProductCode().equals(spu.getProductCode())) + .findFirst(); + + if (exist.isPresent()) { + SmErpSpuEntity entity = exist.get(); + org.springframework.beans.BeanUtils.copyProperties(spu, entity); + toUpdate.add(entity); + } else { + toInsert.add(spu); + } + }); + + // 批量操作 + if (!toInsert.isEmpty()) { + this.saveBatch((toInsert)); + } + if (!toUpdate.isEmpty()) { + this.updateBatchById(toUpdate); + } + } + + // 构建关联数据 + private void buildRelatedData(SmErpSpuImportDto dto, SmErpSpuEntity spu, + List unitBatch, + List stockBatch, + List codeBatch) { + // 包装单位 + SmErpUnitEntity unit = BeanUtils.toBean(dto, SmErpUnitEntity.class); + unit.setSpuId(spu.getId()); + unit.setIsBase(true); + unitBatch.add(unit); + + // 库存 + SmErpStockEntity stock = BeanUtils.toBean(dto, SmErpStockEntity.class); + stock.setErpSpuId(spu.getId()); + stockBatch.add(stock); + + // 辅助条码 + SmErpAuxiliaryCodeEntity code = new SmErpAuxiliaryCodeEntity(); + code.setBarCode(dto.getBarCode()); + code.setSpuId(spu.getId()); + code.setUnitId(unit.getId()); + codeBatch.add(code); + } + + public void asyncExecuteImport(String taskId, List dtos) { + executor.execute(() -> { + List> errors = new ArrayList<>(); + // 使用线程安全的计数器 + AtomicInteger successCount = new AtomicInteger(0); + + try { + taskStoreService.updateTask(taskId, t -> t.setStatus("PROCESSING")); + + for (int i = 0; i < dtos.size(); i++) { + try { + processSingleItem(dtos.get(i), i + 1); + successCount.incrementAndGet(); + } catch (Exception e) { + errors.add(buildErrorInfo(i + 1, dtos.get(i).getProductCode(), e)); + } + } + + taskStoreService.updateTask(taskId, t -> { + t.setStatus("COMPLETED"); + t.setSuccess(successCount.get()); + t.setError(errors.size()); + t.setErrors(errors); + t.setFinishTime(LocalDateTime.now()); + }); + } catch (Exception e) { + taskStoreService.updateTask(taskId, t -> { + t.setStatus("FAILED"); + t.setError(dtos.size()); + t.setErrors(Collections.singletonList( + buildErrorInfo(0, "SYSTEM", e) + )); + }); + } + }); + } + + private void processSingleItem(SmErpSpuImportDto dto, int row) throws Exception { + // 数据校验 + List validations = ErpSpuValidator.validate(dto, row); + if (!validations.isEmpty()) { + throw new RuntimeException(String.join(",", validations)); + } + + // 处理分类 + Long categoryId = resolveCategory(dto.getClassification()); + + // 处理商品 + SmErpSpuEntity spu = resolveSpu(dto, categoryId); + + // 处理关联数据 + handleRelatedData(spu, dto); + } + + private Long resolveCategory(String name) { + SmErpCategoryEntity category = smErpCategoryMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpCategoryEntity::getName, name)); + + if (category != null) return category.getId(); + + SmErpCategoryEntity newCategory = new SmErpCategoryEntity(); + newCategory.setName(name); + if (smErpCategoryMapper.insert(newCategory) > 0) { + return newCategory.getId(); + } + throw new RuntimeException("分类创建失败"); + } + + private SmErpSpuEntity resolveSpu(SmErpSpuImportDto dto, Long categoryId) { + SmErpSpuEntity existing = baseMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpSpuEntity::getProductCode, dto.getProductCode())); + + SmErpSpuEntity spu = BeanUtils.toBean(dto, SmErpSpuEntity.class); + spu.setProductNature("电子称称重商品".equals(dto.getCommodityNature()) ? "2" : "1"); + spu.setCategoryId(categoryId); + + if (existing != null) { + spu.setId(existing.getId()); + if (baseMapper.updateById(spu) <= 0) { + throw new RuntimeException("商品更新失败"); + } + } else { + if (baseMapper.insert(spu) <= 0) { + throw new RuntimeException("商品创建失败"); + } + } + return spu; + } + + private void handleRelatedData(SmErpSpuEntity spu, SmErpSpuImportDto dto) { + // 处理单位 + SmErpUnitEntity unit = Optional.ofNullable(smErpUnitService.getOne( + new LambdaQueryWrapper() + .eq(SmErpUnitEntity::getSpuId, spu.getId()))) + .orElseGet(() -> new SmErpUnitEntity()); + + org.springframework.beans.BeanUtils.copyProperties(dto, unit); + unit.setSpuId(spu.getId()); + unit.setIsBase(true); + smErpUnitService.saveOrUpdate(unit); + + // 处理库存 + SmErpStockEntity stock = Optional.ofNullable(smErpStockMapper.selectOne( + new LambdaQueryWrapper() + .eq(SmErpStockEntity::getErpSpuId, spu.getId()))) + .orElseGet(() -> new SmErpStockEntity()); + + org.springframework.beans.BeanUtils.copyProperties(dto, stock); + stock.setErpSpuId(spu.getId()); + smErpStockMapper.insertOrUpdate(stock); + + // 处理条码 + SmErpAuxiliaryCodeEntity code = Optional.ofNullable(smErpAuxiliaryCodeService.getOne( + new LambdaQueryWrapper() + .eq(SmErpAuxiliaryCodeEntity::getSpuId, spu.getId()))) + .orElseGet(() -> new SmErpAuxiliaryCodeEntity()); + + code.setBarCode(dto.getBarCode()); + code.setSpuId(spu.getId()); + code.setUnitId(unit.getId()); + smErpAuxiliaryCodeService.saveOrUpdate(code); + } + + private Map buildErrorInfo(int row, String code, Exception e) { + return new HashMap() {{ + put("row", row); + put("productCode", code); + put("reason", e.getMessage()); + }}; + } + } \ No newline at end of file