引入依赖
<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;}
}