EasyExcel多行表头带动态下拉框导入导出具体实现

一、准备环境包

maven:<!-- guava本地缓存-->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.2-jre</version>
</dependency><!--easyexcel依赖-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.10</version>
</dependency>

二、多行表头的excel导出 带有设置下拉框


设计代码:import java.lang.annotation.*;/*** 标注导出的列为下拉框类型,并为下拉框设置内容*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelSelected {/*** 固定下拉内容*/String[] source() default {};/*** 动态下拉内容*/Class<? extends ExcelDynamicSelect>[] sourceClass() default {};/*** 设置下拉框的起始行,默认为第二行*/int firstRow() default 1;/*** 设置下拉框的结束行,默认为最后一行*/int lastRow() default 0x10000;
}
public interface ExcelDynamicSelect {/*** 获取动态生成的下拉框可选数据* @return 动态生成的下拉框可选数据*/String[] getSource();
}
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;@Configuration
public class CacheConfig {@Beanpublic Cache<String, Object> myCache() {return CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).maximumSize(100).build();}
}

import cn.hutool.extra.spring.SpringUtil;
import com.google.common.cache.Cache;
import com.youqian.common.rest.RestResponse;
import com.youqian.pms.api.feign.warehouse.client.WarehouseClient;
import com.youqian.pms.api.feign.warehouse.dto.DropdownQuery;
import com.youqian.pms.api.feign.warehouse.dto.SupplierBasicInfo;
import com.youqian.pms.boot.utils.easyexcelutil.ExcelDynamicSelect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.List;/*** 获取所有供应商名称列表*/
@Slf4j
@Component
public class SupplierNameServiceImpl implements ExcelDynamicSelect {private static final WarehouseClient warehouseClient;private static final Cache<String, Object> myCache;static {warehouseClient = SpringUtil.getBean(WarehouseClient.class);myCache = SpringUtil.getBean(Cache.class);}@PostConstructprivate void init() {myCache.put("all-supplier-name", this.getDataList());}private String getDataList() {DropdownQuery query = new DropdownQuery();RestResponse<List<SupplierBasicInfo>> listRestResponse = warehouseClient.queryAllSuppliers(query);StringBuilder stringBuilder = new StringBuilder();if (listRestResponse.isSuccess()) {List<SupplierBasicInfo> data = listRestResponse.getData();for (SupplierBasicInfo datum : data) {String supplierName = datum.getSupplierName();if (StringUtils.isNotBlank(supplierName)) {stringBuilder.append(supplierName).append(",");}}}log.info("guava cache queryAllSuppliers");return stringBuilder.toString();}@Overridepublic String[] getSource() {String value = "";try {value = String.valueOf(myCache.get("all-supplier-name", this::getDataList));} catch (Exception e) {log.warn("guava cache queryAllSuppliers warn :", e);}return value.split(",");}}

import cn.hutool.extra.spring.SpringUtil;
import com.google.common.cache.Cache;
import com.youqian.common.rest.RestResponse;
import com.youqian.pms.api.feign.warehouse.client.WarehouseClient;
import com.youqian.pms.api.feign.warehouse.dto.WarehouseManagementListReqDto;
import com.youqian.pms.api.feign.warehouse.dto.WarehouseManagementListRespDto;
import com.youqian.pms.boot.utils.easyexcelutil.ExcelDynamicSelect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.List;@Slf4j
@Component
public class WarehouseNameServiceImpl implements ExcelDynamicSelect {private static final WarehouseClient warehouseClient;private static final Cache<String, Object> myCache;static {warehouseClient = SpringUtil.getBean(WarehouseClient.class);myCache = SpringUtil.getBean(Cache.class);}@PostConstructprivate void init() {myCache.put("all-warehouse-name", this.getDataList());}private String getDataList() {WarehouseManagementListReqDto query = new WarehouseManagementListReqDto();RestResponse<List<WarehouseManagementListRespDto>> listRestResponse = warehouseClient.findList(query);StringBuilder stringBuilder = new StringBuilder();if (listRestResponse.isSuccess()) {List<WarehouseManagementListRespDto> data = listRestResponse.getData();for (WarehouseManagementListRespDto datum : data) {String warehouseName = datum.getWarehouseName();if (StringUtils.isNotBlank(warehouseName)) {stringBuilder.append(warehouseName).append(",");}}}log.info("guava cache warehouseName");return stringBuilder.toString();}@Overridepublic String[] getSource() {String value = "";try {value = String.valueOf(myCache.get("all-warehouse-name", this::getDataList));} catch (Exception e) {log.warn("guava cache warehouseName warn :", e);}return value.split(",");}}
import com.youqian.pms.boot.utils.easyexcelutil.ExcelDynamicSelect;
import com.youqian.pms.common.enums.buyorder.BuyTypeEnum;
import lombok.extern.slf4j.Slf4j;/*** 获取采购类型列表*/
@Slf4j
public class BuyTypeStrServiceImpl implements ExcelDynamicSelect {@Overridepublic String[] getSource() {StringBuilder value = new StringBuilder();for (BuyTypeEnum typeEnum : BuyTypeEnum.values()) {value.append(typeEnum.getDesc()).append(",");}return value.toString().split(",");}}
/*** 采购订单模板导出*/
@ApiOperation("采购订单导入模板导出")
@PostMapping("/buyOrder/exportPurchasingOrderTemplate")
public void exportPurchasingOrderTemplate(HttpServletResponse response) {log.info("exportPurchasingOrderTemplate start");this.buyOrderOperateService.exportPurchasingOrderTemplate(response);
}
导出模板对象:多个表头对应多个对象import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.youqian.pms.boot.utils.easyexcelutil.ExcelSelected;
import lombok.Data;import java.io.Serializable;/*** 采购订单基本信息*/
@Data
@HeadRowHeight(20)
@ContentRowHeight(18)
public class PurchaseOrderBaseInfoResp implements Serializable {private static final long serialVersionUID = -2121379439910317195L;/*** 供应商名称*/@ExcelSelected(sourceClass = SupplierNameServiceImpl.class)@ExcelProperty(index = 0,value = "供应商名称")@ColumnWidth(30)private String supplierName;/*** 仓库名称*/@ExcelSelected(sourceClass = WarehouseNameServiceImpl.class)@ExcelProperty(index = 1,value = "仓库名称")@ColumnWidth(30)private String warehouseName;/*** 采购类型*/@ExcelSelected(sourceClass = BuyTypeStrServiceImpl.class)@ExcelProperty(index = 2,value = "采购类型")@ColumnWidth(30)private String buyTypeStr;/*** 货主*/@ExcelSelected(sourceClass = BuyerNameServiceImpl.class)@ExcelProperty(index = 3,value = "货主")@ColumnWidth(30)private String buyerName;
}

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;/*** 采购订单商品信息*/
@Data
@HeadRowHeight(20)
@ContentRowHeight(18)
public class PurchaseOrderGoodsResp {/*** sku编码*/@ExcelProperty(index = 0,value = "SKU 编码")@ColumnWidth(30)private String skuCode;/*** 数量*/@ExcelProperty(index = 1,value = "数量")@ColumnWidth(30)private String count;/*** 单价*/@ExcelProperty(index = 2,value = "单价")@ColumnWidth(30)private String price;
}

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;@Slf4j
public class EasyExcelUtil {/*** 创建即将导出的sheet页(sheet页中含有带下拉框的列)* @param head 导出的表头信息和配置* @param sheetNo sheet索引* @param sheetName sheet名称* @param <T> 泛型* @return sheet页*/public static <T> WriteSheet writeSelectedSheet(Class<T> head, Integer sheetNo, String sheetName) {Map<Integer, ExcelSelectedResolve> selectedMap = resolveSelectedAnnotation(head);return EasyExcel.writerSheet(sheetNo, sheetName).head(head).registerWriteHandler(new SelectedSheetWriteHandler(selectedMap)).build();}/*** 解析表头类中的下拉注解* @param head 表头类* @param <T> 泛型* @return Map<下拉框列索引, 下拉框内容> map*/private static <T> Map<Integer, ExcelSelectedResolve> resolveSelectedAnnotation(Class<T> head) {Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>();// getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性Field[] fields = head.getDeclaredFields();for (int i = 0; i < fields.length; i++){Field field = fields[i];// 解析注解信息ExcelSelected selected = field.getAnnotation(ExcelSelected.class);ExcelProperty property = field.getAnnotation(ExcelProperty.class);if (selected != null) {ExcelSelectedResolve excelSelectedResolve = new ExcelSelectedResolve();String[] source = excelSelectedResolve.resolveSelectedSource(selected);if (source != null && source.length > 0){excelSelectedResolve.setSource(source);excelSelectedResolve.setFirstRow(selected.firstRow());excelSelectedResolve.setLastRow(selected.lastRow());if (property != null && property.index() >= 0){selectedMap.put(property.index(), excelSelectedResolve);} else {selectedMap.put(i, excelSelectedResolve);}}}}return selectedMap;}public static boolean isIDNumber(String IDNumber) {if (IDNumber == null || "".equals(IDNumber)) {return false;}// 定义判别用户身份证号的正则表达式(15位或者18位,最后一位可以为字母)String regularExpression = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|" +"(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)";//假设18位身份证号码:41000119910101123X  410001 19910101 123X//^开头//[1-9] 第一位1-9中的一个      4//\\d{5} 五位数字           10001(前六位省市县地区)//(18|19|20)                19(现阶段可能取值范围18xx-20xx年)//\\d{2}                    91(年份)//((0[1-9])|(10|11|12))     01(月份)//(([0-2][1-9])|10|20|30|31)01(日期)//\\d{3} 三位数字            123(第十七位奇数代表男,偶数代表女)//[0-9Xx] 0123456789Xx其中的一个 X(第十八位为校验值)//$结尾//假设15位身份证号码:410001910101123  410001 910101 123//^开头//[1-9] 第一位1-9中的一个      4//\\d{5} 五位数字           10001(前六位省市县地区)//\\d{2}                    91(年份)//((0[1-9])|(10|11|12))     01(月份)//(([0-2][1-9])|10|20|30|31)01(日期)//\\d{3} 三位数字            123(第十五位奇数代表男,偶数代表女),15位身份证不含X//$结尾boolean matches = IDNumber.matches(regularExpression);//判断第18位校验值if (matches) {if (IDNumber.length() == 18) {try {char[] charArray = IDNumber.toCharArray();//前十七位加权因子int[] idCardWi = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};//这是除以11后,可能产生的11位余数对应的验证码String[] idCardY = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};int sum = 0;for (int i = 0; i < idCardWi.length; i++) {int current = Integer.parseInt(String.valueOf(charArray[i]));int count = current * idCardWi[i];sum += count;}char idCardLast = charArray[17];int idCardMod = sum % 11;if (idCardY[idCardMod].toUpperCase().equals(String.valueOf(idCardLast).toUpperCase())) {return true;} else {return false;}} catch (Exception e) {e.printStackTrace();return false;}}return false;}return matches;}public static  boolean isMobile(String phone){Pattern p = null;Matcher m = null;boolean b = false;// 验证手机号String s2="^[1](([3|5|6|7|8|9][\\d])|([4][4,5,6,7,8,9])|([6][2,5,6,7])|([7][^9])|([9][1,8,9]))[\\d]{8}$";if(StringUtils.isNotBlank(phone)){p = Pattern.compile(s2);m = p.matcher(phone);b = m.matches();}return b;}}

import lombok.Data;
import lombok.extern.slf4j.Slf4j;@Data
@Slf4j
public class ExcelSelectedResolve {/*** 下拉内容*/private String[] source;/*** 设置下拉框的起始行,默认为第二行*/private int firstRow;/*** 设置下拉框的结束行,默认为最后一行*/private int lastRow;public String[] resolveSelectedSource(ExcelSelected excelSelected) {if (excelSelected == null) {return null;}// 获取固定下拉框的内容String[] source = excelSelected.source();if (source.length > 0) {return source;}// 获取动态下拉框的内容Class<? extends ExcelDynamicSelect>[] classes = excelSelected.sourceClass();if (classes.length > 0) {try {ExcelDynamicSelect excelDynamicSelect = classes[0].newInstance();String[] dynamicSelectSource = excelDynamicSelect.getSource();if (dynamicSelectSource != null && dynamicSelectSource.length > 0) {return dynamicSelectSource;}} catch (InstantiationException | IllegalAccessException e) {log.error("解析动态下拉框数据异常", e);}}return null;}}

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;import java.util.Map;@Data
@AllArgsConstructor
public class SelectedSheetWriteHandler implements SheetWriteHandler {private final Map<Integer, ExcelSelectedResolve> selectedMap;/*** 设置阈值,避免生成的导入模板下拉值获取不到,可自行设置数量大小*/private static final Integer LIMIT_NUMBER = 25;/*** Called before create the sheet*/@Overridepublic void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {}/*** Called after the sheet is created*/@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {// 这里可以对cell进行任何操作Sheet sheet = writeSheetHolder.getSheet();DataValidationHelper helper = sheet.getDataValidationHelper();selectedMap.forEach((k, v) -> {// 设置下拉列表的行: 首行,末行,首列,末列
//            CellRangeAddressList rangeList = new CellRangeAddressList(v.getFirstRow(), v.getLastRow(), k, k);CellRangeAddressList rangeList = new CellRangeAddressList(v.getFirstRow(), 5, k, k);// 如果下拉值总数大于25,则使用一个新sheet存储,避免生成的导入模板下拉值获取不到if (v.getSource().length > LIMIT_NUMBER) {//定义sheet的名称//1.创建一个隐藏的sheet 名称为 hidden + kString sheetName = "hidden" + k;Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet hiddenSheet = workbook.createSheet(sheetName);for (int i = 0, length = v.getSource().length; i < length; i++) {// 开始的行数i,列数khiddenSheet.createRow(i).createCell(k).setCellValue(v.getSource()[i]);}Name category1Name = workbook.createName();category1Name.setNameName(sheetName);String excelLine = getExcelLine(k);// =hidden!$H:$1:$H$50  sheet为hidden的 H1列开始H50行数据获取下拉数组String refers = "=" + sheetName + "!$" + excelLine + "$1:$" + excelLine + "$" + (v.getSource().length + 1);// 将刚才设置的sheet引用到你的下拉列表中DataValidationConstraint constraint = helper.createFormulaListConstraint(refers);DataValidation dataValidation = helper.createValidation(constraint, rangeList);writeSheetHolder.getSheet().addValidationData(dataValidation);// 设置存储下拉列值得sheet为隐藏int hiddenIndex = workbook.getSheetIndex(sheetName);if (!workbook.isSheetHidden(hiddenIndex)) {workbook.setSheetHidden(hiddenIndex, true);}}// 设置下拉列表的值DataValidationConstraint constraint = helper.createExplicitListConstraint(v.getSource());// 设置约束DataValidation validation = helper.createValidation(constraint, rangeList);// 阻止输入非下拉选项的值validation.setErrorStyle(DataValidation.ErrorStyle.STOP);validation.setShowErrorBox(true);
//			validation.setSuppressDropDownArrow(true);validation.createErrorBox("提示", "请输入下拉选项中的内容");sheet.addValidationData(validation);});}/*** 返回excel列标A-Z-AA-ZZ** @param num 列数* @return java.lang.String*/private String getExcelLine(int num) {String line = "";int first = num / 26;int second = num % 26;if (first > 0) {line = (char) ('A' + first - 1) + "";}line += (char) ('A' + second) + "";return line;}
}
@Override
public void exportPurchasingOrderTemplate(HttpServletResponse response) {String filename = "采购订单导入模板";try {// 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要filename = URLEncoder.encode(filename, "UTF-8");response.setContentType("application/octet-stream;charset=ISO8859-1");response.setHeader("Content-Disposition", "attachment;filename=" + filename);response.addHeader("Pragma", "no-cache");response.addHeader("Cache-Control", "no-cache");// 设置隔行表头List<List<String>> headList = Lists.newArrayList();headList.add(Lists.newArrayList());headList.add(Lists.newArrayList());headList.add(Lists.newArrayList());headList.add(Lists.newArrayList());headList.add(Lists.newArrayList());ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();WriteSheet writeSheet = EasyExcelUtil.writeSelectedSheet(PurchaseOrderBaseInfoResp.class, 0, filename);excelWriter.write(headList, writeSheet);WriteTable writeTable2 = EasyExcel.writerTable(2).needHead(Boolean.TRUE).head(PurchaseOrderGoodsResp.class).build();excelWriter.write(new ArrayList<String>(), writeSheet, writeTable2);excelWriter.finish();} catch (Exception e) {log.error("导出【采购订单导入模板】 error :",e);}
}

三、多行表头的excel导入

 /*** 采购商品批量导入* @return 采购商品列表*/
@ApiOperation("采购订单导入")
@PostMapping("/buyOrder/importPurchasingOrder")
public ResponseResult<AddOrModifyBuyOrderDto> importPurchasingOrder(MultipartFile file) {log.info("importPurchasingOrder start");try {return ResponseResult.buildSuccessResponse(this.buyOrderOperateService.importPurchasingOrder(file));} catch (Exception e) {log.info("采购订单导入失败", e);return ResponseResult.build(-1, e.getMessage(),null);}
}
@Override
public AddOrModifyBuyOrderDto importPurchasingOrder(MultipartFile file) {try {// 读取excel内容List<PurchaseOrderBaseInfoResp> baseInfoRespList = new ArrayList<>();List<PurchaseOrderGoodsResp> goodsRespList = new ArrayList<>();PurchaseOrderBaseInfoRespListener purchaseOrderBaseInfoRespListener = new PurchaseOrderBaseInfoRespListener(baseInfoRespList);PurchaseOrderGoodsRespListener purchaseOrderGoodsRespListener = new PurchaseOrderGoodsRespListener(goodsRespList);EasyExcel.read(file.getInputStream(), PurchaseOrderBaseInfoResp.class, purchaseOrderBaseInfoRespListener).head(PurchaseOrderBaseInfoResp.class).sheet().doRead();EasyExcel.read(file.getInputStream(), PurchaseOrderGoodsResp.class, purchaseOrderGoodsRespListener).headRowNumber(7).sheet().doRead();// 组装数据return this.assemblyObject(baseInfoRespList, goodsRespList);} catch (Exception e){log.error("导出【采购订单导入模板】 error :",e);throw new Exception(e.getMessage());}
}
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.youqian.pms.boot.controller.internal.buyorder.dto.PurchaseOrderBaseInfoResp;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.Map;@Getter
@Slf4j
public class PurchaseOrderBaseInfoRespListener implements ReadListener<PurchaseOrderBaseInfoResp> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private final List<PurchaseOrderBaseInfoResp> cachedDataList;public PurchaseOrderBaseInfoRespListener(List<PurchaseOrderBaseInfoResp> cachedDataList) {this.cachedDataList = cachedDataList;}@Overridepublic void onException(Exception e, AnalysisContext analysisContext) {}@Overridepublic void invokeHead(Map<Integer, CellData> map, AnalysisContext analysisContext) {}/*** 这个每一条数据解析都会来调用** @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(PurchaseOrderBaseInfoResp data, AnalysisContext context) {Integer rowIndex = context.readRowHolder().getRowIndex();if (rowIndex < 6) {log.info("解析到一条数据:{}", JSON.toJSONString(data));cachedDataList.add(data);}}@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {log.info("所有数据解析完成!");}@Overridepublic boolean hasNext(AnalysisContext analysisContext) {Integer rowIndex = analysisContext.readRowHolder().getRowIndex();return rowIndex < 6;}}
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.youqian.pms.boot.controller.internal.buyorder.dto.PurchaseOrderGoodsResp;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.Map;@Getter
@Slf4j
public class PurchaseOrderGoodsRespListener implements ReadListener<PurchaseOrderGoodsResp> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private final List<PurchaseOrderGoodsResp> cachedDataList;/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来*/public PurchaseOrderGoodsRespListener(List<PurchaseOrderGoodsResp> cachedDataList) {this.cachedDataList = cachedDataList;}@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}@Overridepublic void invokeHead(Map<Integer, CellData> map, AnalysisContext analysisContext) {System.out.println();}/*** 这个每一条数据解析都会来调用** @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(PurchaseOrderGoodsResp data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));cachedDataList.add(data);}@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库log.info("所有数据解析完成!");}@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return true;}}
private AddOrModifyBuyOrderDto assemblyObject(List<PurchaseOrderBaseInfoResp> baseInfoRespList, List<PurchaseOrderGoodsResp> goodsRespList) {// 校验数据if (CollectionUtils.isEmpty(baseInfoRespList) || CollectionUtils.isEmpty(goodsRespList)) {throw new Exception("导入的数据为空,请检查后重新操作");}PurchaseOrderBaseInfoResp baseInfoResp = baseInfoRespList.get(0);String buyerName = baseInfoResp.getBuyerName();if (StringUtils.isEmpty(buyerName)) {throw new Exception("[货主]不可为空");}String buyTypeStr = baseInfoResp.getBuyTypeStr();if (StringUtils.isEmpty(buyTypeStr)) {throw new Exception("[采购类型]不可为空");}String supplierName = baseInfoResp.getSupplierName();if (StringUtils.isEmpty(supplierName)) {throw new Exception("[供应商]不可为空");}String warehouseName = baseInfoResp.getWarehouseName();if (StringUtils.isEmpty(warehouseName)) {throw new Exception("[仓库]不可为空");}List<String> skuCodeList = new ArrayList<>();for (PurchaseOrderGoodsResp orderGoodsResp : goodsRespList) {String count = orderGoodsResp.getCount();if (StringUtils.isEmpty(count)) {throw new Exception("[SKU 数量]不可为空");}String price = orderGoodsResp.getPrice();if (StringUtils.isEmpty(price)) {throw new Exception("[SKU 单价]不可为空");}String skuCode = orderGoodsResp.getSkuCode();if (StringUtils.isEmpty(skuCode)) {throw new Exception("[SKU 编码]不可为空");}skuCodeList.add(skuCode);}Map<String, PurchaseOrderGoodsResp> skuCode2IdentityMap = goodsRespList.stream().collect(Collectors.toMap(PurchaseOrderGoodsResp::getSkuCode, Function.identity(), (oldOne, newOne) -> newOne));AddOrModifyBuyOrderDto addDto = new AddOrModifyBuyOrderDto();CompletableFuture<Void> f1 = CompletableFuture.runAsync(()->{RestResponse<BuySupplierMainResp> buySupplierMainRespRestResponse = supplierBuyerUserService.querySupplierMainListByBuyerName(buyerName);BuySupplierMainResp data = buySupplierMainRespRestResponse.getData();if (buySupplierMainRespRestResponse.isSuccess() && null != data) {Long buyerId = data.getId();addDto.setBuyerId(buyerId);} else {throw new Exception("[货主]: "+buyerName+" 不存在,请检查后重新填写");}addDto.setBuyerName(buyerName);int buyType = BuyTypeEnum.getCodeByDesc(buyTypeStr);if (buyType == -1) {throw new Exception("[采购类型]: "+buyTypeStr+" 不存在,请检查后重新填写");}addDto.setBuyType(buyType);});CompletableFuture<Void> f2 = CompletableFuture.runAsync(()->{DropdownQuery query = new DropdownQuery();query.setText(supplierName);RestResponse<List<SupplierBasicInfo>> listRestResponse = warehouseClient.queryAllSuppliers(query);log.info("AssemblyObject queryAllSuppliers req = {}, result = {}", JsonUtils.toJsonString(query), JsonUtils.toJsonString(listRestResponse));if (listRestResponse.isSuccess()) {List<SupplierBasicInfo> supplierBasicInfos = listRestResponse.getData();if (CollectionUtils.isNotEmpty(supplierBasicInfos)) {Integer supplierId = supplierBasicInfos.get(0).getId();if (null != supplierId) {addDto.setSupplierId(Long.valueOf(supplierId));} else {throw new Exception("[供应商]: "+supplierName+" 不存在,请检查后重新填写");}} else {throw new Exception("[供应商]: "+supplierName+" 不存在,请检查后重新填写");}}});addDto.setSupplierName(supplierName);CompletableFuture<Void> f3 = CompletableFuture.runAsync(()->{WarehouseManagementListReqDto warehouseManagementListReqDto = new WarehouseManagementListReqDto();warehouseManagementListReqDto.setWarehouseName(warehouseName);RestResponse<List<WarehouseManagementListRespDto>> warehouseManagementListResp = warehouseClient.findList(warehouseManagementListReqDto);log.info("AssemblyObject findList req = {}, result = {}", JsonUtils.toJsonString(warehouseManagementListReqDto), JsonUtils.toJsonString(warehouseManagementListResp));if (warehouseManagementListResp.isSuccess()) {List<WarehouseManagementListRespDto> warehouseManagementListRespDtos = warehouseManagementListResp.getData();if (CollectionUtils.isNotEmpty(warehouseManagementListRespDtos)) {Long warehouseId = warehouseManagementListRespDtos.get(0).getId();if (null != warehouseId) {addDto.setWarehouseId(warehouseId);} else {throw new Exception("[仓库]: "+warehouseName+" 不存在,请检查后重新填写");}} else {throw new Exception("[仓库]: "+warehouseName+" 不存在,请检查后重新填写");}}});CompletableFuture<Void> f4 = CompletableFuture.runAsync(() -> {List<BuyOrderItemDto> itemList = new ArrayList<>();BigDecimal postAmount = new BigDecimal("0");MaterielSpuInfoQueryReqDto materielSpuInfoQueryReqDto = new MaterielSpuInfoQueryReqDto();materielSpuInfoQueryReqDto.setSkuCodeList(skuCodeList);RestResponse<PageInfo<MaterielSpuInfoRespDto>> pageInfoRestResponse = warehouseClient.warehouseoutboundorderitemInfo(materielSpuInfoQueryReqDto);log.info("AssemblyObject warehouseoutboundorderitemInfo req = {}, result = {}", skuCodeList, JsonUtils.toJsonString(pageInfoRestResponse));if (pageInfoRestResponse.isSuccess()) {PageInfo<MaterielSpuInfoRespDto> materielSpuInfoResp = pageInfoRestResponse.getData();if (null != materielSpuInfoResp) {List<MaterielSpuInfoRespDto> list = materielSpuInfoResp.getList();if (CollectionUtils.isEmpty(list))  {throw new Exception("[SKU]: "+skuCodeList+" 不存在,请检查后重新填写");}for (MaterielSpuInfoRespDto dto : list) {String spuCode = dto.getSpuCode();String spuName = dto.getSpuName();List<MaterielSkuBySpuRespDto> skuListInfo = dto.getSkuListInfo();for (MaterielSkuBySpuRespDto materielSkuBySpuRespDto : skuListInfo) {String skuCode = materielSkuBySpuRespDto.getSkuCode();PurchaseOrderGoodsResp purchaseOrderGoodsResp = skuCode2IdentityMap.get(skuCode);String count = purchaseOrderGoodsResp.getCount();String price = purchaseOrderGoodsResp.getPrice();String skuName = materielSkuBySpuRespDto.getSkuName();Long materielModelId = materielSkuBySpuRespDto.getMaterielModelId();Long materielBrandId = materielSkuBySpuRespDto.getMaterielBrandId();Long materielClassId = materielSkuBySpuRespDto.getMaterielClassId();String materielBrandName = materielSkuBySpuRespDto.getMaterielBrandName();String materielClassName = materielSkuBySpuRespDto.getMaterielClassName();String materielModelName = materielSkuBySpuRespDto.getMaterielModelName();String thumbnailUrl = materielSkuBySpuRespDto.getThumbnailUrl();Long materielId = materielSkuBySpuRespDto.getId();String specValues = materielSkuBySpuRespDto.getSpecValues();Integer enableUniqueCodeFlag = materielSkuBySpuRespDto.getEnableUniqueCodeFlag();String materielNewConfigName = materielSkuBySpuRespDto.getMaterielNewConfigName();String materielBrandCode = materielSkuBySpuRespDto.getMaterielBrandCode();String materielClassCode = materielSkuBySpuRespDto.getMaterielClassCode();BuyOrderItemDto buyOrderItemDto = new BuyOrderItemDto();buyOrderItemDto.setSkuCode(skuCode);buyOrderItemDto.setSkuName(skuName);buyOrderItemDto.setMaterielModelId(materielModelId);buyOrderItemDto.setMaterielBrandId(materielBrandId);buyOrderItemDto.setMaterielClassId(materielClassId);buyOrderItemDto.setMaterielModelName(materielModelName);buyOrderItemDto.setMaterielBrandName(materielBrandName);buyOrderItemDto.setMaterielClassName(materielClassName);buyOrderItemDto.setSkuImg(thumbnailUrl);buyOrderItemDto.setMaterielId(materielId);buyOrderItemDto.setMaterielName(skuName);buyOrderItemDto.setSpec(specValues);buyOrderItemDto.setEnableUniqueCodeFlag(enableUniqueCodeFlag);buyOrderItemDto.setMaterielNewConfigName(materielNewConfigName);buyOrderItemDto.setSpuCode(spuCode);buyOrderItemDto.setSpuName(spuName);buyOrderItemDto.setCount(Integer.valueOf(count));buyOrderItemDto.setSkuAmount(new BigDecimal(price));buyOrderItemDto.setRebateAmount(new BigDecimal("0"));itemList.add(buyOrderItemDto);postAmount = new BigDecimal(price).multiply(new BigDecimal(count)).add(postAmount);}}}addDto.setItemList(itemList);addDto.setRebate(0);BuyOrderPaymentDto buyOrderPayment = new BuyOrderPaymentDto();buyOrderPayment.setAdvanceAmount(new BigDecimal("0"));buyOrderPayment.setBalancePaymentDay(1);buyOrderPayment.setPostAmount(postAmount);buyOrderPayment.setSettlementCycle(1);buyOrderPayment.setTotalAmount(postAmount);addDto.setBuyOrderPayment(buyOrderPayment);}});try {CompletableFuture<Void> future = CompletableFuture.allOf(f1, f2, f3, f4);future.get();} catch (Exception e){throw new Exception(e.getMessage().split("=",4)[3].replace(")",""));}return addDto;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/833437.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免费SSL证书?轻松申请攻略来了!

在当今的互联网时代&#xff0c;网络安全已经成为一个不容忽视的重要课题。随着在线交流和交易活动的增加&#xff0c;保护网站和用户信息的重要性日益突显。SSL证书&#xff0c;即安全套接字层证书&#xff0c;它为互联网通信提供了加密服务&#xff0c;确保数据的安全性和完整…

淘宝扭蛋机小程序开发:转动幸运,开启无限惊喜

一、探索未知&#xff0c;开启全新扭蛋体验 淘宝扭蛋机小程序&#xff0c;为您带来一场前所未有的扭蛋盛宴。在这个充满神秘与乐趣的平台上&#xff0c;每一次点击都将引领您走进未知的宝藏世界&#xff0c;每一次旋转都可能揭示出意想不到的惊喜。 二、海量商品&#xff0c;…

Kubernetes 教程:在 Containerd 容器中使用 GPU

原文链接:Kubernetes 教程:在 Containerd 容器中使用 GPU 云原生实验室本文介绍了如何在使用 Containerd 作为运行时的 Kubernetes 集群中使用 GPU 资源。https://fuckcloudnative.io/posts/add-nvidia-gpu-support-to-k8s-with-containerd/ 前两天闹得沸沸扬扬的事件不知道…

3D 交互展示该怎么做?

在博维数孪&#xff08;Bowell&#xff09;平台制作3D交互展示的流程相对简单&#xff0c;主要分为以下几个步骤&#xff1a; 1、准备3D模型&#xff1a;首先&#xff0c;你需要有一个3D模型。如果你有3D建模的经验&#xff0c;可以使用3ds Max或Blender等软件自行创建。如果没…

护眼台灯十大品牌哪个好?热销榜护眼灯十大品牌推荐

护眼台灯十大品牌哪个好&#xff1f;在这篇文章中&#xff0c;我将向大家介绍十大护眼台灯品牌&#xff0c;其中包括书客、松下、飞利浦等知名品牌。我精心挑选这些品牌&#xff0c;旨在为大家提供明智的选择参考。这些品牌的护眼台灯拥有的功能比较多&#xff0c;提供的光线也…

揭秘软胶囊品质的秘密武器:西奥机电CHT-01软胶囊弹性硬度测试仪

揭秘软胶囊品质的秘密武器&#xff1a;西奥机电CHT-01软胶囊弹性硬度测试仪 在医药行业中&#xff0c;软胶囊作为一种常见的药品剂型&#xff0c;因其独特的封装方式和便利性而受到广泛青睐。然而&#xff0c;软胶囊的质量问题也一直是制药企业关注的焦点。为了确保软胶囊的质量…

国际数字影像产业园专场招聘会暨四川城市职业学院双选会成功举办

为了进一步强化校企合作&#xff0c;链接企业与高素质人才&#xff0c;促进毕业生实现高质量就业&#xff0c;2024年5月7日&#xff0c;“成就梦想 职通未来”国际数字影像产业园专场招聘会暨四川城市职业学院2024届毕业生校园双选会成功举行。 当天&#xff0c;国际数字影像产…

全国首创!成都代表:国际数字影像产业园运营中

国际数字影像产业园&#xff0c;这座充满活力和创意的产业园区&#xff0c;以其独特的“数字影像文创”新型发展模式&#xff0c;正逐渐成为成都文创产业的标杆。它不仅仅是一个简单的成都文创产业园区&#xff0c;更是一个将数字影像、文化演艺、会展节庆、数字产业、艺术培训…

适用于 iPhone 的最佳数据恢复应用程序

意外删除了重要iPhone文件&#xff0c;或尝试从损坏的手机访问文件&#xff1f;我们收集了适用于 iPhone 的最佳数据恢复应用程序&#xff0c;这可能会扭转局面。 iPhone 数据恢复应用程序是一种您希望永远不需要的工具&#xff0c;但如果您需要的话&#xff0c;您一定会很高兴…

【编码利器 —— BaiduComate】

目录 1. 智能编码助手介绍 2. 场景需求 3. 功能体验 3.1指令功能 3.2插件用法 3.3知识用法 3.4自定义配置 4. 试用感受 5. AI编程应用 6.总结 智能编码助手是当下人工智能技术在编程领域的一项重要应用。Baidu Comate智能编码助手作为一款具有强大功能和智能特性的工…

计算机系列之算法分析与设计

21、算法分析与设计 算法是对特定问题求解步骤的一种描述。它是指令的有限序列&#xff0c;其中每一条指令标识一个或多个操作。 它具有有穷性、确定性&#xff08;含义确定、输入输出确定&#xff0c;相同输入相同输出&#xff1b;执行路径唯一&#xff09;、可行性、输入&a…

链表经典面试题01

目录 引言 面试题01:返回倒数第k个节点 题目描述: 思路分析: 代码展示: 面试题02:链表的回文结构 题目描述: 描述 思路分析: 代码展示: 面试题03:相交链表 题目描述: 思路分析: 代码展示: 小结: 引言 这次的题均来自力扣和牛客有关链表的经典面试题,代码只会展示…

C++从入门到精通——类的6个默认成员函数之拷贝构造函数

拷贝构造函数 前言一、拷贝构造函数概念理解定义 二、拷贝构造函数的特征三、注意要点写法实践传址返回与引用返回的区别传址返回引用返回 传值返回和传址返回的对比总结测试 前言 类的6个默认成员函数&#xff1a;如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中…

抢占用户|AI助力企业高效挖掘潜在客户,推动高质量转化

随着人工智能&#xff08;AI&#xff09;技术的崛起&#xff0c;企业终于可以在这个数字化时代获得一种强大的工具&#xff0c;帮助企业迅速而准确地找到潜在客户。AI不仅能够处理海量的数据&#xff0c;还能自动分析和识别潜在客户的特征和行为模式&#xff0c;为企业营销提供…

【WEEK11】 【DAY1】Employee Management System Part 2【English Version】

2024.5.6 Monday Continuing from 【WEEK10】 【DAY2】Employee Management System Part 1【English Version】 Contents 10.3. Page Internationalization10.3.1. Preparation10.3.2. Configuration File Writing10.3.2.1. Create an i18n (abbreviation for internationaliza…

专业的保密网文件导入导出系统,让文件流转行为更可控安全

军工单位因其涉及国防安全和军事机密&#xff0c;对保密工作有极高的要求&#xff0c;通常会采取严格的网络隔离措施来保护敏感信息和提高网络安全性。常见的方式是通过物理隔离将网络彻底分隔开来&#xff0c;比如保密网和非保密网。网络隔离后&#xff0c;仍有数据交换的需求…

GORM的常见命令

文章目录 一、什么是GORM&#xff1f;二、GORM连接mysql以及AutoMigrate创建表三、查询1、检索此对象是否存在于数据库&#xff08;First,Take,Last方法&#xff09;2、Find()方法检索3、根据指定字段查询 四、更新1、Save() 保存多个字段2、更新单个字段 五、删除 一、什么是G…

Python中设计注册登录代码

import hashlib import json import os import sys # user interface 用户是界面 UI """ 用户登录系统 1.注册 2.登陆 0.退出 """ # 读取users.bin def load(path): return json.load(open(path, "rt")) # 保存user.bin def save(dic…

Figma 高效技巧:设计系统中的图标嵌套

Figma 高效技巧&#xff1a;设计系统中的图标嵌套 在设计中&#xff0c;图标起着不可或缺的作用。一套便捷易用的图标嵌套方法可以有效提高设计效率。 分享一下我在图标嵌套上走过的弯路和经验教训。我的图标嵌套可以分三个阶段&#xff1a; 第一阶段&#xff1a;建立图标库 一…

目标检测实战(八): 使用YOLOv7完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)

文章目录 一、目标检测介绍二、YOLOv7介绍三、源码/论文获取四、环境搭建4.1 环境检测 五、数据集准备六、 模型训练七、模型验证八、模型测试九、错误总结9.1 错误1-numpy jas mp attribute int9.2 错误2-测试代码未能跑出检测框9.3 错误3- Command git tag returned non-zero…