引入依赖
<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 {
//此处判断文件大小不能为0
if (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<>();
//获取表格数据,根据表格每一行的列数量不同,获取数据后组装型号json
getExcelData(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);
}
}
@Override
public 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:entity
private 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 {
//产品id
private String skuId;
//产品型号
private String skuName;
//规格英文名称
private String skuEnName;
//产品规格参数List
private List<SkuParamsList> skuParamsList;
@Data
public static class SkuParamsList {
//规格id
private String paramId;
//规格名称
private String paramName;
//规格英文名称
private String paramEnName;
//规格参数值
private String paramValue;
}
}