From ddc95002ef4c7593a006443690d5ae6bba693227 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期日, 13 九月 2026 11:09:56 +0800
Subject: [PATCH] fix: 产品导入限制
---
src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java | 315 +++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 284 insertions(+), 31 deletions(-)
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
index bc3840f..dd0e7ab 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
@@ -3,30 +3,39 @@
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.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.ProductDto;
+import com.ruoyi.basic.dto.ProductImportRowDto;
import com.ruoyi.basic.dto.ProductModelDto;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.service.IProductModelService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.common.utils.uuid.IdUtils;
+import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.technology.mapper.TechnologyBomMapper;
+import com.ruoyi.technology.pojo.TechnologyBom;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
/**
* 銆愯濉啓鍔熻兘鍚嶇О銆慡ervice涓氬姟灞傚鐞�
@@ -40,27 +49,42 @@
private final ProductMapper productMapper;
private final SalesLedgerProductMapper salesLedgerProductMapper;
+ private final TechnologyBomMapper technologyBomMapper;
private ProductModelMapper productModelMapper;
+
+ private static final List<String> IMPORT_SHEET_NAMES = Arrays.asList("鍘熸枡", "鍗婃垚鍝�", "鎴愬搧");
@Override
public int addOrEditProductModel(ProductModelDto productModelDto) {
+ String model = StringUtils.trim(productModelDto.getModel());
+ String productCode = StringUtils.trim(productModelDto.getProductCode());
+ productModelDto.setModel(model);
+ productModelDto.setProductCode(productCode);
+ checkModelAndProductCodeUnique(model, productCode, productModelDto.getId());
if (productModelDto.getId() == null) {
ProductModel productModel = new ProductModel();
- BeanUtils.copyProperties(productModelDto,productModel);
- productModelMapper.insert(productModel);
-
- String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
- String idStr = String.format("%06d", productModel.getId()); // 鍏綅琛�0
- String newProductCode = "BM" + dateStr + idStr;
- // 鏇存柊鏁版嵁搴撲腑鐨刾roductCode
- productModel.setProductCode(newProductCode);
- return productModelMapper.updateById(productModel);
+ BeanUtils.copyProperties(productModelDto, productModel);
+ return productModelMapper.insert(productModel);
} else {
return productModelMapper.updateById(productModelDto);
}
}
+ private void checkModelAndProductCodeUnique(String model, String productCode, Long currentId) {
+ if (StringUtils.isEmpty(model) || StringUtils.isEmpty(productCode)) {
+ return;
+ }
+ LambdaQueryWrapper<ProductModel> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(ProductModel::getModel, model)
+ .eq(ProductModel::getProductCode, productCode)
+ .ne(currentId != null, ProductModel::getId, currentId)
+ .last("limit 1");
+ ProductModel duplicateProductModel = productModelMapper.selectOne(queryWrapper);
+ if (duplicateProductModel != null) {
+ throw new ServiceException("瀵瑰簲鐨勫瀷鍙�" + model + "鐨勪骇鍝佺紪鐮�" + productCode + "宸茬粡瀛樺湪");
+ }
+ }
@Override
@@ -71,6 +95,14 @@
throw new RuntimeException("宸茬粡瀛樺湪璇ヤ骇鍝佺殑閿�鍞彴璐﹀拰閲囪喘鍙拌处");
}
+
+ // 鏄惁瀛樺湪BOM
+ List<TechnologyBom> technologyBoms = technologyBomMapper.selectList(new QueryWrapper<TechnologyBom>()
+ .lambda().in(TechnologyBom::getProductModelId, ids));
+ if (CollectionUtils.isNotEmpty(technologyBoms)) {
+ throw new RuntimeException("宸茬粡瀛樺湪璇ヤ骇鍝佺殑BOM鏁版嵁");
+ }
+
return productModelMapper.deleteBatchIds(Arrays.asList(ids));
}
@@ -83,6 +115,7 @@
/**
* 鏍规嵁id鏌ヨ浜у搧瑙勬牸鍒嗛〉鏌ヨ
+ *
* @param page
* @param productDto
* @return
@@ -95,24 +128,244 @@
}
@Override
- public Boolean importProduct(MultipartFile file) {
- try {
- ExcelUtil<ProductModel> productModelExcelUtil = new ExcelUtil<>(ProductModel.class);
- List<ProductModel> productModelList = productModelExcelUtil.importExcel(file.getInputStream());
- Map<String, List<ProductModel>> collect = productModelList.stream().collect(Collectors.groupingBy(ProductModel::getProductName));
- collect.forEach((k,v)->{
- Product product = productMapper.selectOne(new LambdaQueryWrapper<Product>().eq(Product::getProductName, k).last("LIMIT 1"));
- if (product != null) {
- v.forEach(productModel -> {
- productModel.setProductId(product.getId());
- });
- this.saveOrUpdateBatch(v);
- }
- });
- return true;
- }catch (Exception e) {
- e.printStackTrace();
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult importProductModel(MultipartFile file) {
+ if (file == null || file.isEmpty()) {
+ return AjaxResult.error("璇烽�夋嫨瑕佸鍏ョ殑鏂囦欢");
}
- return false;
+
+ Map<String, List<ProductImportRowDto>> sheetMap;
+ try {
+ ExcelUtil<ProductImportRowDto> excelUtil = new ExcelUtil<>(ProductImportRowDto.class);
+ sheetMap = excelUtil.importExcelMultiSheet(IMPORT_SHEET_NAMES, file.getInputStream(), 0);
+ } catch (Exception e) {
+ log.error("瑙f瀽浜у搧瀵煎叆鏂囦欢寮傚父", e);
+ throw new ServiceException("浜у搧瀵煎叆鏂囦欢瑙f瀽澶辫触");
+ }
+ if (sheetMap.isEmpty()) {
+ return AjaxResult.error("鏈壘鍒� [鍘熸枡/鍗婃垚鍝�/鎴愬搧] 宸ヤ綔琛紝鎴栨枃浠朵腑娌℃湁鏁版嵁");
+ }
+
+ // 鍘婚噸閿細浜у搧鍚嶇О + 瑙勬牸鍨嬪彿 + 缂栫爜锛堢┖缂栫爜鎸夌┖涓插弬涓庢瘮杈冿級
+ Set<String> seenKeys = loadExistingProductKeys(sheetMap);
+
+ int successCount = 0;
+ int skipCount = 0;
+ Set<String> generatedCodes = new HashSet<>();
+ for (String sheetName : IMPORT_SHEET_NAMES) {
+ List<ProductImportRowDto> rows = sheetMap.get(sheetName);
+ if (CollectionUtils.isEmpty(rows)) {
+ continue;
+ }
+ Long topId = findOrCreateTopProduct(sheetName);
+ Map<String, Long> nodeCache = new HashMap<>();
+ for (int i = 0; i < rows.size(); i++) {
+ ProductImportRowDto row = rows.get(i);
+ // 娌℃湁鍚嶇О/缂栫爜/瑙勬牸鐨勮鏋勪笉鎴愪骇鍝佹暟鎹紝鎸夌┖琛岃烦杩�
+ if (hasNoProductContent(row)) {
+ continue;
+ }
+ String parentName = trimToNull(row.getProductParentName());
+ String categoryName = trimToNull(row.getProductCategoryName());
+ String productName = trimToNull(row.getProductName());
+ if (productName == null) {
+ throw new ServiceException(String.format("宸ヤ綔琛ㄣ��%s銆戠 %d 琛� [浜у搧鍚嶇О] 涓嶈兘涓虹┖锛堣琛屽凡濉細%s锛�",
+ sheetName, resolveRowNum(row, i), describeRow(row)));
+ }
+
+ String model = trimToNull(row.getModel());
+ if (model == null) {
+ model = "/";
+ }
+ String code = trimToNull(row.getProductCode());
+ if (!seenKeys.add(dedupKey(productName, model, code))) {
+ skipCount++;
+ continue;
+ }
+
+ String path = String.valueOf(topId);
+ Long currentParentId = topId;
+ if (parentName != null) {
+ path = path + ">" + parentName;
+ currentParentId = ensureChildNode(nodeCache, path, currentParentId, parentName);
+ }
+ if (categoryName != null) {
+ path = path + ">" + categoryName;
+ currentParentId = ensureChildNode(nodeCache, path, currentParentId, categoryName);
+ }
+ path = path + ">" + productName;
+ Long leafId = ensureChildNode(nodeCache, path, currentParentId, productName);
+
+ if (code == null) {
+ code = generateUniqueCode(generatedCodes);
+ }
+
+ ProductModel productModel = new ProductModel();
+ productModel.setProductId(leafId);
+ productModel.setProductCode(code);
+ productModel.setModel(model);
+ productModel.setUnit(trimToNull(row.getUnit()));
+ productModelMapper.insert(productModel);
+ successCount++;
+ }
+ }
+
+ String message = skipCount > 0
+ ? String.format("鎴愬姛瀵煎叆 %d 鏉★紝璺宠繃宸插瓨鍦� %d 鏉�", successCount, skipCount)
+ : String.format("鎴愬姛瀵煎叆 %d 鏉�", successCount);
+ return AjaxResult.success(message);
+ }
+
+ private Long findOrCreateTopProduct(String name) {
+ Product root = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+ .isNull(Product::getParentId)
+ .eq(Product::getProductName, name)
+ .last("limit 1"));
+ if (root != null) {
+ return root.getId();
+ }
+ Product product = new Product();
+ product.setProductName(name);
+ productMapper.insert(product);
+ return product.getId();
+ }
+
+ private Long ensureChildNode(Map<String, Long> nodeCache, String path, Long parentId, String name) {
+ Long id = nodeCache.get(path);
+ if (id != null) {
+ return id;
+ }
+ Product existing = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+ .eq(Product::getParentId, parentId)
+ .eq(Product::getProductName, name)
+ .last("limit 1"));
+ if (existing != null) {
+ nodeCache.put(path, existing.getId());
+ return existing.getId();
+ }
+ Product product = new Product();
+ product.setParentId(parentId);
+ product.setProductName(name);
+ productMapper.insert(product);
+ nodeCache.put(path, product.getId());
+ return product.getId();
+ }
+
+ /**
+ * 棰勫姞杞藉簱涓凡瀛樺湪鐨勫幓閲嶉敭锛堜骇鍝佸悕绉� + 瑙勬牸鍨嬪彿 + 缂栫爜锛夛紝骞朵綔涓烘湰娆″鍏ョ殑鍒濆闆嗗悎銆�
+ */
+ private Set<String> loadExistingProductKeys(Map<String, List<ProductImportRowDto>> sheetMap) {
+ Set<String> productNames = new HashSet<>();
+ for (List<ProductImportRowDto> rows : sheetMap.values()) {
+ if (CollectionUtils.isEmpty(rows)) {
+ continue;
+ }
+ for (ProductImportRowDto row : rows) {
+ String productName = trimToNull(row.getProductName());
+ if (productName != null) {
+ productNames.add(productName);
+ }
+ }
+ }
+ Set<String> keys = new HashSet<>();
+ if (productNames.isEmpty()) {
+ return keys;
+ }
+
+ List<Product> products = productMapper.selectList(new LambdaQueryWrapper<Product>()
+ .in(Product::getProductName, productNames));
+ if (CollectionUtils.isEmpty(products)) {
+ return keys;
+ }
+ Map<Long, String> nameById = new HashMap<>();
+ List<Long> productIds = new ArrayList<>();
+ for (Product product : products) {
+ nameById.put(product.getId(), product.getProductName());
+ productIds.add(product.getId());
+ }
+
+ List<ProductModel> productModels = productModelMapper.selectList(new LambdaQueryWrapper<ProductModel>()
+ .in(ProductModel::getProductId, productIds));
+ for (ProductModel productModel : productModels) {
+ String productName = nameById.get(productModel.getProductId());
+ if (productName != null) {
+ keys.add(dedupKey(productName, productModel.getModel(), productModel.getProductCode()));
+ }
+ }
+ return keys;
+ }
+
+ /**
+ * 瑙f瀽鏃朵細璺宠繃绌鸿
+ */
+ private static int resolveRowNum(ProductImportRowDto row, int index) {
+ return row.getExcelRowNum() != null ? row.getExcelRowNum() : index + 2;
+ }
+
+ private static boolean hasNoProductContent(ProductImportRowDto row) {
+ return trimToNull(row.getProductName()) == null
+ && trimToNull(row.getProductCode()) == null
+ && trimToNull(row.getModel()) == null;
+ }
+
+ private static String describeRow(ProductImportRowDto row) {
+ StringBuilder description = new StringBuilder();
+ appendFilledColumn(description, "浜у搧鐖剁被", row.getProductParentName());
+ appendFilledColumn(description, "浜у搧澶х被", row.getProductCategoryName());
+ appendFilledColumn(description, "浜у搧鍚嶇О", row.getProductName());
+ appendFilledColumn(description, "浜у搧缂栫爜", row.getProductCode());
+ appendFilledColumn(description, "瑙勬牸鍨嬪彿", row.getModel());
+ appendFilledColumn(description, "鍗曚綅", row.getUnit());
+ return description.length() == 0 ? "鏃�" : description.toString();
+ }
+
+ private static void appendFilledColumn(StringBuilder description, String columnName, String value) {
+ String trimmed = trimToNull(value);
+ if (trimmed == null) {
+ return;
+ }
+ if (description.length() > 0) {
+ description.append("锛�");
+ }
+ description.append(columnName).append('=').append(trimmed);
+ }
+
+ private static String dedupKey(String productName, String model, String productCode) {
+ String normalizedModel = trimToNull(model) == null ? "/" : model.trim();
+ String normalizedCode = productCode == null ? "" : productCode.trim();
+ return productName.trim() + "\u0001" + normalizedModel + "\u0001" + normalizedCode;
+ }
+
+ private String generateUniqueCode(Set<String> generatedCodes) {
+ for (int i = 0; i < 200; i++) {
+ String code = randomCode(5);
+ if (generatedCodes.contains(code)) {
+ continue;
+ }
+ Long count = productModelMapper.selectCount(new LambdaQueryWrapper<ProductModel>()
+ .eq(ProductModel::getProductCode, code));
+ if (count == null || count == 0) {
+ generatedCodes.add(code);
+ return code;
+ }
+ }
+ throw new ServiceException("鐢熸垚鍞竴浜у搧缂栫爜澶辫触锛岃閲嶈瘯");
+ }
+
+ private static String randomCode(int length) {
+ String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ sb.append(chars.charAt(ThreadLocalRandom.current().nextInt(chars.length())));
+ }
+ return sb.toString();
+ }
+
+ private static String trimToNull(String value) {
+ if (value == null) {
+ return null;
+ }
+ String trimmed = value.trim();
+ return trimmed.isEmpty() ? null : trimmed;
}
}
--
Gitblit v1.9.3