diff --git a/src/main/java/com/gaotao/common/utils/ExcelUtil.java b/src/main/java/com/gaotao/common/utils/ExcelUtil.java new file mode 100644 index 0000000..28baea6 --- /dev/null +++ b/src/main/java/com/gaotao/common/utils/ExcelUtil.java @@ -0,0 +1,132 @@ +package com.gaotao.common.utils; + +import org.apache.poi.ss.usermodel.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * Excel工具类 + * 用于读取Excel文件并转换为Map列表 + */ +public class ExcelUtil { + + /** + * 读取Excel文件并转换为Map列表 + * @param file Excel文件 + * @return List> 每行数据的Map列表,key为列索引(从0开始) + * @throws Exception 读取异常 + */ + public static List> readExcelToMapList(MultipartFile file) throws Exception { + List> resultList = new ArrayList<>(); + + try (InputStream inputStream = file.getInputStream(); + Workbook workbook = WorkbookFactory.create(inputStream)) { + + // 获取第一个工作表 + Sheet sheet = workbook.getSheetAt(0); + + // 获取有效行数 + int lastRowNum = sheet.getLastRowNum(); + + // 遍历每一行(跳过表头,从第1行开始) + for (int rowIndex = 1; rowIndex <= lastRowNum; rowIndex++) { + Row row = sheet.getRow(rowIndex); + if (row == null) { + continue; + } + + // 检查是否为空行 + if (isEmptyRow(row)) { + continue; + } + + Map rowMap = new HashMap<>(); + + // 遍历每一列 + for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) { + Cell cell = row.getCell(cellIndex); + Object cellValue = getCellValue(cell); + rowMap.put(String.valueOf(cellIndex), cellValue); + } + + resultList.add(rowMap); + } + + } catch (IOException e) { + throw new Exception("读取Excel文件失败: " + e.getMessage(), e); + } + + return resultList; + } + + /** + * 获取单元格的值 + * @param cell 单元格 + * @return 单元格值 + */ + private static Object getCellValue(Cell cell) { + if (cell == null) { + return null; + } + + switch (cell.getCellType()) { + case STRING: + return cell.getStringCellValue().trim(); + case NUMERIC: + if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { + // 日期类型 + Date date = cell.getDateCellValue(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(date); + } else { + // 数字类型 + double numericValue = cell.getNumericCellValue(); + // 如果是整数,返回整数格式 + if (numericValue == Math.floor(numericValue)) { + return String.valueOf((long) numericValue); + } else { + return String.valueOf(numericValue); + } + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case FORMULA: + // 公式类型,获取计算后的值 + try { + return String.valueOf(cell.getNumericCellValue()); + } catch (Exception e) { + return cell.getStringCellValue().trim(); + } + case BLANK: + case ERROR: + default: + return null; + } + } + + /** + * 检查行是否为空 + * @param row 行对象 + * @return true表示空行 + */ + private static boolean isEmptyRow(Row row) { + if (row == null) { + return true; + } + + for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) { + Cell cell = row.getCell(cellIndex); + if (cell != null && cell.getCellType() != CellType.BLANK) { + Object cellValue = getCellValue(cell); + if (cellValue != null && !cellValue.toString().trim().isEmpty()) { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/gaotao/modules/notify/controller/NewIssureController.java b/src/main/java/com/gaotao/modules/notify/controller/NewIssureController.java index 94931d5..578c4c0 100644 --- a/src/main/java/com/gaotao/modules/notify/controller/NewIssureController.java +++ b/src/main/java/com/gaotao/modules/notify/controller/NewIssureController.java @@ -4,14 +4,17 @@ import com.gaotao.common.utils.R; import com.gaotao.modules.api.entity.IfsShopOrder; import com.gaotao.modules.api.service.IfsApiService; import com.gaotao.modules.base.entity.SOScheduledRoutingData; +import com.gaotao.modules.notify.entity.NotifyExcelData; import com.gaotao.modules.notify.entity.SOIssueNotifyOrderMaterialList; import com.gaotao.modules.notify.entity.SOIssueNotifyOrderMaterialListData; import com.gaotao.modules.notify.entity.vo.ShopOrderMaterialVo; import com.gaotao.modules.notify.service.NewIssureService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; + @RestController @RequestMapping("orderIssure/newIssure") public class NewIssureController { @@ -36,4 +39,26 @@ public class NewIssureController { List rows = newIssureService.getSOSBOMForIssureNew(data); return R.ok().put("rows", rows); } + + /** + * @Description Excel文件上传并解析 + * @Title uploadNotifyExcel + * @param file Excel文件 + * @author system + * @date 2024/12/19 + * @return R + * @throws Exception + */ + @PostMapping(value="/uploadNotifyExcel") + @ResponseBody + public R uploadNotifyExcel(@RequestParam("file") MultipartFile file) throws Exception { + try { + newIssureService.uploadNotifyExcel(file); + return R.ok(); + } catch (Exception e) { + System.err.println("Excel文件处理异常: " + e.getMessage()); + e.printStackTrace(); + return R.error("Excel文件处理失败: " + e.getMessage()); + } + } } diff --git a/src/main/java/com/gaotao/modules/notify/entity/NotifyExcelData.java b/src/main/java/com/gaotao/modules/notify/entity/NotifyExcelData.java new file mode 100644 index 0000000..984b49a --- /dev/null +++ b/src/main/java/com/gaotao/modules/notify/entity/NotifyExcelData.java @@ -0,0 +1,56 @@ +package com.gaotao.modules.notify.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.apache.ibatis.type.Alias; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Alias("NotifyExcelData") +public class NotifyExcelData { + + /** + * 工厂编码 + */ + private String site; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 发布号 + */ + private String releaseNo; + + /** + * 序列号 + */ + private String sequenceNo; + + /** + * BOM行号 + */ + private Integer bomLineNo; + + /** + * 物料编码 + */ + private String materialPartNo; + + /** + * 需求日期 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date needDate; + + /** + * 数量 + */ + private BigDecimal qty; +} \ No newline at end of file diff --git a/src/main/java/com/gaotao/modules/notify/service/NewIssureService.java b/src/main/java/com/gaotao/modules/notify/service/NewIssureService.java index 8e3ef72..10a8d7e 100644 --- a/src/main/java/com/gaotao/modules/notify/service/NewIssureService.java +++ b/src/main/java/com/gaotao/modules/notify/service/NewIssureService.java @@ -2,12 +2,24 @@ package com.gaotao.modules.notify.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.gaotao.modules.base.entity.SOScheduledRoutingData; +import com.gaotao.modules.notify.entity.NotifyExcelData; import com.gaotao.modules.notify.entity.SOIssueNotifyOrderMaterialListData; +import org.springframework.web.multipart.MultipartFile; import java.util.List; public interface NewIssureService { + List getSOSBOMForIssureNew(SOScheduledRoutingData data) throws JsonProcessingException; - List getSOSBOMForIssureNew(SOScheduledRoutingData data) throws JsonProcessingException; + /** + * @Description Excel文件上传并解析 + * @Title uploadNotifyExcel + * @param file Excel文件 + * @author system + * @date 2024/12/19 + * @return List + * @throws Exception + */ + void uploadNotifyExcel(MultipartFile file) throws Exception; } diff --git a/src/main/java/com/gaotao/modules/notify/service/impl/NewIssureServiceImpl.java b/src/main/java/com/gaotao/modules/notify/service/impl/NewIssureServiceImpl.java index 56de02d..6526e5d 100644 --- a/src/main/java/com/gaotao/modules/notify/service/impl/NewIssureServiceImpl.java +++ b/src/main/java/com/gaotao/modules/notify/service/impl/NewIssureServiceImpl.java @@ -1,19 +1,27 @@ package com.gaotao.modules.notify.service.impl; import com.fasterxml.jackson.core.JsonProcessingException; +import com.gaotao.common.utils.ExcelUtil; import com.gaotao.modules.api.entity.IfsShopOrder; import com.gaotao.modules.api.service.IfsApiService; import com.gaotao.modules.base.entity.SOScheduledRoutingData; +import com.gaotao.modules.notify.entity.NotifyExcelData; import com.gaotao.modules.notify.entity.SOIssueNotifyOrderMaterialListData; import com.gaotao.modules.notify.entity.vo.ShopOrderMaterialVo; import com.gaotao.modules.notify.mapper.NewIssureMapper; import com.gaotao.modules.notify.service.NewIssureService; +import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Map; @Service public class NewIssureServiceImpl implements NewIssureService { @@ -63,4 +71,142 @@ public class NewIssureServiceImpl implements NewIssureService { } return result; } + + @Override + public void uploadNotifyExcel(MultipartFile file) throws Exception { + // 校验文件格式 + if (file == null || file.isEmpty()) { + throw new RuntimeException("请选择要上传的文件"); + } + + String fileName = file.getOriginalFilename(); + if (StringUtils.isBlank(fileName)) { + throw new RuntimeException("文件名不能为空"); + } + + String fileExtension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); + if (!".xls".equals(fileExtension) && !".xlsx".equals(fileExtension)) { + throw new RuntimeException("文件格式不正确,只支持.xls和.xlsx格式的Excel文件"); + } + + // 使用ExcelUtil读取Excel文件 + List> excelDataList = ExcelUtil.readExcelToMapList(file); + + if (excelDataList.isEmpty()) { + throw new RuntimeException("Excel文件中没有有效数据"); + } + + // 转换为NotifyExcelData列表 + List notifyExcelDataList = new ArrayList<>(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + for (int i = 0; i < excelDataList.size(); i++) { + Map rowData = excelDataList.get(i); + int rowIndex = i + 2; // Excel行号(考虑表头) + + try { + NotifyExcelData notifyData = new NotifyExcelData(); + + // 第一列:site + Object siteObj = rowData.get("0"); + if (siteObj == null || StringUtils.isBlank(siteObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的工厂编码(site)不能为空"); + } + notifyData.setSite(siteObj.toString().trim()); + + // 第二列:orderNo + Object orderNoObj = rowData.get("1"); + if (orderNoObj == null || StringUtils.isBlank(orderNoObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的订单号(orderNo)不能为空"); + } + notifyData.setOrderNo(orderNoObj.toString().trim()); + + // 第三列:releaseNo + Object releaseNoObj = rowData.get("2"); + if (releaseNoObj == null || StringUtils.isBlank(releaseNoObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的发布号(releaseNo)不能为空"); + } + notifyData.setReleaseNo(releaseNoObj.toString().trim()); + + // 第四列:sequenceNo + Object sequenceNoObj = rowData.get("3"); + if (sequenceNoObj == null || StringUtils.isBlank(sequenceNoObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的序列号(sequenceNo)不能为空"); + } + notifyData.setSequenceNo(sequenceNoObj.toString().trim()); + + // 第五列:bomLineNo + Object bomLineNoObj = rowData.get("4"); + if (bomLineNoObj == null || StringUtils.isBlank(bomLineNoObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的BOM行号(bomLineNo)不能为空"); + } + try { + Integer bomLineNo = Integer.valueOf(bomLineNoObj.toString().trim()); + if (bomLineNo <= 0) { + throw new RuntimeException("第" + rowIndex + "行的BOM行号(bomLineNo)必须是大于0的整数"); + } + notifyData.setBomLineNo(bomLineNo); + } catch (NumberFormatException e) { + throw new RuntimeException("第" + rowIndex + "行的BOM行号(bomLineNo)必须是有效的整数"); + } + + // 第六列:materialPartNo + Object materialPartNoObj = rowData.get("5"); + if (materialPartNoObj == null || StringUtils.isBlank(materialPartNoObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的物料编码(materialPartNo)不能为空"); + } + notifyData.setMaterialPartNo(materialPartNoObj.toString().trim()); + + // 第七列:needDate + Object needDateObj = rowData.get("6"); + if (needDateObj == null || StringUtils.isBlank(needDateObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的需求日期(needDate)不能为空"); + } + try { + Date needDate = dateFormat.parse(needDateObj.toString().trim()); + notifyData.setNeedDate(needDate); + } catch (ParseException e) { + throw new RuntimeException("第" + rowIndex + "行的需求日期(needDate)格式不正确,请使用YYYY-MM-DD格式"); + } + + // 第八列:qty + Object qtyObj = rowData.get("7"); + if (qtyObj == null || StringUtils.isBlank(qtyObj.toString())) { + throw new RuntimeException("第" + rowIndex + "行的数量(qty)不能为空"); + } + try { + BigDecimal qty = new BigDecimal(qtyObj.toString().trim()); + if (qty.compareTo(BigDecimal.ZERO) <= 0) { + throw new RuntimeException("第" + rowIndex + "行的数量(qty)必须大于0"); + } + notifyData.setQty(qty); + } catch (NumberFormatException e) { + throw new RuntimeException("第" + rowIndex + "行的数量(qty)必须是有效的数字"); + } + + notifyExcelDataList.add(notifyData); + + } catch (Exception e) { + throw new RuntimeException("第" + rowIndex + "行数据处理异常: " + e.getMessage()); + } + } + + // 打印解析结果到控制台 + System.out.println("=== Excel解析结果 ==="); + System.out.println("共解析到 " + notifyExcelDataList.size() + " 条数据:"); + for (int i = 0; i < notifyExcelDataList.size(); i++) { + NotifyExcelData data = notifyExcelDataList.get(i); + System.out.println("第" + (i + 1) + "条: site=" + data.getSite() + + ", orderNo=" + data.getOrderNo() + + ", releaseNo=" + data.getReleaseNo() + + ", sequenceNo=" + data.getSequenceNo() + + ", bomLineNo=" + data.getBomLineNo() + + ", materialPartNo=" + data.getMaterialPartNo() + + ", needDate=" + dateFormat.format(data.getNeedDate()) + + ", qty=" + data.getQty()); + } + System.out.println("=== 解析完成 ==="); + + + } }