package com.ruoyi.system.controller; import java.io.*; import java.math.BigDecimal; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.fasterxml.jackson.core.JsonProcessingException; import com.ruoyi.common.excel.DefaultExcelListener; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.FtpUtil; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.VersionComparator; import com.ruoyi.common.utils.file.SmbUtil; import com.ruoyi.common.poi.ExcelTemplateProc; import com.ruoyi.common.poi.DynamicDataMapping; import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.dto.MaterialUseDTO; import com.ruoyi.system.domain.dto.ProcessRouteDTO; import com.ruoyi.system.domain.dto.ProcessRouteExcelDTO; import com.ruoyi.system.domain.dto.excuteDrawing.RigidChainModelDTO; import com.ruoyi.system.domain.vo.*; import com.ruoyi.system.domain.vo.BomDataVO; import com.ruoyi.system.mapper.ProcessOrderProMapper; import com.ruoyi.system.mapper.SafetyStockMapper; import com.ruoyi.system.runner.JdUtil; import com.ruoyi.system.service.*; import lombok.RequiredArgsConstructor; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import com.ruoyi.common.annotation.RepeatSubmit; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.PageQuery; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.validate.AddGroup; import com.ruoyi.common.core.validate.EditGroup; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.bo.ProcessOrderProBo; import com.ruoyi.common.core.page.TableDataInfo; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; /** * 生产项目 * @author tzy * @date 2024-10-22 */ @Validated @RequiredArgsConstructor @RestController @Slf4j @RequestMapping("/system/orderPro") public class ProcessOrderProController extends BaseController { private final IProcessOrderProService iProcessOrderProService; @Autowired private final IMrpResultCheckService iMrpResultCheckService; private final ProcessOrderProMapper processOrderProMapper; private final IImMaterialService imMaterialService; private final ISafetyStockService safetyStockService; private final IProcessRouteService processRouteService; private final SafetyStockMapper stockMapper; private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy年MM月dd日"); /** * 查询项目令号列表 */ @SaCheckPermission("system:orderPro:list") @GetMapping("/list") public TableDataInfo list(ProcessOrderProBo bo, PageQuery pageQuery) { return iProcessOrderProService.queryPageList(bo, pageQuery); } @SaCheckPermission("system:orderPro:processlist") @GetMapping("/processlist") public List processList(ProcessOrderProBo bo) { return iProcessOrderProService.selectProList(bo); } /** * 导出项目令号列表 */ @SaCheckPermission("system:orderPro:export") @Log(title = "项目令号", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(ProcessOrderProBo bo, HttpServletResponse response) { List list = iProcessOrderProService.queryList(bo); ExcelUtil.exportExcel(list, "项目令号", ProcessOrderProVo.class, response); } /** * 获取项目令号详细信息 * * @param id 主键 */ @SaCheckPermission("system:orderPro:query") @GetMapping("/{id}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { return R.ok(iProcessOrderProService.queryById(id)); } /** * 新增项目令号 */ @SaCheckPermission("system:orderPro:add") @Log(title = "项目令号", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody ProcessOrderProBo bo) { boolean result = iProcessOrderProService.insertByBo(bo); return toAjax(result); } /** * 修改项目令号 */ @SaCheckPermission("system:orderPro:edit") @Log(title = "项目令号", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody ProcessOrderProBo bo) { return toAjax(iProcessOrderProService.updateByBo(bo)); } /** * 删除项目令号 * * @param ids 主键串 */ @SaCheckPermission("system:orderPro:remove") @Log(title = "项目令号", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { return iProcessOrderProService.deleteWithValidByIds(Arrays.asList(ids), true); } @SaCheckPermission("system:orderPro:getExcel") @Log(title = "获取项目生产数据表", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/getExcel") public R getExcel(@Validated(AddGroup.class) @RequestBody ProcessOrderProBo bo) { // 获取项目生产数据表 获取项目令号 也是文件夹的名称 SmbUtil.downloadExcelFiles(bo.getProductionOrderNo()); // 在本地读取EXCEL 文件 String ExcelName = "D:\\file\\" + bo.getProductionOrderNo() + ".xlsx"; try { File file = new File(ExcelName); if (file.exists()) { // 读取Excel的sheet6 从第三行开始 读取到第4列 无数据的跳过 DefaultExcelListener listener = new DefaultExcelListener<>(true); EasyExcel.read(ExcelName, ProcessRoute.class, listener).sheet(6).headRowNumber(3).doRead(); List list = listener.getExcelResult().getList(); for (ProcessRoute processRoute : list) { System.out.println(processRoute.getMaterialCode()); System.out.println(processRoute.getMaterialName()); System.out.println(processRoute.getMaterial()); System.out.println(processRoute.getDiscWeight()); System.out.println("==================================================================="); } } else { throw new ServiceException("没有数据"); } } catch (Exception e) { throw new RuntimeException(e); } //读取excel文件sheet return toAjax(iProcessOrderProService.insertByBo(bo)); } /** * 批量更新项目令号的计划开始和结束时间 * 根据工艺路线中的工序获取每个项目令号的最早开始时间和最晚结束时间,并更新到ProcessOrderPro */ @PostMapping("/batchUpdateProjectTimeRanges") public void batchUpdateProjectTimeRanges() { iProcessOrderProService.batchUpdateProjectTimeRanges(); } @SaCheckPermission("system:orderPro:addProduct") @Log(title = "排产计划", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/addProduct") public R addProduct(@Validated(AddGroup.class) @RequestBody RigidChainModelDTO orderPro) { iProcessOrderProService.addProduct(orderPro); return R.ok(); } @SaCheckPermission("system:orderPro:executDrawing") @Log(title = "执行出图", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/executDrawing") public R executDrawing(@RequestBody ProcessOrderProBo orderPro) { String s = iProcessOrderProService.executDrawing(orderPro); return R.ok(s); } @GetMapping("/executDrawing/progress/{id}") public SseEmitter executDrawingProgress(@PathVariable Long id) { return iProcessOrderProService.startDrawingSse(id); } /** * 上传dwg图纸 * * @param filePath * @return */ @SaCheckPermission("system:orderPro:uploadDwg") @Log(title = "上传dwg图纸", businessType = BusinessType.uploadDwg) @PostMapping("/uploadDwg") @ResponseBody public R uploadContractPDF(@RequestParam("ids") List ids, @RequestParam("file") MultipartFile filePath) { String originalFilename = filePath.getOriginalFilename(); if (StringUtils.isEmpty(originalFilename)) { return R.fail("获取文件名称错误!!"); } String suffix = originalFilename.substring(originalFilename.lastIndexOf('.') + 1); if (!"dwg".equalsIgnoreCase(suffix)) { return R.fail("禁止非法文件上传!!"); } List results = new ArrayList<>(); for (Long id : ids) { if (id == null) { results.add("ID为空,已跳过"); continue; } String res = iProcessOrderProService.uploadContractPDF(id.intValue(), originalFilename, filePath); results.add("ID=" + id + ":" + res); } return R.ok(StringUtils.join(results, ",")); } /** * 下载PDF并生成zip包 */ @SaCheckPermission("system:orderPro:uploadPDF") @Log(title = "下载PDF", businessType = BusinessType.UPDATE) @CrossOrigin(origins = "*", allowedHeaders = "*", exposedHeaders = {"Content-Disposition", "Content-Length"}) @GetMapping("/uploadPDF") public void uploadPDF(@RequestParam Long id, HttpServletResponse response) { try { // CORS headers for all responses (success and error) response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition, Content-Length"); // 调用service方法获取文件路径 R result = iProcessOrderProService.uploadPDF(id); if (result.getCode() != 200) { response.setStatus(HttpStatus.BAD_REQUEST.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":" + result.getCode() + ",\"msg\":\"" + result.getMsg() + "\"}"); return; } String filePath = result.getMsg(); File file = new File(filePath); // 检查文件是否存在 if (!file.exists()) { response.setStatus(HttpStatus.NOT_FOUND.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":404,\"msg\":\"文件不存在\"}"); return; } // 设置响应头 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\""); response.setHeader("Content-Length", String.valueOf(file.length())); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition, Content-Length"); // 写入文件流 try (FileInputStream fis = new FileInputStream(file); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[8192]; int length; while ((length = fis.read(buffer)) > 0) { os.write(buffer, 0, length); } os.flush(); } } catch (Exception e) { log.error("下载PDF文件失败", e); try { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.getWriter().write("下载失败: " + e.getMessage()); } catch (IOException ex) { log.error("写入错误响应失败", ex); } } } /** * 下载ZIP包 */ @SaCheckPermission("system:orderPro:downloadZip") @Log(title = "下载ZIP包", businessType = BusinessType.OTHER) @GetMapping("/downloadZip/{id}") public void downloadZip(@PathVariable Long id, HttpServletResponse response) { try { // 获取ZIP包路径 R result = iProcessOrderProService.uploadPDF(id); if (result.getCode() != 200) { response.setContentType("text/html;charset=utf-8"); response.getWriter().write("生成ZIP包失败: " + result.getMsg()); return; } String zipPath = result.getData(); File zipFile = new File(zipPath); if (!zipFile.exists()) { response.setContentType("text/html;charset=utf-8"); response.getWriter().write("ZIP文件不存在: " + zipPath); return; } // 设置响应头 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(zipFile.getName(), "UTF-8")); response.setContentLength((int) zipFile.length()); // 写入文件流 try (FileInputStream fis = new FileInputStream(zipFile); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) > 0) { os.write(buffer, 0, length); } os.flush(); } } catch (Exception e) { try { response.setContentType("text/html;charset=utf-8"); response.getWriter().write("下载失败: " + e.getMessage()); } catch (IOException ex) { ex.printStackTrace(); } } } @GetMapping("/overdue") public List getOverdueProjects() { return iProcessOrderProService.getOverdueProjects(); } @SaCheckPermission("system:orderPro:geMRPResults") @Log(title = "获取MRP复核结果", businessType = BusinessType.OTHER) @PostMapping("/getMRPResults/{id}") public R> geMRPResults(@PathVariable Long id) { return iMrpResultCheckService.getMRPResults(id); } @SaCheckPermission("system:orderPro:getRouteLog") @Log(title = "获取工艺上传结果", businessType = BusinessType.OTHER) @PostMapping("/getRouteLog/{id}") public R getRouteLog(@PathVariable Long id) throws JsonProcessingException { return iProcessOrderProService.getRouteLog(id); } @SaCheckPermission("system:orderPro:exportRoute") @Log(title = "下载工艺生产表", businessType = BusinessType.EXPORT) @PostMapping("/exportRoute") public void exportRoute(@RequestParam("id") Long id, HttpServletResponse response) { try { ProcessOrderPro orderPro = processOrderProMapper.selectById(id); // 下载Excel文件 SmbUtil.downloadExcelFiles(orderPro.getProductionOrderNo()); // 构建文件路径 String excelName = "D:\\file\\" + orderPro.getProductionOrderNo() + "汇总表.xlsx"; String rawDataFile = "D:\\file\\RawDataTable.xlsx"; File file = new File(excelName); if (!file.exists()) { throw new ServiceException("项目 " + orderPro.getProductionOrderNo() + " 未出图"); } // 1. 读取第一个sheet的数据list - 使用POI直接读取以保留空格 List allDataList = readExcelWithPOI(excelName,orderPro.getProductionOrderNo()); List routeList = readExcelPOIRoute(excelName,orderPro.getProductionOrderNo()); // 2. 读取原始表数据 List rawDataList = readRawDataTable(rawDataFile); // 3. 数据分类处理 List vmiList = new ArrayList<>(); // 009开头 List elecOutList = new ArrayList<>(); // 两个空格和017开头 List supplierList = new ArrayList<>(); // 甲供件 List evoProductsList = new ArrayList<>(); // 伊特 List processDataList = new ArrayList<>(); // 工艺数据(剩余数据) // 分类逻辑 for (ProductionOrderVo item : allDataList) { String materialCode = item.getDrawingNo(); String remark = item.getRemark(); // 使用备注字段 // 009开头的加入VMI表 if (materialCode != null) { String drawingNo = item.getDrawingNo(); String drawingName = item.getDrawingName(); if (drawingName != null) { ImMaterial material = imMaterialService.selectByCodeAndName(drawingNo, drawingName); if (material != null) { //判断是否是VMI物料 if ("true".equals(material.getClassificationName())) { // 检查是否已存在相同的DrawingNo boolean found = false; for (VMIDataVO existingVmi : vmiList) { if (drawingNo.equals(existingVmi.getDrawingNo())) { // 将数量和批次数量相加 Integer currentQuantity = existingVmi.getQuantity() != null ? existingVmi.getQuantity() : 0; Integer itemQuantity = item.getQuantity() != null ? item.getQuantity().intValue() : 0; existingVmi.setQuantity(currentQuantity + itemQuantity); Integer currentBatchQuantity = existingVmi.getBatchQuantity() != null ? existingVmi.getBatchQuantity() : 0; Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.parseInt(item.getBatchQuantity()) : 0; existingVmi.setBatchQuantity(currentBatchQuantity + itemBatchQuantity); found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的VMI数据 if (!found) { VMIDataVO vmiData = convertToVMIDataVO(item); vmiList.add(vmiData); } continue; // 已分类,跳过后续检查 } } } // 电气外包分类条件:物料编码开头空格/特定前缀 或 备注包含"外购" if (materialCode.startsWith(" ") || materialCode.startsWith("009301") || materialCode.startsWith("009999") || materialCode.startsWith("017003") || materialCode.startsWith("017002") || materialCode.startsWith("009001") || materialCode.startsWith("009081") || (remark != null && remark.contains("外购"))) { // 过滤安全库存:如果属于安全库存,则进入工艺数据列表 Boolean isSafeStock = safetyStockService.isSafeCode(materialCode.trim()); if (isSafeStock) { // 属于安全库存,添加到工艺数据列表 processDataList.add(item); continue; // 已分类,跳过后续检查 } else { // 不属于安全库存,检查是否已存在相同的DrawingNo boolean found = false; for (ElecOutDataVO existingElec : elecOutList) { if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingElec.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingElec.getQuantity() != null ? existingElec.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; Double newQuantity = currentQuantity + itemQuantity; existingElec.setQuantity(newQuantity); // 批次数量相加(String类型) String currentBatchQuantity = existingElec.getBatchQuantity() != null ? (existingElec.getBatchQuantity()).toString() : "0"; String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; try { Integer currentBatch = Integer.valueOf(currentBatchQuantity); Integer itemBatch = Integer.valueOf(itemBatchQuantity); String newBatchQuantity = String.valueOf(currentBatch + itemBatch); existingElec.setBatchQuantity(Integer.valueOf(newBatchQuantity)); } catch (NumberFormatException e) { // 如果转换失败,保持原值 existingElec.setBatchQuantity(Integer.valueOf(currentBatchQuantity)); } found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的电气外包数据 if (!found) { ElecOutDataVO elecData = convertToElecOutDataVO(item); elecOutList.add(elecData); } continue; // 已分类,跳过后续检查 } } // 备注是甲供件的 if (remark != null && remark.contains("甲供件")) { SupProvidDataVO supplierData = convertToSupProvidDataVO(item); supplierList.add(supplierData); continue; // 已分类,跳过后续检查 } // 备注是伊特 if (remark != null && remark.contains("伊特")) { EVOProductsDataVO evoData = convertToEVOProductsDataVO(item); evoProductsList.add(evoData); continue; // 已分类,跳过后续检查 } } // 其他数据作为工艺数据(剩余数据) // 检查是否已存在相同的DrawingNo boolean found = false; for (ProductionOrderVo existingProcess : processDataList) { if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingProcess.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingProcess.getQuantity() != null ? existingProcess.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; Double newQuantity = currentQuantity + itemQuantity; existingProcess.setQuantity(newQuantity); // 批次数量相加(String类型) String currentBatchQuantity = existingProcess.getBatchQuantity() != null ? existingProcess.getBatchQuantity() : "0"; String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; try { Integer currentBatch = Integer.valueOf(currentBatchQuantity); Integer itemBatch = Integer.valueOf(itemBatchQuantity); String newBatchQuantity = String.valueOf(currentBatch + itemBatch); existingProcess.setBatchQuantity(newBatchQuantity); } catch (NumberFormatException e) { // 如果转换失败,保持原值 existingProcess.setBatchQuantity(currentBatchQuantity); } found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的工艺数据 if (!found) { processDataList.add(item); } } // 使用Excel模板文件 String templatePath = "jpg/生产及工艺计划模版.xlsx"; String outputPath = "D:/file/" + orderPro.getProductionOrderNo() + "生产及工艺计划表.xlsx"; // 准备模板数据 Map staticDataMap = new HashMap<>(); staticDataMap.put("productionOrderNo", orderPro.getProductionOrderNo()); staticDataMap.put("productionName", orderPro.getProductionName()); //获取工艺数据信息 List excelDTOList = iProcessOrderProService.getRouteAndBomDetail(routeList, processDataList, orderPro); excelDTOList.sort(Comparator.comparing(ProcessRouteExcelDTO::getMaterial, Comparator.nullsLast((m1, m2) -> { // 总装部件优先 boolean isTotal1 = "总装部件".equals(m1); boolean isTotal2 = "总装部件".equals(m2); if (isTotal1 && !isTotal2) return -1; if (!isTotal1 && isTotal2) return 1; if (isTotal1 && isTotal2) return 0; // 其他材质按字母顺序排序 if (m1 == null && m2 == null) return 0; if (m1 == null) return 1; if (m2 == null) return -1; return m1.compareTo(m2); }) ).thenComparing( ProcessRouteExcelDTO::getMaterialCode, Comparator.nullsLast(new VersionComparator()) )); // 准备动态数据映射 List dynamicDataMappingList = new ArrayList<>(); // 添加生产订单数据 if (!allDataList.isEmpty()) { List> productionDataList = convertProductionOrderToMapList(allDataList, orderPro.getProductionOrderNo()); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProductionOrder", productionDataList)); } // 添加工艺数据(第七个sheet:工艺及生产计划表) if (!processDataList.isEmpty()) { List> processDataMapList = convertProductionOrderToMapList(processDataList, orderPro.getProductionOrderNo()); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProcessData", processDataMapList)); } // 添加VMI数据 if (!vmiList.isEmpty()) { List> vmiDataList = convertVMIDataToMapList(vmiList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("VMIDataVO", vmiDataList)); } // 添加电气外购数据 if (!elecOutList.isEmpty()) { List> elecDataList = convertElecOutDataToMapList(elecOutList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ElecOutDataVO", elecDataList)); } // 添加BOM数据 if (!rawDataList.isEmpty()) { List> bomDataList = convertBomDataToMapList(rawDataList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("BomDataVO", bomDataList)); } // 添加甲供件数据 if (!supplierList.isEmpty()) { List> supplierDataList = convertSupProvidDataToMapList(supplierList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("SupProvidDataVO", supplierDataList)); } // 添加伊特产品数据 if (!evoProductsList.isEmpty()) { List> evoDataList = convertEVOProductsDataToMapList(evoProductsList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("EVOProductsDataVO", evoDataList)); } /* // 添加伊特产品数据 if (!excelDTOList.isEmpty()) { List> evoRouteDataList = convertRouteDataToMapList(excelDTOList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProcessRouteExcelDTO", evoRouteDataList)); }*/ // 使用模板导出Excel ExcelTemplateProc.doExportExcelByTemplateProc(templatePath, outputPath, staticDataMap, dynamicDataMappingList); // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(orderPro.getProductionOrderNo() + "_分类BOM表", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 将生成的文件写入响应 File outputFile = new File(outputPath); if (outputFile.exists()) { try (FileInputStream fis = new FileInputStream(outputFile); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[8192]; int length; while ((length = fis.read(buffer)) > 0) { os.write(buffer, 0, length); } os.flush(); } // 删除临时文件 outputFile.delete(); } } catch (Exception e) { log.error("导出分类BOM表失败", e); throw new RuntimeException("导出失败: " + e.getMessage()); } } private List readExcelPOIRoute(String excelName,String name) { List resultList = new ArrayList<>(); try (FileInputStream fis = new FileInputStream(excelName); XSSFWorkbook workbook = new XSSFWorkbook(fis)) { XSSFSheet sheet = workbook.getSheetAt(6); // 读取第一个sheet // 从第3行开始读取(headRowNumber=2,所以从第3行开始) for (int rowIndex = 3; rowIndex <= sheet.getLastRowNum(); rowIndex++) { XSSFRow row = sheet.getRow(rowIndex); if (row == null) { continue; } ProcessRoute vo = new ProcessRoute(); // 根据列索引读取数据,保留原始空格 vo.setMaterialCode(getCellValueAsString(row.getCell(0))); // 图号 vo.setRouteDescription(name); // 图号 vo.setMaterialName(getCellValueAsString(row.getCell(1))); // 名称 vo.setMaterial(getCellValueAsString(row.getCell(2))); // 数量 vo.setDiscWeight(getCellValueAsDouble(row.getCell(3))); vo.setRawMaterialCode(getCellValueAsString(row.getCell(4))); vo.setRawMaterialName(getCellValueAsString(row.getCell(5))); vo.setBomMaterial(getCellValueAsString(row.getCell(6))); vo.setBomUnit(getCellValueAsString(row.getCell(9))); vo.setBomDanZhong(getCellValueAsDouble(row.getCell(7))); vo.setDiscUsage(getCellValueAsString(row.getCell(8))); // 单重 vo.setBatchQuantity(getCellValueAsLong(row.getCell(18))); // 批次数量 vo.setUnitQuantity(getCellValueAsDouble(row.getCell(17))); // 批次数量 vo.setXuEndTime(getCellValueAsDate(row.getCell(21))); // 批次数量 vo.setXuStartTime(getCellValueAsDate(row.getCell(20))); // 批次数量 resultList.add(vo); } log.info("使用POI读取Excel成功,共{}条记录", resultList.size()); } catch (Exception e) { log.error("使用POI读取Excel失败", e); throw new ServiceException("读取Excel文件失败: " + e.getMessage()); } return resultList; } /** * 读取RawDataTable数据 */ private List readRawDataTable(String filePath) { List dataList = new ArrayList<>(); try { File file = new File(filePath); if (!file.exists()) { log.warn("RawDataTable.xlsx文件不存在: {}", filePath); return dataList; } // 读取Excel数据 DefaultExcelListener listener = new DefaultExcelListener<>(true); EasyExcel.read(filePath, BomDataVO.class, listener) .sheet(0) // 读取第一个sheet .headRowNumber(2) // 从第1行开始读取 .doRead(); dataList = listener.getExcelResult().getList(); log.info("成功读取RawDataTable数据,共{}条记录", dataList.size()); } catch (Exception e) { log.error("读取RawDataTable数据失败", e); } return dataList; } /** * 转换为Map列表(用于模板) */ private List> convertToMapList(List routeList, ProcessOrderPro orderPro) { List> mapList = new ArrayList<>(); for (ProcessRoute route : routeList) { Map map = new HashMap<>(); map.put("materialCode", route.getMaterialCode()); map.put("materialName", route.getMaterialName()); map.put("material", route.getMaterial()); map.put("unitWeight", route.getDiscWeight()); map.put("quantity", route.getUnitQuantity()); map.put("batchQuantity", route.getBatchQuantity()); map.put("workCenter", route.getWorkCenter()); map.put("processName", route.getProcessName()); map.put("processDescription", route.getProcessDescription()); map.put("productionOrderNo", orderPro.getProductionOrderNo()); map.put("productionName", orderPro.getProductionName()); map.put("drawingNo", orderPro.getDrawingNo()); map.put("mainProducts", orderPro.getDrawingName()); map.put("mainProductsName", orderPro.getDrawingName()); mapList.add(map); } return mapList; } /** * 使用POI直接读取Excel文件,保留前后空格 */ private List readExcelWithPOI(String excelPath,String orderName) { List resultList = new ArrayList<>(); try (FileInputStream fis = new FileInputStream(excelPath); XSSFWorkbook workbook = new XSSFWorkbook(fis)) { XSSFSheet sheet = workbook.getSheetAt(0); // 读取第一个sheet // 从第3行开始读取(headRowNumber=2,所以从第3行开始) for (int rowIndex = 2; rowIndex <= sheet.getLastRowNum(); rowIndex++) { XSSFRow row = sheet.getRow(rowIndex); if (row == null) { continue; } ProductionOrderVo vo = new ProductionOrderVo(); // 根据列索引读取数据,保留原始空格 vo.setId(getCellValueAsLong(row.getCell(0))); vo.setProductionOrderNo(orderName); vo.setDrawingNo(getCellValueAsString(row.getCell(1))); // 图号 vo.setDrawingName(getCellValueAsString(row.getCell(2))); // 名称 vo.setQuantity(getCellValueAsDouble(row.getCell(3))); // 数量 vo.setMaterial(getCellValueAsString(row.getCell(4))); // 材料 vo.setSingleWeight(getCellValueAsDouble(row.getCell(5))); // 单重 vo.setTotalWeight(getCellValueAsDouble(row.getCell(6))); // 总重 vo.setRemark(getCellValueAsString(row.getCell(7))); // 备注 vo.setBatchQuantity(getCellValueAsString(row.getCell(8))); // 批次数量 vo.setParentPart(getCellValueAsString(row.getCell(9))); // 上级部件 vo.setParentDrawingNo(getCellValueAsString(row.getCell(10))); // 上级部件图号 vo.setMainProducts(getCellValueAsString(row.getCell(11))); // 主产品图号 vo.setMainProductsName(getCellValueAsString(row.getCell(12))); // 主产品名称 resultList.add(vo); } log.info("使用POI读取Excel成功,共{}条记录", resultList.size()); } catch (Exception e) { log.error("使用POI读取Excel失败", e); throw new ServiceException("读取Excel文件失败: " + e.getMessage()); } return resultList; } /** * 获取单元格的字符串值,保留前后空格 */ private String getCellValueAsString(XSSFCell cell) { if (cell == null) { return null; } switch (cell.getCellType()) { case STRING: // 直接从POI获取原始字符串,保留前后空格 String value = cell.getStringCellValue(); return value; case NUMERIC: return String.valueOf(cell.getNumericCellValue()); case BOOLEAN: return String.valueOf(cell.getBooleanCellValue()); case FORMULA: return cell.getCellFormula(); default: return null; } } /** * 获取单元格的Date值 */ private Date getCellValueAsDate(XSSFCell cell) { if (cell == null) { return null; } switch (cell.getCellType()) { case NUMERIC: if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { return cell.getDateCellValue(); } return null; case STRING: try { String dateStr = cell.getStringCellValue(); if (dateStr == null || dateStr.trim().isEmpty()) { return null; } // 尝试解析字符串日期 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return sdf.parse(dateStr); } catch (ParseException e) { sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.parse(dateStr); } } catch (Exception e) { return null; } default: return null; } } /** * 获取单元格的Long值 */ private Long getCellValueAsLong(XSSFCell cell) { if (cell == null) { return null; } switch (cell.getCellType()) { case NUMERIC: return (long) cell.getNumericCellValue(); case STRING: try { return Long.valueOf(cell.getStringCellValue()); } catch (NumberFormatException e) { return null; } default: return null; } } /** * 获取单元格的Double值 */ private Double getCellValueAsDouble(XSSFCell cell) { if (cell == null) { return null; } switch (cell.getCellType()) { case NUMERIC: return cell.getNumericCellValue(); case STRING: try { return Double.valueOf(cell.getStringCellValue()); } catch (NumberFormatException e) { return null; } default: return null; } } /** * 获取字符串的Unicode表示 */ private String getUnicodeString(String str) { StringBuilder sb = new StringBuilder(); for (char c : str.toCharArray()) { sb.append("\\u").append(String.format("%04x", (int) c)); } return sb.toString(); } /** * 转换ProductionOrder为Map列表(用于模板) */ private List> convertProductionOrderToMapList(List productionOrderList, String proName) { List> mapList = new ArrayList<>(); for (ProductionOrderVo item : productionOrderList) { Map map = new HashMap<>(); map.put("productionOrderNo", proName); map.put("drawingNo", item.getDrawingNo()); map.put("drawingName", item.getDrawingName()); map.put("quantity", item.getQuantity()); map.put("material", item.getMaterial()); map.put("singleWeight", item.getSingleWeight()); map.put("totalWeight", item.getTotalWeight()); map.put("remark", item.getRemark()); map.put("parentPart", item.getParentPart()); map.put("parentDrawingNo", item.getParentDrawingNo()); map.put("batchQuantity", item.getBatchQuantity()); // 为了兼容模板,也提供数字格式的batchQuantity Integer batchQuantityInt = null; if (item.getBatchQuantity() != null && !item.getBatchQuantity().isEmpty()) { try { batchQuantityInt = Integer.valueOf(item.getBatchQuantity()); } catch (NumberFormatException e) { // 如果转换失败,保持原值 map.put("batchQuantityInt", null); } } map.put("batchQuantityInt", batchQuantityInt); map.put("mainProducts", item.getMainProducts()); map.put("mainProductsName", item.getMainProductsName()); mapList.add(map); } return mapList; } /** * 转换BOM数据为Map列表(用于模板) */ private List> convertBomDataToMapList(List bomDataList) { List> mapList = new ArrayList<>(); for (BomDataVO bom : bomDataList) { Map map = new HashMap<>(); map.put("index", bom.getIndex()); map.put("drawingNo", bom.getDrawingNo()); map.put("name", bom.getName()); map.put("quantity", bom.getQuantity()); map.put("material", bom.getMaterial()); map.put("unitWeight", bom.getUnitWeight()); map.put("totalWeight", bom.getTotalWeight()); map.put("remark", bom.getRemark()); map.put("component", bom.getComponent()); map.put("componentDrawingNo", bom.getComponentDrawingNo()); mapList.add(map); } return mapList; } /** * 转换ProductionOrder为VMIDataVO */ private VMIDataVO convertToVMIDataVO(ProductionOrderVo item) { VMIDataVO vmiData = new VMIDataVO(); vmiData.setIndex(null); // ProductionOrder没有index字段 vmiData.setDrawingNo(item.getDrawingNo()); vmiData.setName(item.getDrawingName()); vmiData.setQuantity(item.getQuantity() != null ? item.getQuantity().intValue() : null); vmiData.setMaterial(item.getMaterial()); vmiData.setUnitWeight(item.getSingleWeight() != null ? BigDecimal.valueOf(item.getSingleWeight()) : null); vmiData.setTotalWeight(item.getTotalWeight() != null ? BigDecimal.valueOf(item.getTotalWeight()) : null); vmiData.setRemark(item.getRemark()); vmiData.setBatchQuantity(item.getBatchQuantity() != null ? Integer.valueOf(item.getBatchQuantity()) : null); return vmiData; } /** * 转换ProductionOrder为ElecOutDataVO */ private ElecOutDataVO convertToElecOutDataVO(ProductionOrderVo item) { ElecOutDataVO elecData = new ElecOutDataVO(); elecData.setIndex(null); // ProductionOrder没有index字段 elecData.setDrawingNo(item.getDrawingNo()); elecData.setName(item.getDrawingName()); elecData.setQuantity(Double.valueOf(item.getQuantity() != null ? item.getQuantity().intValue() : null)); elecData.setMaterial(item.getMaterial()); elecData.setUnitWeight(item.getSingleWeight() != null ? BigDecimal.valueOf(item.getSingleWeight()) : null); elecData.setTotalWeight(item.getTotalWeight() != null ? BigDecimal.valueOf(item.getTotalWeight()) : null); elecData.setRemark(item.getRemark()); elecData.setBatchQuantity(item.getBatchQuantity() != null ? Integer.valueOf(item.getBatchQuantity()) : null); return elecData; } /** * 转换ProductionOrder为SupProvidDataVO */ private SupProvidDataVO convertToSupProvidDataVO(ProductionOrderVo item) { SupProvidDataVO supplierData = new SupProvidDataVO(); supplierData.setIndex(null); // ProductionOrder没有index字段 supplierData.setDrawingNo(item.getDrawingNo()); supplierData.setName(item.getDrawingName()); supplierData.setQuantity(item.getQuantity() != null ? item.getQuantity().intValue() : null); supplierData.setMaterial(item.getMaterial()); supplierData.setUnitWeight(item.getSingleWeight() != null ? BigDecimal.valueOf(item.getSingleWeight()) : null); supplierData.setTotalWeight(item.getTotalWeight() != null ? BigDecimal.valueOf(item.getTotalWeight()) : null); supplierData.setRemark(item.getRemark()); supplierData.setBatchQuantity(item.getBatchQuantity() != null ? Integer.valueOf(item.getBatchQuantity()) : null); return supplierData; } /** * 转换ProductionOrder为EVOProductsDataVO */ private EVOProductsDataVO convertToEVOProductsDataVO(ProductionOrderVo item) { EVOProductsDataVO evoData = new EVOProductsDataVO(); evoData.setIndex(null); // ProductionOrder没有index字段 evoData.setDrawingNo(item.getDrawingNo()); evoData.setName(item.getDrawingName()); evoData.setQuantity(item.getQuantity() != null ? item.getQuantity().intValue() : null); evoData.setMaterial(item.getMaterial()); evoData.setUnitWeight(item.getSingleWeight() != null ? BigDecimal.valueOf(item.getSingleWeight()) : null); evoData.setTotalWeight(item.getTotalWeight() != null ? BigDecimal.valueOf(item.getTotalWeight()) : null); evoData.setRemark(item.getRemark()); evoData.setBatchQuantity(item.getBatchQuantity() != null ? Integer.valueOf(item.getBatchQuantity()) : null); return evoData; } /** * 转换VMIDataVO为Map列表(用于模板) */ private List> convertVMIDataToMapList(List vmiDataList) { List> mapList = new ArrayList<>(); int index = 1; for (VMIDataVO item : vmiDataList) { Map map = new HashMap<>(); map.put("index", index); map.put("drawingNo", item.getDrawingNo()); map.put("name", item.getName()); map.put("quantity", item.getQuantity()); map.put("material", item.getMaterial()); map.put("unitWeight", item.getUnitWeight()); map.put("totalWeight", item.getTotalWeight()); map.put("remark", item.getRemark()); map.put("batchQuantity", item.getBatchQuantity()); mapList.add(map); index++; } return mapList; } /** * 转换ElecOutDataVO为Map列表(用于模板) */ private List> convertElecOutDataToMapList(List elecOutDataList) { List> mapList = new ArrayList<>(); int index = 1; for (ElecOutDataVO item : elecOutDataList) { Map map = new HashMap<>(); map.put("index", index); map.put("drawingNo", item.getDrawingNo()); map.put("name", item.getName()); map.put("quantity", item.getQuantity()); map.put("material", item.getMaterial()); map.put("unitWeight", item.getUnitWeight()); map.put("totalWeight", item.getTotalWeight()); map.put("remark", item.getRemark()); map.put("batchQuantity", item.getBatchQuantity()); mapList.add(map); index++; } return mapList; } /** * 转换SupProvidDataVO为Map列表(用于模板) */ private List> convertSupProvidDataToMapList(List supplierDataList) { List> mapList = new ArrayList<>(); int index = 1; for (SupProvidDataVO item : supplierDataList) { Map map = new HashMap<>(); map.put("index", index); map.put("drawingNo", item.getDrawingNo()); map.put("name", item.getName()); map.put("quantity", item.getQuantity()); map.put("material", item.getMaterial()); map.put("unitWeight", item.getUnitWeight()); map.put("totalWeight", item.getTotalWeight()); map.put("remark", item.getRemark()); map.put("batchQuantity", item.getBatchQuantity()); mapList.add(map); index++; } return mapList; } /** * 转换EVOProductsDataVO为Map列表(用于模板) */ private List> convertEVOProductsDataToMapList(List evoProductsDataList) { List> mapList = new ArrayList<>(); int index = 1; for (EVOProductsDataVO item : evoProductsDataList) { Map map = new HashMap<>(); map.put("index", index); map.put("drawingNo", item.getDrawingNo()); map.put("name", item.getName()); map.put("quantity", item.getQuantity()); map.put("material", item.getMaterial()); map.put("unitWeight", item.getUnitWeight()); map.put("totalWeight", item.getTotalWeight()); map.put("remark", item.getRemark()); map.put("batchQuantity", item.getBatchQuantity()); mapList.add(map); index++; } return mapList; } private String formatDate(Date date) { return date == null ? "" : DATE_FORMAT.format(date); } /** * 转换工艺VO为Map列表(用于模板) */ private List> convertRouteDataToMapList(List routeDataList) { List> mapList = new ArrayList<>(); int index = 1; for (ProcessRoute item : routeDataList) { Map map = new HashMap<>(); map.put("index", index); map.put("routeDescription", item.getRouteDescription()); // 生产令号 map.put("materialCode", item.getMaterialCode()); // 物料编码 map.put("materialName", item.getMaterialName()); // 物料名称 map.put("material", item.getMaterial()); // 材质 map.put("discWeight", item.getDiscWeight()); // 单重KG map.put("processNo", item.getProcessNo()); // 工序号 map.put("workCenter", item.getWorkCenter()); // 工作中心 map.put("processName", item.getProcessName()); // 工序名称 map.put("processDescription", item.getProcessDescription()); // 工序说明 map.put("processControl", item.getProcessControl()); // 工序控制 map.put("activityDuration", item.getActivityDuration()); // 活动时长 map.put("activityUnit", item.getActivityUnit()); // 活动单位 map.put("unitQuantity", item.getUnitQuantity()); // 单台数量 map.put("batchQuantity", item.getBatchQuantity()); // 本批数量 map.put("firstBatchQuantity", item.getFirstBatchQuantity()); // 首批数量 map.put("planStartTime", formatDate(item.getPlanStartTime())); map.put("planEndTime", formatDate(item.getPlanEndTime())); map.put("xuStartTime", formatDate(item.getXuStartTime())); map.put("xuEndTime", formatDate(item.getXuEndTime())); mapList.add(map); index++; } return mapList; } @SaCheckPermission("system:orderPro:exportRoute2") @Log(title = "下载工艺生产表2", businessType = BusinessType.EXPORT) @GetMapping ("/exportRoute2") public void exportRoute2(@RequestParam("id") Long id, HttpServletResponse response) { try { ProcessOrderPro orderPro = processOrderProMapper.selectById(id); // 下载Excel文件 SmbUtil.downloadExcelFiles(orderPro.getProductionOrderNo()); // 构建文件路径 String excelName = "D:\\file\\" + orderPro.getProductionOrderNo() + "汇总表.xlsx"; String rawDataFile = "D:\\file\\RawDataTable.xlsx"; File file = new File(excelName); if (!file.exists()) { throw new ServiceException("项目 " + orderPro.getProductionOrderNo() + " 未出图"); } // 1. 读取第一个sheet的数据list - 使用POI直接读取以保留空格 List allDataList = readExcelWithPOI(excelName,orderPro.getProductionOrderNo()); List routeList = readExcelPOIRoute(excelName,orderPro.getProductionOrderNo()); List routes = new ArrayList<>(); List> kingdeeBomRows = new ArrayList<>(); for (ProcessRoute base : routeList) { String materialCode = base.getMaterialCode(); //如果项目零号是CP 跳过安全库存物料 LambdaQueryWrapper wr = new LambdaQueryWrapper<>(); wr.eq(SafetyStock::getMaterialCode, materialCode); SafetyStock safetyStock = stockMapper.selectOne(wr); if (safetyStock != null && base.getRouteDescription().startsWith("CP")) { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); routes.add(item); continue; } if (StringUtils.isBlank(materialCode)) { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); routes.add(item); continue; } String bomversion = JdUtil.readGetTheLatestVersion(materialCode); List bomItems = StringUtils.isNotBlank(bomversion) ? JdUtil.getMaterialUseXByVer(bomversion) : Collections.emptyList(); List routeGuDing = JdUtil.getRouteGuDing(materialCode); if (routeGuDing != null && !routeGuDing.isEmpty()) { routeGuDing.stream().forEach(r -> { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); // 不写入BOM字段,保持纯工艺数据行 item.setProcessNo(r.getProcessNo()); item.setWorkCenter(r.getWorkCenter()); item.setProcessName(r.getProcessName()); item.setProcessDescription(r.getProcessDescription()); item.setProcessControl(r.getProcessControl()); item.setActivityDuration(r.getActivityDuration()); item.setActivityUnit(r.getActivityUnit()); routes.add(item); }); } else { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); // 不写入BOM字段,保持纯工艺数据行 routes.add(item); } if (bomItems != null && !bomItems.isEmpty()) { for (MaterialUseDTO b : bomItems) { Map bomMap = new HashMap<>(); bomMap.put("routeDescription", base.getRouteDescription()); bomMap.put("materialCode", base.getMaterialCode()); bomMap.put("materialName", base.getMaterialName()); bomMap.put("material", base.getMaterial()); bomMap.put("discWeight", base.getDiscWeight()); bomMap.put("rawMaterialCode", b.getMaterialCode()); bomMap.put("rawMaterialName", b.getMaterialName()); bomMap.put("bomMaterial", b.getCaizhi()); bomMap.put("bomDanZhong", b.getDanzhong()); if (b.getChildUnit().equals("根")) { bomMap.put("discUsage", (b.getFenzi() != null && b.getFenmu() != null) ? (b.getFenzi() + "/" + b.getFenmu()) : null); } else { bomMap.put("discUsage", (b.getFenzi() != null && b.getFenmu() != null) ? b.getFenzi() : null); } bomMap.put("bomUnit", b.getChildUnit()); kingdeeBomRows.add(bomMap); } } } // 用生成的 routes 替换原始 routeList,保持原序展开后的结构用于后续导出 routeList = routes; // 2. 读取原始表数据 List rawDataList = readRawDataTable(rawDataFile); // 3. 数据分类处理 List vmiList = new ArrayList<>(); // 009开头 List elecOutList = new ArrayList<>(); // 两个空格和017开头 List supplierList = new ArrayList<>(); // 甲供件 List evoProductsList = new ArrayList<>(); // 伊特 List processDataList = new ArrayList<>(); // 工艺数据(剩余数据) // 分类逻辑 for (ProductionOrderVo item : allDataList) { String materialCode = item.getDrawingNo(); String remark = item.getRemark(); // 使用备注字段 // 009开头的加入VMI表 if (materialCode != null) { String drawingNo = item.getDrawingNo(); String drawingName = item.getDrawingName(); if (drawingName != null) { ImMaterial material = imMaterialService.selectByCodeAndName(drawingNo, drawingName); if (material != null) { //判断是否是VMI物料 if ("true".equals(material.getClassificationName())) { // 检查是否已存在相同的DrawingNo boolean found = false; for (VMIDataVO existingVmi : vmiList) { if (drawingNo.equals(existingVmi.getDrawingNo())) { // 将数量和批次数量相加 Integer currentQuantity = existingVmi.getQuantity() != null ? existingVmi.getQuantity() : 0; Integer itemQuantity = item.getQuantity() != null ? item.getQuantity().intValue() : 0; existingVmi.setQuantity(currentQuantity + itemQuantity); Integer currentBatchQuantity = existingVmi.getBatchQuantity() != null ? existingVmi.getBatchQuantity() : 0; Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.parseInt(item.getBatchQuantity()) : 0; existingVmi.setBatchQuantity(currentBatchQuantity + itemBatchQuantity); found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的VMI数据 if (!found) { VMIDataVO vmiData = convertToVMIDataVO(item); vmiList.add(vmiData); } continue; // 已分类,跳过后续检查 } } } // 电气外包分类条件:物料编码开头空格/特定前缀 或 备注包含"外购" if (materialCode.startsWith(" ") || materialCode.startsWith("009301") || materialCode.startsWith("009999") || materialCode.startsWith("017003") || materialCode.startsWith("017002") || materialCode.startsWith("009001") || materialCode.startsWith("009081") || (remark != null && remark.contains("外购"))) { // 过滤安全库存:如果属于安全库存,则进入工艺数据列表 Boolean isSafeStock = safetyStockService.isSafeCode(materialCode.trim()); if (isSafeStock) { // 属于安全库存,添加到工艺数据列表 processDataList.add(item); continue; // 已分类,跳过后续检查 } else { // 不属于安全库存,检查是否已存在相同的DrawingNo boolean found = false; for (ElecOutDataVO existingElec : elecOutList) { if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingElec.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingElec.getQuantity() != null ? existingElec.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; Double newQuantity = currentQuantity + itemQuantity; existingElec.setQuantity(newQuantity); // 批次数量相加(String类型) String currentBatchQuantity = existingElec.getBatchQuantity() != null ? (existingElec.getBatchQuantity()).toString() : "0"; String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; try { Integer currentBatch = Integer.valueOf(currentBatchQuantity); Integer itemBatch = Integer.valueOf(itemBatchQuantity); String newBatchQuantity = String.valueOf(currentBatch + itemBatch); existingElec.setBatchQuantity(Integer.valueOf(newBatchQuantity)); } catch (NumberFormatException e) { // 如果转换失败,保持原值 existingElec.setBatchQuantity(Integer.valueOf(currentBatchQuantity)); } found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的电气外包数据 if (!found) { ElecOutDataVO elecData = convertToElecOutDataVO(item); elecOutList.add(elecData); } continue; // 已分类,跳过后续检查 } } // 备注是甲供件的 if (remark != null && remark.contains("甲供件")) { SupProvidDataVO supplierData = convertToSupProvidDataVO(item); supplierList.add(supplierData); continue; // 已分类,跳过后续检查 } // 备注是伊特 if (remark != null && remark.contains("伊特")) { EVOProductsDataVO evoData = convertToEVOProductsDataVO(item); evoProductsList.add(evoData); continue; // 已分类,跳过后续检查 } } // 检查是否已存在相同的DrawingNo boolean found = false; for (ProductionOrderVo existingProcess : processDataList) { if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingProcess.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingProcess.getQuantity() != null ? existingProcess.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; Double newQuantity = currentQuantity + itemQuantity; existingProcess.setQuantity(newQuantity); // 批次数量相加(String类型) String currentBatchQuantity = existingProcess.getBatchQuantity() != null ? existingProcess.getBatchQuantity() : "0"; String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; try { Integer currentBatch = Integer.valueOf(currentBatchQuantity); Integer itemBatch = Integer.valueOf(itemBatchQuantity); String newBatchQuantity = String.valueOf(currentBatch + itemBatch); existingProcess.setBatchQuantity(newBatchQuantity); } catch (NumberFormatException e) { // 如果转换失败,保持原值 existingProcess.setBatchQuantity(currentBatchQuantity); } found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的工艺数据 if (!found) { processDataList.add(item); } } // 使用Excel模板文件 String templatePath = "jpg/生产及工艺计划模版.xlsx"; String outputPath = "D:/file/" + orderPro.getProductionOrderNo() + "生产及工艺计划表.xlsx"; // 准备模板数据 Map staticDataMap = new HashMap<>(); staticDataMap.put("productionOrderNo", orderPro.getProductionOrderNo()); staticDataMap.put("productionName", orderPro.getProductionName()); // 准备动态数据映射 List dynamicDataMappingList = new ArrayList<>(); // 添加生产订单数据 if (!allDataList.isEmpty()) { List> productionDataList = convertProductionOrderToMapList(allDataList, orderPro.getProductionOrderNo()); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProductionOrder", productionDataList)); } // 添加工艺数据(第七个sheet:工艺及生产计划表) if (!processDataList.isEmpty()) { List> processDataMapList = convertProductionOrderToMapList(processDataList, orderPro.getProductionOrderNo()); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProcessData", processDataMapList)); } // 添加VMI数据 if (!vmiList.isEmpty()) { List> vmiDataList = convertVMIDataToMapList(vmiList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("VMIDataVO", vmiDataList)); } // 添加电气外购数据 if (!elecOutList.isEmpty()) { List> elecDataList = convertElecOutDataToMapList(elecOutList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ElecOutDataVO", elecDataList)); } // 添加BOM数据 if (!rawDataList.isEmpty()) { List> bomDataList = convertBomDataToMapList(rawDataList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("BomDataVO", bomDataList)); } // 添加甲供件数据 if (!supplierList.isEmpty()) { List> supplierDataList = convertSupProvidDataToMapList(supplierList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("SupProvidDataVO", supplierDataList)); } // 添加伊特产品数据 if (!evoProductsList.isEmpty()) { List> evoDataList = convertEVOProductsDataToMapList(evoProductsList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("EVOProductsDataVO", evoDataList)); } if (!kingdeeBomRows.isEmpty()) { List> bomDataList2 = convertKingdeeBomToMapList(kingdeeBomRows); // 合并到 RouteBomData 中统一按物料分组导出(方案A) } // 添加伊特产品数据 if (!routeList.isEmpty()) { // 合并到 RouteBomData 中统一按物料分组导出(方案A) } // 方案A:合并工艺与BOM为单一数据集并按物料分组 if (!routeList.isEmpty() || !kingdeeBomRows.isEmpty()) { List> routeBomDataList = convertRouteBomToMapList(routeList, kingdeeBomRows); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("RouteBomData", routeBomDataList)); } // 使用模板导出Excel ExcelTemplateProc.doExportExcelByTemplateProc(templatePath, outputPath, staticDataMap, dynamicDataMappingList); // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(orderPro.getProductionOrderNo() + "_分类BOM表", "UTF-8") .replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 将生成的文件写入响应 File outputFile = new File(outputPath); if (outputFile.exists()) { try (FileInputStream fis = new FileInputStream(outputFile); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[8192]; int length; while ((length = fis.read(buffer)) > 0) { os.write(buffer, 0, length); } os.flush(); } // 删除临时文件 outputFile.delete(); } } catch (Exception e) { log.error("导出分类BOM表失败", e); throw new RuntimeException("导出失败: " + e.getMessage()); } } /** * 合并工艺路线与金蝶BOM行,按物料编码分组生成统一的 RouteBomData 列表 * 每条记录包含两类:rowType=route(工艺行)或 rowType=bom(BOM行),以便模板在同一块中连续展示 */ private List> convertRouteBomToMapList(List routeDataList, List> kingdeeBomRows) { Map>> grouped = new LinkedHashMap<>(); // 追加BOM数据到对应物料分组;若该物料此前未出现,则在最后新增一个分组 for (Map row : kingdeeBomRows) { String mcode = Objects.toString(row.get("materialCode"), "").trim(); String childCode = Objects.toString(row.get("rawMaterialCode"), "").trim(); String parentMaterial = Objects.toString(row.get("material"), "").trim(); if (mcode.isEmpty() || childCode.isEmpty()) { continue; } /* if ("总装部件".equals(parentMaterial)) { continue; }*/ grouped.computeIfAbsent(mcode, k -> new ArrayList<>()); Map map = new HashMap<>(); map.put("rowType", "bom"); map.put("routeDescription", Objects.toString(row.get("routeDescription"), "")); map.put("materialCode", mcode); map.put("materialName", Objects.toString(row.get("materialName"), "")); map.put("material", Objects.toString(row.get("material"), "")); map.put("discWeight", Objects.toString(row.get("discWeight"), "")); // 工艺占位(当前行类型为BOM,工艺字段置空) map.put("processNo", ""); map.put("workCenter", ""); map.put("processName", ""); map.put("processDescription", ""); map.put("processControl", ""); map.put("activityDuration", ""); map.put("activityUnit", ""); map.put("unitQuantity", ""); map.put("batchQuantity", ""); map.put("firstBatchQuantity", ""); map.put("planStartTime", ""); map.put("planEndTime", ""); map.put("xuStartTime", ""); map.put("xuEndTime", ""); // BOM具体字段 map.put("rawMaterialCode", childCode); map.put("rawMaterialName", Objects.toString(row.get("rawMaterialName"), "")); map.put("bomMaterial", Objects.toString(row.get("bomMaterial"), "")); map.put("bomDanZhong", Objects.toString(row.get("bomDanZhong"), "")); map.put("discUsage", Objects.toString(row.get("discUsage"), "")); map.put("bomUnit", Objects.toString(row.get("bomUnit"), "")); grouped.get(mcode).add(map); } // 先按工艺数据建立分组与顺序 for (ProcessRoute item : routeDataList) { String mcode = Objects.toString(item.getMaterialCode(), "").trim(); boolean noRoute = Objects.toString(item.getMaterialCode(), "").trim().isEmpty() && Objects.toString(item.getMaterialName(), "").trim().isEmpty() && Objects.toString(item.getMaterial(), "").trim().isEmpty() && item.getDiscWeight() == null && item.getProcessNo() == null && Objects.toString(item.getWorkCenter(), "").trim().isEmpty() && Objects.toString(item.getProcessName(), "").trim().isEmpty() && Objects.toString(item.getProcessDescription(), "").trim().isEmpty() && Objects.toString(item.getProcessControl(), "").trim().isEmpty() && item.getActivityDuration() == null && Objects.toString(item.getActivityUnit(), "").trim().isEmpty() && item.getUnitQuantity() == null && item.getBatchQuantity() == null && Objects.toString(item.getFirstBatchQuantity(), "").trim().isEmpty() && item.getPlanStartTime() == null && item.getPlanEndTime() == null && item.getXuStartTime() == null && item.getXuEndTime() == null; if (noRoute) { continue; } grouped.computeIfAbsent(mcode, k -> new ArrayList<>()); Map map = new HashMap<>(); map.put("rowType", "route"); map.put("routeDescription", Objects.toString(item.getRouteDescription(), "")); map.put("materialCode", mcode); map.put("materialName", Objects.toString(item.getMaterialName(), "")); map.put("material", Objects.toString(item.getMaterial(), "")); map.put("discWeight", Objects.toString(item.getDiscWeight(), "")); map.put("processNo", item.getProcessNo()); map.put("workCenter", Objects.toString(item.getWorkCenter(), "")); map.put("processName", Objects.toString(item.getProcessName(), "")); map.put("processDescription", Objects.toString(item.getProcessDescription(), "")); map.put("processControl", Objects.toString(item.getProcessControl(), "")); map.put("activityDuration", Objects.toString(item.getActivityDuration(), "")); map.put("activityUnit", Objects.toString(item.getActivityUnit(), "")); map.put("unitQuantity", item.getUnitQuantity()); map.put("batchQuantity", item.getBatchQuantity()); map.put("firstBatchQuantity", Objects.toString(item.getFirstBatchQuantity(), "")); map.put("planStartTime", formatDate(item.getPlanStartTime())); map.put("planEndTime", formatDate(item.getPlanEndTime())); map.put("xuStartTime", formatDate(item.getXuStartTime())); map.put("xuEndTime", formatDate(item.getXuEndTime())); // BOM占位(当前行类型为工艺,BOM字段置空) map.put("rawMaterialCode", ""); map.put("rawMaterialName", ""); map.put("bomMaterial", ""); map.put("bomDanZhong", ""); map.put("discUsage", ""); map.put("bomUnit", ""); grouped.get(mcode).add(map); } // 展平为顺序列表并补齐序号 List> result = new ArrayList<>(); int index = 1; for (Map.Entry>> entry : grouped.entrySet()) { for (Map m : entry.getValue()) { m.put("index", index++); result.add(m); } } return result; } private List> convertKingdeeBomToMapList(List> kingdeeBomRows) { List> mapList = new ArrayList<>(); int index = 1; for (Map row : kingdeeBomRows) { Map map = new HashMap<>(); map.put("index", index); map.put("routeDescription", row.get("routeDescription")); map.put("materialCode", row.get("materialCode")); map.put("materialName", row.get("materialName")); map.put("material", row.get("material")); map.put("discWeight", row.get("discWeight")); map.put("rawMaterialCode", row.get("rawMaterialCode")); map.put("rawMaterialName", row.get("rawMaterialName")); map.put("bomMaterial", row.get("bomMaterial")); map.put("bomDanZhong", row.get("bomDanZhong")); map.put("discUsage", row.get("discUsage")); map.put("bomUnit", row.get("bomUnit")); mapList.add(map); index++; } return mapList; } @SaCheckPermission("system:orderPro:exportRoute3") @Log(title = "下载工艺生产表3", businessType = BusinessType.EXPORT) @GetMapping ("/exportRoute3") public void exportRoute3(@RequestParam("id") Long id, HttpServletResponse response) { try { ProcessOrderPro orderPro = processOrderProMapper.selectById(id); // 下载Excel文件 // 1. 解析年份 String productionCode = orderPro.getProductionOrderNo(); String year = "2025"; // 默认 if (productionCode != null) { if (productionCode.startsWith("EY")) { if (productionCode.length() >= 4) { try { Integer.parseInt(productionCode.substring(2, 4)); year = "20" + productionCode.substring(2, 4); } catch (Exception e) {} } } else { int firstDash = productionCode.indexOf("-"); if (firstDash >= 0 && firstDash + 3 <= productionCode.length()) { try { Integer.parseInt(productionCode.substring(firstDash + 1, firstDash + 3)); year = "20" + productionCode.substring(firstDash + 1, firstDash + 3); } catch (Exception e) {} } } } // 2. 构建FTP路径和本地路径 String ftpPath = "/" + year + "/" + productionCode; String localPath = "D:\\file\\"; // 3. 下载文件 (只下载汇总表.xlsx) FtpUtil.downloadFtpDirectoryFiles("192.168.5.18", "admin", "hbyt2025", 21, ftpPath, localPath, "汇总表.xlsx"); // 构建文件路径 String excelName = "D:\\file\\" + orderPro.getProductionOrderNo() + "汇总表.xlsx"; String rawDataFile = "D:\\file\\RawDataTable.xlsx"; File file = new File(excelName); if (!file.exists() || file.length() == 0) { // 如果文件不存在或为空(下载失败),抛出业务异常 throw new ServiceException("项目 " + orderPro.getProductionOrderNo() + " 汇总表下载失败或不存在"); } // 1. 读取第一个sheet的数据list - 使用POI直接读取以保留空格 List allDataList = readExcelWithPOI(excelName,orderPro.getProductionOrderNo()); List routeList = readExcelPOIRoute(excelName,orderPro.getProductionOrderNo()); List routeList2 = processRouteService.selectByProjectNumber(orderPro.getProductionOrderNo()); // 将routeList2中有但routeList中没有的记录添加到routeList if (routeList2 != null && !routeList2.isEmpty()) { if (routeList == null) { routeList = new ArrayList<>(); } // 使用 Set 存储 routeList 中已有的 materialCode,用于快速排重 Set existingMaterialCodes = routeList.stream() .map(ProcessRoute::getMaterialCode) .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); for (ProcessRoute route : routeList2) { if (StringUtils.isNotBlank(route.getMaterialCode()) && !existingMaterialCodes.contains(route.getMaterialCode())) { routeList.add(route); existingMaterialCodes.add(route.getMaterialCode()); // 避免重复添加 } } } List routes = new ArrayList<>(); List> kingdeeBomRows = new ArrayList<>(); for (ProcessRoute base : routeList) { String processDescription = base.getRouteDescription(); String materialCode = base.getMaterialCode(); String materialName = base.getMaterialName(); //如果项目零号是CP 跳过安全库存物料 LambdaQueryWrapper wr = new LambdaQueryWrapper<>(); wr.eq(SafetyStock::getMaterialCode, materialCode); SafetyStock safetyStock = stockMapper.selectOne(wr); if (safetyStock != null && base.getRouteDescription().startsWith("CP")) { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); item.setXuEndTime(base.getXuEndTime()); item.setXuStartTime(base.getXuStartTime()); routes.add(item); continue; } if (StringUtils.isBlank(materialCode)) { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); item.setXuEndTime(base.getXuEndTime()); item.setXuStartTime(base.getXuStartTime()); routes.add(item); continue; } //List routeGuDing = JdUtil.getRouteGuDing(materialCode);processRouteService List bomItems = processRouteService.getProcessMaterialList(materialCode, materialName, processDescription); List routeGuDing = processRouteService.getProcessRoutesByOrder(processDescription, materialCode); if (routeGuDing != null && !routeGuDing.isEmpty()) { routeGuDing.forEach(r -> { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); // 不写入BOM字段,保持纯工艺数据行 item.setProcessNo(r.getProcessNo()); item.setWorkCenter(r.getWorkCenter()); item.setProcessName(r.getProcessName()); item.setProcessDescription(r.getProcessDescription()); item.setProcessControl(r.getProcessControl()); item.setActivityDuration(r.getActivityDuration()); item.setActivityUnit(r.getActivityUnit()); item.setXuStartTime(r.getXuStartTime()); item.setXuEndTime(r.getXuEndTime()); routes.add(item); }); } else { ProcessRoute item = new ProcessRoute(); item.setRouteDescription(base.getRouteDescription()); item.setMaterialCode(base.getMaterialCode()); item.setMaterialName(base.getMaterialName()); item.setMaterial(base.getMaterial()); item.setDiscWeight(base.getDiscWeight()); item.setUnitQuantity(base.getUnitQuantity()); item.setBatchQuantity(base.getBatchQuantity()); item.setXuStartTime(base.getXuStartTime()); item.setXuEndTime(base.getXuEndTime()); // 不写入BOM字段,保持纯工艺数据行 routes.add(item); } if (bomItems != null && !bomItems.isEmpty()) { for (MaterialBom b : bomItems) { Map bomMap = new HashMap<>(); bomMap.put("routeDescription", base.getRouteDescription()); bomMap.put("materialCode", base.getMaterialCode()); bomMap.put("materialName", base.getMaterialName()); bomMap.put("material", base.getMaterial()); bomMap.put("discWeight", base.getDiscWeight()); bomMap.put("rawMaterialCode", b.getMaterialCode()); bomMap.put("rawMaterialName", b.getMaterialName()); bomMap.put("bomMaterial", b.getMaterialType()); bomMap.put("bomDanZhong", null); if ("根".equals(b.getUnit())) { if (b.getQuantity() != null && b.getQuantity().contains("/")) { String[] arr = b.getQuantity().split("/"); bomMap.put("discUsage", Double.parseDouble(arr[0]) / Double.parseDouble(arr[1])); } else { bomMap.put("discUsage", null); } } else if ("mm".equals(b.getUnit())) { b.setUnit("m"); bomMap.put("discUsage", b.getQuantity()); } else { bomMap.put("discUsage", b.getQuantity()); } bomMap.put("bomUnit", b.getUnit()); kingdeeBomRows.add(bomMap); } } } // 用生成的 routes 替换原始 routeList,保持原序展开后的结构用于后续导出 routeList = routes; // 2. 读取原始表数据 List rawDataList = readRawDataTable(rawDataFile); // 3. 数据分类处理 List vmiList = new ArrayList<>(); // 009开头 List elecOutList = new ArrayList<>(); // 两个空格和017开头 List supplierList = new ArrayList<>(); // 甲供件 List evoProductsList = new ArrayList<>(); // 伊特 List processDataList = new ArrayList<>(); // 工艺数据(剩余数据) // 分类逻辑 for (ProductionOrderVo item : allDataList) { String materialCode = item.getDrawingNo(); String remark = item.getRemark(); // 使用备注字段 // 009开头的加入VMI表 if (materialCode != null) { String drawingNo = item.getDrawingNo(); String drawingName = item.getDrawingName(); if (drawingName != null) { ImMaterial material = imMaterialService.selectByCodeAndName(drawingNo, drawingName); if (material != null) { //判断是否是VMI物料 if ("true".equals(material.getClassificationName())) { // 检查是否已存在相同的DrawingNo boolean found = false; for (VMIDataVO existingVmi : vmiList) { if (drawingNo.equals(existingVmi.getDrawingNo())) { // 将数量和批次数量相加 Integer currentQuantity = existingVmi.getQuantity() != null ? existingVmi.getQuantity() : 0; Integer itemQuantity = item.getQuantity() != null ? item.getQuantity().intValue() : 0; existingVmi.setQuantity(currentQuantity + itemQuantity); Integer currentBatchQuantity = existingVmi.getBatchQuantity() != null ? existingVmi.getBatchQuantity() : 0; Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.parseInt(item.getBatchQuantity()) : 0; existingVmi.setBatchQuantity(currentBatchQuantity + itemBatchQuantity); found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的VMI数据 if (!found) { VMIDataVO vmiData = convertToVMIDataVO(item); vmiList.add(vmiData); } continue; // 已分类,跳过后续检查 } } } // 电气外包分类条件:物料编码开头空格/特定前缀 或 备注包含"外购" if (materialCode.startsWith(" ") || materialCode.startsWith("009301") || materialCode.startsWith("009999") || materialCode.startsWith("017003") || materialCode.startsWith("017002") || materialCode.startsWith("009001") || materialCode.startsWith("009081") || (remark != null && remark.contains("外购"))) { // 过滤安全库存:如果属于安全库存,则进入工艺数据列表 Boolean isSafeStock = safetyStockService.isSafeCode(materialCode.trim()); if (isSafeStock) { // 属于安全库存,添加到工艺数据列表 processDataList.add(item); continue; // 已分类,跳过后续检查 } else { // 不属于安全库存,检查是否已存在相同的DrawingNo boolean found = false; for (ElecOutDataVO existingElec : elecOutList) { if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingElec.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingElec.getQuantity() != null ? existingElec.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; Double newQuantity = currentQuantity + itemQuantity; existingElec.setQuantity(newQuantity); // 批次数量相加(String类型) String currentBatchQuantity = existingElec.getBatchQuantity() != null ? (existingElec.getBatchQuantity()).toString() : "0"; String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; try { Integer currentBatch = Integer.valueOf(currentBatchQuantity); Integer itemBatch = Integer.valueOf(itemBatchQuantity); String newBatchQuantity = String.valueOf(currentBatch + itemBatch); existingElec.setBatchQuantity(Integer.valueOf(newBatchQuantity)); } catch (NumberFormatException e) { // 如果转换失败,保持原值 existingElec.setBatchQuantity(Integer.valueOf(currentBatchQuantity)); } found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的电气外包数据 if (!found) { ElecOutDataVO elecData = convertToElecOutDataVO(item); elecOutList.add(elecData); } continue; // 已分类,跳过后续检查 } } // 备注是甲供件的 if (remark != null && remark.contains("甲供件")) { SupProvidDataVO supplierData = convertToSupProvidDataVO(item); supplierList.add(supplierData); continue; // 已分类,跳过后续检查 } // 备注是伊特 if (remark != null && remark.contains("伊特")) { EVOProductsDataVO evoData = convertToEVOProductsDataVO(item); evoProductsList.add(evoData); continue; // 已分类,跳过后续检查 } } // 检查是否已存在相同的DrawingNo boolean found = false; for (ProductionOrderVo existingProcess : processDataList) { if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingProcess.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingProcess.getQuantity() != null ? existingProcess.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; Double newQuantity = currentQuantity + itemQuantity; existingProcess.setQuantity(newQuantity); // 批次数量相加(String类型) String currentBatchQuantity = existingProcess.getBatchQuantity() != null ? existingProcess.getBatchQuantity() : "0"; String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; try { Integer currentBatch = Integer.valueOf(currentBatchQuantity); Integer itemBatch = Integer.valueOf(itemBatchQuantity); String newBatchQuantity = String.valueOf(currentBatch + itemBatch); existingProcess.setBatchQuantity(newBatchQuantity); } catch (NumberFormatException e) { // 如果转换失败,保持原值 existingProcess.setBatchQuantity(currentBatchQuantity); } found = true; break; } } // 如果没有找到相同的DrawingNo,则添加新的工艺数据 if (!found) { processDataList.add(item); } } // 使用Excel模板文件 String templatePath = "jpg/生产及工艺计划模版.xlsx"; String outputPath = "D:/file/" + orderPro.getProductionOrderNo() + "生产及工艺计划表.xlsx"; // 准备模板数据 Map staticDataMap = new HashMap<>(); staticDataMap.put("productionOrderNo", orderPro.getProductionOrderNo()); staticDataMap.put("productionName", orderPro.getProductionName()); // 准备动态数据映射 List dynamicDataMappingList = new ArrayList<>(); // 添加生产订单数据 if (!allDataList.isEmpty()) { List> productionDataList = convertProductionOrderToMapList(allDataList, orderPro.getProductionOrderNo()); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProductionOrder", productionDataList)); } // 添加工艺数据(第七个sheet:工艺及生产计划表) if (!processDataList.isEmpty()) { List> processDataMapList = convertProductionOrderToMapList(processDataList, orderPro.getProductionOrderNo()); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProcessData", processDataMapList)); } // 添加VMI数据 if (!vmiList.isEmpty()) { List> vmiDataList = convertVMIDataToMapList(vmiList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("VMIDataVO", vmiDataList)); } // 添加电气外购数据 if (!elecOutList.isEmpty()) { List> elecDataList = convertElecOutDataToMapList(elecOutList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ElecOutDataVO", elecDataList)); } // 添加BOM数据 if (!rawDataList.isEmpty()) { List> bomDataList = convertBomDataToMapList(rawDataList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("BomDataVO", bomDataList)); } // 添加甲供件数据 if (!supplierList.isEmpty()) { List> supplierDataList = convertSupProvidDataToMapList(supplierList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("SupProvidDataVO", supplierDataList)); } // 添加伊特产品数据 if (!evoProductsList.isEmpty()) { List> evoDataList = convertEVOProductsDataToMapList(evoProductsList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("EVOProductsDataVO", evoDataList)); } if (!kingdeeBomRows.isEmpty()) { List> bomDataList2 = convertKingdeeBomToMapList(kingdeeBomRows); // 合并到 RouteBomData 中统一按物料分组导出(方案A) } // 添加伊特产品数据 if (!routeList.isEmpty()) { // 合并到 RouteBomData 中统一按物料分组导出(方案A) } // 方案A:合并工艺与BOM为单一数据集并按物料分组 if (!routeList.isEmpty() || !kingdeeBomRows.isEmpty()) { List> routeBomDataList = convertRouteBomToMapList(routeList, kingdeeBomRows); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("RouteBomData", routeBomDataList)); } // 使用模板导出Excel ExcelTemplateProc.doExportExcelByTemplateProc(templatePath, outputPath, staticDataMap, dynamicDataMappingList); // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(orderPro.getProductionOrderNo() + "_分类BOM表", "UTF-8") .replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 将生成的文件写入响应 File outputFile = new File(outputPath); if (outputFile.exists()) { try (FileInputStream fis = new FileInputStream(outputFile); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[8192]; int length; while ((length = fis.read(buffer)) > 0) { os.write(buffer, 0, length); } os.flush(); } // 删除临时文件 outputFile.delete(); } } catch (Exception e) { log.error("导出分类BOM表失败", e); throw new RuntimeException("导出失败: " + e.getMessage()); } } }