?引入依賴
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.11</version><scope>compile</scope></dependency>
需要導入到某個目錄下
如果產品名稱相同,就追加里面的型號,如果型號也相同就返回提示,
如果產品名稱相同,表格內的數據要和瀏覽器錄入的 型號對應參數保持一致,順序、名稱、數量等都要完全一致
如果產品名稱相同,表格內部的 型號對應的參數必須保持一致,順序、名稱、數量等都要完全一致;
例如:
?導入的表格要個瀏覽器對應上
controller
/*** 導入設備*/@PostMapping("importProduct")@ApiOperationSupport(order = 11)@ApiOperation(value = "導入設備", notes = "傳入excel/產品")public R importProduct(MultipartFile file, Long categoryId) throws IOException {//此處判斷文件大小不能為0if (file.getSize() == 0) {return R.fail("文件大小不能為空");}if (categoryId == null || categoryId <= 0) {return R.fail("請選擇所屬產品分類");}return productService.importProduct(file, categoryId);}
services
//導入產品@Override@Transactional(rollbackFor = Exception.class)public R importProduct(MultipartFile file, Long categoryId) throws IOException {CategoryEntity category = categoryService.getById(categoryId);if (category == null) {throw new ServiceException("產品分類不存在");}DataExcelListener<ProductImportExcel> listener = new DataExcelListener<ProductImportExcel>();// headRowNumber(2):表示第一、二行為表頭,從第三行取值ExcelReader excelReader = EasyExcelFactory.read(file.getInputStream(), ProductImportExcel.class, listener).headRowNumber(2).build();excelReader.readAll();List<ProductImportExcel> data = listener.getDatas();excelReader.finish();Map<String, List<CodeProTypeJson>> map = new LinkedHashMap<>();//獲取表格數據,根據表格每一行的列數量不同,獲取數據后組裝型號jsongetExcelData(data, map);// 轉換數據并添加產品List<ProductEntity> productList = new ArrayList<>();//判斷導入的產品名稱和已有的產品名稱是否相同,如果相同就追加型號List<String> productNameList = new ArrayList<>();for (Map.Entry<String, List<CodeProTypeJson>> listEntry : map.entrySet()) {String productName = listEntry.getKey();List<CodeProTypeJson> value = listEntry.getValue();ProductEntity product = new ProductEntity();product.setCategoryId(categoryId);product.setCodeProName(productName);product.setCodeProType(JSON.toJSONString(value));product.setCodeProTypeNum(value.size());product.setEnterpriseId(IotAuthUtil.getEnterpriseId());productNameList.add(productName);productList.add(product);}// 批量查詢 已經存在的產品 存在就覆蓋 不存在就新增if (productNameList != null && productNameList.size() > 0) {LambdaQueryWrapper<ProductEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.in(ProductEntity::getCodeProName, productNameList);List<ProductEntity> selectList = baseMapper.selectList(queryWrapper);List<ProductEntity> updateList = new ArrayList<>();List<ProductEntity> addList = new ArrayList<>();for (ProductEntity entity : productList) {// 判斷產品是否已經添加,如已添加就走修改邏輯,未添加就走添加邏輯ProductEntity product = containsProductEntity(entity, selectList);if (product != null) {entity.setId(product.getId());updateList.add(entity);} else {addList.add(entity);}/*Long productId = containsProductEntity(entity, selectList);if (productId != null) {entity.setId(productId);updateList.add(entity);} else {addList.add(entity);}*/}// 批量更新if (updateList != null && updateList.size() > 0) {this.updateBatchById(updateList);}// 批量添加if (addList != null && addList.size() > 0) {this.saveBatch(addList);}}return R.success("導入成功!");}
?Excel導入數據 解析監聽器 用于獲取excel表格的數據
package com.bluebird.code.util;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Cell;import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** Excel導入數據 解析監聽器 用于獲取excel表格的數據*/
public class DataExcelListener<T> extends AnalysisEventListener<T> {/*** 自定義用于暫時存儲data* 可以通過實例獲取該值*/private List<T> datas = new ArrayList<>();/*** 每解析一行都會回調invoke()方法** @param object 讀取后的數據對象* @param context 內容*/@Override@SuppressWarnings("unchecked")public void invoke(Object object, AnalysisContext context) {T data = (T) object;//數據存儲到list,供批量處理,判斷每一行是否為空行。if (data != null && !isEmptyRow(context.readRowHolder().getCellMap())) {datas.add(data);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {//解析結束銷毀不用的資源//注意不要調用datas.clear(),否則getDatas為null}/*** 返回數據** @return 返回讀取的數據集合**/public List<T> getDatas() {return datas;}/*** 設置讀取的數據集合** @param datas 設置讀取的數據集合**/public void setDatas(List<T> datas) {this.datas = datas;}/*** 判斷一行是否為空行* @param cellMap 當前行的單元格映射* @return 如果所有單元格都為空,則返回 true,否則返回 false*/private boolean isEmptyRow(Map<Integer, Cell> cellMap) {for (Object cellValue : cellMap.values()) {if (cellValue != null && !cellValue.toString().trim().isEmpty()) {return false;}}return true;}}
獲取數據后組裝型號json串 (同時處理表格內部相同產品名稱不同型號參數名的問題,要確保相同參數名稱的參數名保持一致)
// 獲取數據后組裝型號json串 (同時處理表格內部相同產品名稱不同型號參數名的問題,要確保相同參數名稱的參數名保持一致)public void getExcelData(List<ProductImportExcel> data, Map<String, List<CodeProTypeJson>> map) {int count = 2;// 判斷產品名稱和型號是否相等,如過相等就返回提示,產品名和型號組成合并后需要唯一Map<String, String> proNameTypeMap = new HashMap<>();// 定義一個map集合,key:產品名稱,value:型號集合的名稱拼接Map<String, String> mapProductType = new LinkedHashMap<>();for (ProductImportExcel entity : data) {count++;String productName = entity.getCodeProName();String codeProType = entity.getCodeProType();String proNameType = productName + codeProType;if (StringUtils.isBlank(productName) || StringUtils.isEmpty(productName)) {throw new ServiceException("第 " + count + " 行產品名稱不能為空!");}if (StringUtils.isBlank(codeProType) || StringUtils.isEmpty(codeProType)) {throw new ServiceException("第 " + count + " 行規格型號不能為空!");}//String proNameTypeStr = proNameTypeMap.get(proNameType);if (StringUtils.isNotBlank(proNameTypeStr)) {throw new ServiceException("第 " + count + " 行和第 " + (count - 1) + "產品名稱下的規格型號不能重復!");}proNameTypeMap.put(proNameType, proNameType);List<CodeProTypeJson> list = map.get(productName);CodeProTypeJson json = new CodeProTypeJson();List<CodeProTypeJson.SkuParamsList> listDate = new ArrayList<>();json.setSkuName(entity.getCodeProType());json.setSkuId(IdWorker.getIdStr());// 將參數名進行拼接StringBuilder paramSb = new StringBuilder();// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName1())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName1());skuParamsList.setParamValue(entity.getParamValue1());listDate.add(skuParamsList);paramSb.append(entity.getParamName1());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName2())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName2());skuParamsList.setParamValue(entity.getParamValue2());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName2());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName3())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName3());skuParamsList.setParamValue(entity.getParamValue3());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName3());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName4())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName4());skuParamsList.setParamValue(entity.getParamValue4());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName4());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName5())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName5());skuParamsList.setParamValue(entity.getParamValue5());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName5());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName6())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName6());skuParamsList.setParamValue(entity.getParamValue6());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName6());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName7())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName7());skuParamsList.setParamValue(entity.getParamValue7());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName7());}// 根據型號參數名字處理表格數據,如果此單元格為空就不處理(因為每個產品的型號數量不確定)if (StringUtils.isNotEmpty(entity.getParamName8())) {CodeProTypeJson.SkuParamsList skuParamsList = new CodeProTypeJson.SkuParamsList();skuParamsList.setParamId(IdWorker.getIdStr());skuParamsList.setParamName(entity.getParamName8());skuParamsList.setParamValue(entity.getParamValue8());listDate.add(skuParamsList);paramSb.append("->");paramSb.append(entity.getParamName8());}// 將參數名拼接后轉換字符串String paramSbStr = paramSb.toString();// 根據當前行的產品名稱去map集合獲取已經存入的參數名的拼接串String paramNameMap = mapProductType.get(productName);if (Func.isNotBlank( paramNameMap ) && !paramNameMap.equals( paramSbStr )) {String msg = "[ " + paramNameMap + " ] 和 [ " + paramSbStr + " ] 型號參數名稱、數量、順序需保持一致";throw new ServiceException(" 產品名稱為:[ " + productName + " ] 的型號參數與表格其他行的參數名稱或順序不一致,請修改!</br>" + msg);}// 如果校驗通過后添加參數拼接串到map集合中mapProductType.put(productName, paramSbStr);json.setSkuParamsList(listDate);if (list == null) {list = new ArrayList<>();}List<CodeProTypeJson> typeJsons = map.get(productName);if (typeJsons != null && typeJsons.size() > 0) {typeJsons.add(json);map.put(productName, typeJsons);} else {list.add(json);map.put(productName, list);}}}
判斷產品是否已經添加,如已添加就走修改邏輯,未添加就走添加邏輯? excel:entity
// 判斷產品是否已經添加,如已添加就走修改邏輯,未添加就走添加邏輯 excel:entityprivate static ProductEntity containsProductEntity(ProductEntity entity, List<ProductEntity> selectList) {for (ProductEntity item : selectList) {if (item.getCodeProName().equals(entity.getCodeProName())) {// 如果導入的產品名稱和已經添加的產品名稱相同,就追加型號,如果型號相同,就返回提示List<CodeProTypeJson> itemTypeJsonList = JSONObject.parseArray(Func.toStr(item.getCodeProType()), CodeProTypeJson.class);List<CodeProTypeJson> excelTypeJsonList = JSONObject.parseArray(Func.toStr(entity.getCodeProType()), CodeProTypeJson.class);for (CodeProTypeJson itemJson : itemTypeJsonList) {for (CodeProTypeJson excelJson : excelTypeJsonList) {if (Objects.equals(itemJson.getSkuName(), excelJson.getSkuName())) {throw new ServiceException(" 產品名稱為: " + item.getCodeProName() + " 的 " + itemJson.getSkuName() + " 型號已存在,請先修改該型號");}}}// 比較 如果導入的產品名稱和數據庫已有的數據產品名稱相同,就比對型號的名稱和順序是否一致,如果不一致就返回提示if (itemTypeJsonList != null && itemTypeJsonList.size() > 0 && excelTypeJsonList != null && excelTypeJsonList.size() > 0) {// 獲取查詢到的第一個型號的參數列表CodeProTypeJson codeProTypeJson = itemTypeJsonList.get(0);List<CodeProTypeJson.SkuParamsList> itemSkuParamsList = codeProTypeJson.getSkuParamsList();// 將參數名進行拼接StringBuilder sbItem = new StringBuilder();for (int i = 0; i < itemSkuParamsList.size(); i++) {CodeProTypeJson.SkuParamsList param = itemSkuParamsList.get(i);sbItem.append(param.getParamName());if (i < itemSkuParamsList.size() - 1) {sbItem.append("->");}}String itemTypeJson = sbItem.toString();// 遍歷excel表格里面的所有型號對應的參數列表for (CodeProTypeJson json : excelTypeJsonList) {List<CodeProTypeJson.SkuParamsList> skuParamsList = json.getSkuParamsList();// 遍歷某個型號的所有參數名,將參數名進行拼接StringBuilder sbJson = new StringBuilder();for (int i = 0; i < skuParamsList.size(); i++) {CodeProTypeJson.SkuParamsList param = skuParamsList.get(i);sbJson.append(param.getParamName());if (i < skuParamsList.size() - 1) {sbJson.append("->");}}String excelTypeJson = sbJson.toString();System.out.println("itemTypeJson " + itemTypeJson);System.out.println("excelTypeJson " + excelTypeJson);System.out.println(itemTypeJson.equals(excelTypeJson));// 比對數據庫查詢的 拼接參數和excel表格里面的 拼接參數是否一致if (!itemTypeJson.equals(excelTypeJson)) {String msg = "原參數[ " + itemTypeJson + " ] 新參數 [ " + excelTypeJson + " ]";throw new ServiceException(" 產品名稱為:[ " + item.getCodeProName() + " ]的型號參數與已有參數名稱不一致或順序不一致,請修改!</br>" + msg);}}}itemTypeJsonList.addAll(excelTypeJsonList);entity.setCodeProType(JSON.toJSONString(itemTypeJsonList));entity.setCodeProTypeNum(itemTypeJsonList.size());entity.setId(item.getId());return entity;}}return null;}
ProductEntity實體類
package com.bluebird.code.entity;import com.baomidou.mybatisplus.annotation.TableName;
import com.bluebird.core.tenant.mp.TenantEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;/*** 賦碼 - 產品表 實體類 */
@Data
@TableName("t_code_product")
@ApiModel(value = "Product對象", description = "賦碼 - 產品表")
@EqualsAndHashCode(callSuper = true)
public class ProductEntity extends TenantEntity {/*** 企業id*/@ApiModelProperty(value = "企業id")private Long enterpriseId;/*** 產品名稱*/@ApiModelProperty(value = "產品名稱")private String codeProName;/*** 產品編號*/@ApiModelProperty(value = "產品編號")private String codeProNum;/*** 產品分類*/@ApiModelProperty(value = "產品分類")private Long categoryId;/*** 產品圖片*/@ApiModelProperty(value = "產品圖片")private String codeProImage;/*** 產品主圖圖片*/@ApiModelProperty(value = "產品主圖圖片")private String codeMainImage;/*** 產品視頻*/@ApiModelProperty(value = "產品視頻")private String codeProVideo;/*** 產品型號(第一組元素必須為規格型號)** @see com.bluebird.code.dto.CodeProTypeJson*/@ApiModelProperty(value = "產品型號(第一組元素必須為規格型號)")
// private CodeProTypeJson codeProType;private String codeProType;/*** 產品簡介*/@ApiModelProperty(value = "產品簡介")private String codeProDesc;/*** 規格數量*/@ApiModelProperty(value = "規格數量")private Integer codeProTypeNum;/*** 排序*/@ApiModelProperty(value = "排序")private Integer sort;/*** 備注*/@ApiModelProperty(value = "備注")private String remark;}
CodeProTypeJson 產品型號Json對象 (第一組元素必須為規格型號)
package com.bluebird.code.dto;import lombok.Data;import java.util.List;/**** 產品型號Json對象 (第一組元素必須為規格型號)*** @return:* @date: 2024/6/20*/
@Data
public class CodeProTypeJson {//產品idprivate String skuId;//產品型號private String skuName;//規格英文名稱private String skuEnName;//產品規格參數Listprivate List<SkuParamsList> skuParamsList;@Datapublic static class SkuParamsList {//規格idprivate String paramId;//規格名稱private String paramName;//規格英文名稱private String paramEnName;//規格參數值private String paramValue;}
}