通过easyexcel导入数据,添加表格参数的校验,同表格内校验以及和已有数据的校验

引入依赖

复制代码
<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;
	}
}
相关推荐
Wenzar_3 分钟前
**零信任架构下的微服务权限控制:用Go实现基于JWT的动态访问策略**在现代云原生环境中,
java·python·微服务·云原生·架构
zhangfeng11337 分钟前
多台服务器同时训练llamfactory 大语言模型 国家超算中心 Slurm 是目前全球最主流的开源、高性能计算(HPC)集群资源管理与作业调度系统
服务器·语言模型·开源
不会写DN1 小时前
其实跨域问题是后端来解决的? CORS
服务器·网络·面试·go
爱学习的小囧7 小时前
ESXi 8.0 原生支持 NVMe 固态硬盘吗?VMD 配置详解教程
linux·运维·服务器·esxi·esxi8.0
lUie INGA7 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
坚持就完事了7 小时前
Linux中的变量
linux·运维·服务器
geBR OTTE7 小时前
SpringBoot中整合ONLYOFFICE在线编辑
java·spring boot·后端
Porunarufu7 小时前
博客系统UI自动化测试报告
java
Cat_Rocky7 小时前
利用Packet Tracer网络实验
linux·运维·服务器
of Watermelon League8 小时前
SpringBoot集成Flink-CDC,实现对数据库数据的监听
数据库·spring boot·flink