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;软胶囊的质量问题也一直是制药企业关注的焦点。为了确保软胶囊的质量…

Qt窗口及QWidget类详解

QtWidgets 模块及窗口基本概念 QWidget 类是所有用户界面对象的基类,QWidget 及其子类是开发桌面应用的核心,这些类都位于 QtWidgets 模块内,注意:QtWidgets 是模块,QWidget 是类(少一个字母 t 和最后的 s)。 部件或窗口部件:Qt 把建立用户界面的元素称为窗口部件(widg…

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

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

Redis: windows安装使用、本地启动、客户端ARDM

https://www.cnblogs.com/xiaoniandexigua/p/17419288.html https://www.cnblogs.com/lal520/p/17981824 5款实用的Redis可视化工具-CSDN博客 AnotherRedisDesktopManager: Github国内镜像&#xff0c;供下载使用&#xff0c;有问题可移步到下面GitHub链接

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

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

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

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

Kubernetes(K8S) — 生产环境

生产环境 生产质量的 Kubernetes 集群需要规划和准备。 如果你的 Kubernetes 集群是用来运行关键负载的&#xff0c;该集群必须被配置为弹性的&#xff08;Resilient&#xff09;。 生产环境考量 需要考虑的因数 可用性&#xff1a;一个单机的 Kubernetes 学习环境 具有单点…

Docker 中快速构建 Redis Cluster 集群

Docker 中快速构建 Redis Cluster 集群 目录 前言环境准备 所需软件配置网络 构建 Redis Cluster 镜像 创建自定义 Dockerfile构建镜像 启动 Redis 节点容器 启动命令 配置 Redis Cluster 集群 创建 Redis 集群验证集群状态 总结 前言 Redis 是一个高性能的键值对数据库&am…

GIS入门,不使用任何第三方库,纯JavaScript实现Catmull-Rom样条曲线(Spline Curve)插值算法,生成更加平滑连续的曲线

前言 本章介绍不使用任何第三方库,纯JavaScript实现样条曲线(Spline Curve)插值算法,生成更加平滑连续的曲线。 样条曲线(Spline Curve)插值算法比贝塞尔曲线生成的曲线更加平滑连续。 介绍 样条曲线(Spline Curve)是一种通过给定的控制点插值生成平滑曲线的方法。其…

什么是SFP光模块?如何选择SFP光模块?

SFP光模块是一种小型化、支持热拔插的光模块&#xff0c;用于光纤通信、数据通信和网络设备之间的传输。本文将从SFP光模块的概述、技术原理、外观组成、如何选择以太网SFP光模块以及SFP光模块的未来趋势等方面进行介绍&#xff0c;帮助读者更好地了解SFP光模块并正确选择适合自…

浏览器渲染

1 React JSX如何渲染&#xff1f; JSX 本身不能被浏览器直接识别和执行&#xff0c;因为它不是标准的 JavaScript 语法。 JSX 是一种语法扩展&#xff0c;它允许开发者在 JavaScript 中以类似HTML的语法编写UI结构。 使用编译器&#xff08;如Babel&#xff09;&#xff1a;B…

【编码利器 —— 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…

ps基础学习笔记-颜色模式

ps基础学习笔记-颜色模式 在数字图像处理的世界中&#xff0c;颜色模式是至关重要的。它们定义了图像如何存储、显示和打印颜色。不同的颜色模式各有其特点和适用场景。本文将深入探讨常见的颜色模式&#xff1a;RGB、CMYK、索引模式HSB、Lab、灰度模式和位图模式。通过了解这…

Android 蓝牙实战——蓝牙音乐播放/暂停状态(二十二)

对于蓝牙音乐的播放状态&#xff0c;我们首先主要处理的是 onPlayStatusChanged() 回调&#xff0c;这是协议栈通知 FW 层的一个回调接口。还有一个就是 getPlayBackState() 方法&#xff0c;这是媒体应用在初始化时未收到回调信息主动获取当前状态的方法。我们这里就来分析一下…