From 3318f3e9885f9c37e975e884a9facbc13fc74c6d Mon Sep 17 00:00:00 2001 From: tzy Date: Sun, 14 Sep 2025 11:11:38 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=91=E8=9D=B6=E8=B5=84=E4=BA=A7=E5=8D=A1?= =?UTF-8?q?=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/ruoyi/RuoYiApplication.java | 1 - .../src/main/resources/application-dev.yml | 6 +- .../src/main/resources/application-prod.yml | 6 +- .../src/main/resources/application.yml | 7 +- .../java/com/ruoyi/RuoYiTestApplication.java | 21 + .../java/com/ruoyi/test/ExcelSummaryTest.java | 2 +- ruoyi-common/pom.xml | 5 + .../convert/KeepSpaceStringConverter.java | 106 ++ .../ruoyi/common/poi/DynamicDataMapping.java | 61 + .../ruoyi/common/poi/ExcelTemplateProc.java | 455 ++++++ .../poi/ExportExcelByTemplateUtils.java | 65 + .../com/ruoyi/common/poi/MapObjectUtil.java | 89 ++ .../com/ruoyi/common/utils/file/SmbUtil.java | 30 +- .../com/ruoyi/common/utils/poi/ExcelUtil.java | 8 +- .../controller/EleMaterialsController.java | 46 + .../controller/ImMaterialController.java | 2 +- .../controller/ProcessOrderProController.java | 1358 +++++++++++++---- .../controller/ProcessRouteController.java | 2 +- .../com/ruoyi/system/domain/ImMaterial.java | 1 + .../com/ruoyi/system/domain/vo/BomDataVO.java | 55 + .../system/domain/vo/EVOProductsDataVO.java | 42 + .../ruoyi/system/domain/vo/ElecOutDataVO.java | 41 + .../domain/vo/JdKingdeeAssetCardVo.java | 6 +- .../system/domain/vo/ProductionOrderVo.java | 18 +- .../system/domain/vo/SupProvidDataVO.java | 40 + .../com/ruoyi/system/domain/vo/VMIDataVO.java | 41 + .../system/service/ISafetyStockService.java | 2 + .../service/impl/ImMaterialServiceImpl.java | 3 +- .../service/impl/SafetyStockServiceImpl.java | 15 + .../main/resources/EXCEL模板/生产BOM表1.xlsx | Bin 10081 -> 0 bytes .../EXCEL模板/生产及工艺计划模版.xlsx | Bin 0 -> 18631 bytes 31 files changed, 2166 insertions(+), 368 deletions(-) create mode 100644 ruoyi-admin/src/test/java/com/ruoyi/RuoYiTestApplication.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/convert/KeepSpaceStringConverter.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/poi/DynamicDataMapping.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/poi/ExportExcelByTemplateUtils.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/poi/MapObjectUtil.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BomDataVO.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EVOProductsDataVO.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SupProvidDataVO.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VMIDataVO.java delete mode 100644 ruoyi-system/src/main/resources/EXCEL模板/生产BOM表1.xlsx create mode 100644 ruoyi-system/src/main/resources/EXCEL模板/生产及工艺计划模版.xlsx diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java index 47a07ec..a2a8bed 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -20,7 +20,6 @@ import org.springframework.cache.annotation.EnableCaching; @MapperScan("com.ruoyi.**.mapper") @EnableCaching public class RuoYiApplication { - public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication application = new SpringApplication(RuoYiApplication.class); diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 3d95b2d..367c8f0 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -49,7 +49,8 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + #url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root password: root # 从库数据源 @@ -57,7 +58,8 @@ spring: lazy: true type: com.zaxxer.hikari.HikariDataSource driverClassName: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + #url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root password: root # oracle: diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 62d89d0..bcb390f 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -52,7 +52,8 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + #url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root password: root # 从库数据源 @@ -60,7 +61,8 @@ spring: lazy: true type: com.zaxxer.hikari.HikariDataSource driverClassName: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + # url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root password: root # oracle: diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 49b2b03..da14153 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -30,7 +30,6 @@ server: servlet: # 应用的访问路径 context-path: / - # undertow 配置 undertow: # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 max-http-post-size: -1 @@ -144,6 +143,12 @@ security: - /index/JDList - /jmreport/** - /system/procedure/** + - /system/dict/data/type/** + - /system/proPlan/expiryProjects + - /system/proPlan/overdue + - /system/proPlan/list2 + - /system/mrp/** + - /system/orderPro/** # MyBatisPlus配置 # https://baomidou.com/config/ diff --git a/ruoyi-admin/src/test/java/com/ruoyi/RuoYiTestApplication.java b/ruoyi-admin/src/test/java/com/ruoyi/RuoYiTestApplication.java new file mode 100644 index 0000000..49cad30 --- /dev/null +++ b/ruoyi-admin/src/test/java/com/ruoyi/RuoYiTestApplication.java @@ -0,0 +1,21 @@ +package com.ruoyi; + +import com.ruoyi.common.utils.FtpUtil; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +@SpringBootTest(classes=RuoYiApplication.class) +public class RuoYiTestApplication { + + + @Test + public void asfsrfsf() throws FileNotFoundException { + FtpUtil.uploadFile("192.168.5.18", 8022, "admin", "hbyt2025", "", "test11/111111111/222222", "33333.dwg", new FileInputStream(new File("D:\\dwg\\111111111111111.dwg"))); + } + + +} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/test/ExcelSummaryTest.java b/ruoyi-admin/src/test/java/com/ruoyi/test/ExcelSummaryTest.java index 1b0d7e1..98e7538 100644 --- a/ruoyi-admin/src/test/java/com/ruoyi/test/ExcelSummaryTest.java +++ b/ruoyi-admin/src/test/java/com/ruoyi/test/ExcelSummaryTest.java @@ -84,4 +84,4 @@ public class ExcelSummaryTest { throw new RuntimeException("写入汇总 Excel 文件时出错", e); } } -} \ No newline at end of file +} diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 837132c..a788a81 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -173,6 +173,11 @@ itextpdf 5.5.13 + + commons-beanutils + commons-beanutils + 1.9.4 + org.lionsoul diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/convert/KeepSpaceStringConverter.java b/ruoyi-common/src/main/java/com/ruoyi/common/convert/KeepSpaceStringConverter.java new file mode 100644 index 0000000..319572d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/convert/KeepSpaceStringConverter.java @@ -0,0 +1,106 @@ +package com.ruoyi.common.convert; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ReadConverterContext; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.RichTextString; + +public class KeepSpaceStringConverter implements Converter { + + private static boolean printedFields = false; + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public String convertToJavaData(ReadConverterContext context) { + // 打印所有可用字段(仅第一次) + if (!printedFields) { + System.out.println("KeepSpaceStringConverter - ReadConverterContext字段:"); + java.lang.reflect.Field[] fields = context.getClass().getDeclaredFields(); + for (java.lang.reflect.Field field : fields) { + System.out.println(" - " + field.getName() + " (" + field.getType().getSimpleName() + ")"); + } + printedFields = true; + } + + // 尝试通过readCellData字段获取底层的POI Cell + try { + java.lang.reflect.Field readCellDataField = context.getClass().getDeclaredField("readCellData"); + readCellDataField.setAccessible(true); + ReadCellData readCellData = (ReadCellData) readCellDataField.get(context); + + if (readCellData != null) { + // 打印ReadCellData的字段 + System.out.println("KeepSpaceStringConverter - ReadCellData字段:"); + java.lang.reflect.Field[] cellDataFields = readCellData.getClass().getDeclaredFields(); + for (java.lang.reflect.Field field : cellDataFields) { + System.out.println(" - " + field.getName() + " (" + field.getType().getSimpleName() + ")"); + } + + // 尝试不同的字段名获取POI Cell + String[] possibleFieldNames = {"cell", "poiCell", "xssfCell", "xssfCellData"}; + + for (String fieldName : possibleFieldNames) { + try { + java.lang.reflect.Field cellField = readCellData.getClass().getDeclaredField(fieldName); + cellField.setAccessible(true); + Object cellObj = cellField.get(readCellData); + + if (cellObj instanceof Cell) { + Cell poiCell = (Cell) cellObj; + + if (poiCell.getCellType() == CellType.STRING) { + // 直接从POI Cell获取原始数据 + RichTextString richTextString = poiCell.getRichStringCellValue(); + String rawValue = richTextString.getString(); + + System.out.println("KeepSpaceStringConverter - POI原始数据: '" + rawValue + "'"); + System.out.println("KeepSpaceStringConverter - POI数据长度: " + rawValue.length()); + + // 检查是否包含前后空格 + if (!rawValue.equals(rawValue.trim())) { + System.out.println("KeepSpaceStringConverter - POI检测到前后空格!"); + } else { + System.out.println("KeepSpaceStringConverter - POI没有检测到前后空格"); + } + + return rawValue; + } + } + } catch (NoSuchFieldException e) { + // 继续尝试下一个字段名 + continue; + } + } + } + + } catch (Exception e) { + System.out.println("KeepSpaceStringConverter - 反射获取ReadCellData失败: " + e.getMessage()); + } + + // 如果反射失败,使用EasyExcel的方法 + ReadCellData cellData = context.getReadCellData(); + String easyExcelValue = cellData.getStringValue(); + System.out.println("KeepSpaceStringConverter - EasyExcel数据: '" + easyExcelValue + "'"); + System.out.println("KeepSpaceStringConverter - EasyExcel数据长度: " + easyExcelValue.length()); + + return easyExcelValue; + } + + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return new WriteCellData<>(context.getValue()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/poi/DynamicDataMapping.java b/ruoyi-common/src/main/java/com/ruoyi/common/poi/DynamicDataMapping.java new file mode 100644 index 0000000..ac96a91 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/poi/DynamicDataMapping.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.poi; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * @Description 存放需要导出的动态数据 + * @Author susu + * @Date 2024/2/19 + */ +@Data +public class DynamicDataMapping { + + private String dataId; + private List> dataList; + + + /** + * @description: 组装只有一个list类型的动态数据 + * @param dataId + * @param dataList + * @return java.util.List + * @author susu + * @date 2024/2/20 + */ + public static List createOneDataList(String dataId, List> dataList) { + if (dataList == null) + return null; + return Collections.singletonList(getDynamicDataMapping(dataId,dataList)); + } + + /** + * @description: 组装只有多个list类型的动态数据 + * @param transMap + * @return java.util.List + * @author susu + * @date 2024/2/20 + */ + public static List createMorDataList(Map>> transMap) { + if (transMap == null) + return null; + List list = new ArrayList<>(); + transMap.forEach((dataId,dataList)->{ + list.add(getDynamicDataMapping(dataId,dataList)); + }); + return list; + } + + public static DynamicDataMapping getDynamicDataMapping(String dataId, List> dataList){ + DynamicDataMapping dynamicData = new DynamicDataMapping(); + dynamicData.dataId = dataId; + dynamicData.dataList = dataList; + return dynamicData; + } + +} + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java b/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java new file mode 100644 index 0000000..6d30b38 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java @@ -0,0 +1,455 @@ +package com.ruoyi.common.poi; + +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Workbook; +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 org.springframework.util.ResourceUtils; + + +import java.io.*; +import java.util.List; +import java.util.Map; + +/** + * @Description 根据模版导出Excel程序 + * @Author susu + * @Date 2024/2/19 + */ +public class ExcelTemplateProc { + + /** + * @param templateFileName + * @param exportFilePathAndName + * @param staticDataMap + * @param dynamicDataMappingList + * @return void + * @description: 根据模版导出Excel入口 + * @author susu + * @date 2024/2/20 + */ + public static void doExportExcelByTemplateProc(String templateFileName, String exportFilePathAndName, + Map staticDataMap, + List dynamicDataMappingList) throws IOException { + /** + * 1. 从resources下加载模板并替换 + * 使用 ResourceUtils 加载文件 + */ + File file = ResourceUtils.getFile("classpath:"+templateFileName); + InputStream inputStream = new FileInputStream(file); + + Workbook workbook = dealAllSheetsByTemplate(inputStream, staticDataMap, dynamicDataMappingList); + // 2. 保存到本地 + saveExportFile(workbook, exportFilePathAndName); + } + + /** + * @param workbook + * @param excelFilePath + * @return void + * @description: 保存导出的Excel文件到服务器 + * @author susu + * @date 2024/2/20 + */ + public static void saveExportFile(Workbook workbook, String excelFilePath) throws IOException { + FileOutputStream outputStream = new FileOutputStream(excelFilePath); + executeWorkBookWrite(workbook, outputStream); + } + + /** + * @param workbook + * @param outputStream + * @return void + * @description: 数据输出 + * @author susu + * @date 2024/2/20 + */ + public static void executeWorkBookWrite(Workbook workbook, OutputStream outputStream) throws IOException { + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + workbook.close(); + } + + /** + * @param inputStream + * @param staticDataMap + * @param dynamicDataMappingList + * @return org.apache.poi.ss.usermodel.Workbook + * @description: 处理所有sheet页的模版 + * @author susu + * @date 2024/2/20 + */ + public static Workbook dealAllSheetsByTemplate(InputStream inputStream, + Map staticDataMap, + List dynamicDataMappingList) throws IOException { + XSSFWorkbook workbook = new XSSFWorkbook(inputStream); + + + // 处理所有sheet页 + for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) { + XSSFSheet sheet = workbook.getSheetAt(sheetIndex); + // 按模板处理sheet页 + dealSheetDataByTemplate(sheet, staticDataMap, dynamicDataMappingList); + } + + return workbook; + } + + /** + * @param inputStream + * @param staticDataMap + * @param dynamicDataMappingList + * @return org.apache.poi.ss.usermodel.Workbook + * @description: 处理只有一个sheet页的模版 + * @author susu + * @date 2024/2/20 + */ + public static Workbook dealFirstSheetByTemplate(InputStream inputStream, + Map staticDataMap, + List dynamicDataMappingList) throws IOException { + XSSFWorkbook workbook = new XSSFWorkbook(inputStream); + XSSFSheet sheet = workbook.getSheetAt(0); + // 按模板处理sheet页 + dealSheetDataByTemplate(sheet, staticDataMap, dynamicDataMappingList); + return workbook; + } + + /** + * @param sheet + * @param staticDataMap + * @param dynamicDataMappingList + * @return void + * @description: 按模板处理sheet页里的数据 + * @author susu + * @date 2024/2/19 + */ + private static void dealSheetDataByTemplate(XSSFSheet sheet, Map staticDataMap, List dynamicDataMappingList) { + // 循环sheet里每一行 + for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) { + XSSFRow row = sheet.getRow(i); + if (row == null) { + continue; // 跳过空行 + } + + // 添加调试信息:检查每一行是否有静态数据占位符 + if (row.getFirstCellNum() != -1 && row.getLastCellNum() != -1) { + for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) { + XSSFCell cell = row.getCell(j); + if (cell != null) { + String cellValue = cell.getStringCellValue(); + if (cellValue != null && cellValue.contains("{{") && !cellValue.contains(".")) { + System.out.println("发现静态数据占位符: 行" + i + ", 列" + j + ", 内容: " + cellValue); + } + } + } + } + + // 先处理静态数据,再处理动态数据 + dealTemplateDataRow(row, null, staticDataMap); + + DynamicDataMapping dynamicDataMapping = getDynamicRowDataByMatch(row, dynamicDataMappingList); + if (dynamicDataMapping != null) { + i = getTemplateLastRowIndexAfterDealTemplate(sheet, i, dynamicDataMapping); + } else { + // 检查是否有动态数据占位符但没有匹配的数据映射 + if (hasDynamicPlaceholders(row) && !hasMatchingDataMapping(row, dynamicDataMappingList)) { + // 清理没有数据映射的占位符 + clearDynamicPlaceholders(row); + } + } + } + } + + /** + * @param row + * @param dataMap + * @param dataPrefix + * @return void + * @description: 循环处理模版中每行的数据 + * @author susu + * @date 2024/2/20 + */ + private static void dealTemplateDataRow(XSSFRow row, String dataPrefix, Map dataMap) { + if (dataMap == null || row == null) { + return; + } + + int firstCellNum = row.getFirstCellNum(); + int lastCellNum = row.getLastCellNum(); + + // 检查是否有有效的单元格范围 + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { + return; + } + + // 添加调试信息:显示静态数据处理 + if (dataPrefix == null || dataPrefix.isEmpty()) { + System.out.println("开始处理静态数据行: " + row.getRowNum() + ", dataMap=" + dataMap.toString()); + } + + + for (int i = firstCellNum; i < lastCellNum; i++) { + XSSFCell cell = row.getCell(i); + if (cell != null) { + // 添加调试信息:检查静态数据处理 + if (dataPrefix == null || dataPrefix.isEmpty()) { + String cellValue = cell.getStringCellValue(); + if (cellValue != null && cellValue.contains("{{")) { + System.out.println("处理静态数据行,单元格" + i + ": " + cellValue); + } + } + fillInTemplateCellDataValue(cell, dataPrefix, dataMap); + } + } + } + + /** + * @param cell + * @param dataPrefix + * @param dataMap + * @return void + * @description: 填充模版里单元格的值 + * @author susu + * @date 2024/2/20 + */ + private static void fillInTemplateCellDataValue(XSSFCell cell, String dataPrefix, Map dataMap) { + if (cell == null) { + return; + } + String cellValue = cell.getStringCellValue();//获取模版里设置的数据 + if (StringUtils.isEmpty(cellValue)) { + return; + } + + + + boolean flag = false; + dataPrefix = StringUtils.isEmpty(dataPrefix) ? "" : (dataPrefix + "."); + for (Map.Entry entry : dataMap.entrySet()) { + // 循环所有,因为可能一行有多个占位符 + String cellTemplateStr = "{{" + dataPrefix + entry.getKey() + "}}"; + + + if (cellValue.contains(cellTemplateStr)) { + // 替换模版中单元格的数据 + String replacementValue = entry.getValue() == null ? "" : entry.getValue().toString(); + cellValue = cellValue.replace(cellTemplateStr, replacementValue); + flag = true; + // 添加静态数据替换的调试信息 + if (dataPrefix == null || dataPrefix.isEmpty()) { + System.out.println("静态数据替换: " + cellTemplateStr + " -> " + replacementValue); + System.out.println("替换后内容: " + cellValue); + } + } + } + if (flag) { + cell.setCellValue(cellValue); + // 添加调试信息:确认值被设置 + if (dataPrefix == null || dataPrefix.isEmpty()) { + System.out.println("设置单元格值: " + cellValue); + } + } + } + + /** + * @param row + * @param dynamicDataMappingList + * @return com.liu.susu.excel.template.poi.common.DynamicDataMapping + * @description: 通过模版sheet中的行数据 与 动态数据匹配,获取此行需要填充的动态数据 + * @author susu + * @date 2024/2/21 + */ + private static DynamicDataMapping getDynamicRowDataByMatch(XSSFRow row, List dynamicDataMappingList) { + if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) { + return null; + } + + // 检查行是否为空 + if (row == null) { + return null; + } + + int firstCellNum = row.getFirstCellNum(); + int lastCellNum = row.getLastCellNum(); + + // 检查是否有有效的单元格范围 + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { + return null; + } + + for (int j = firstCellNum; j < lastCellNum; j++) { + XSSFCell cell = row.getCell(j); + if (cell == null) { + continue; + } + + String value = cell.getStringCellValue(); + if (value != null) { + + for (DynamicDataMapping dynamicData : dynamicDataMappingList) { + if (value.startsWith("{{" + dynamicData.getDataId() + ".")) { + return dynamicData; + } + } + } + } + return null; + } + + /** + * @param sheet + * @param rowIndex + * @param dynamicDataMapping + * @return int + * @description: 根据动态数据的条数动态复制模版行,每处理一个类型的list返回最后的行数,进而处理下一个类型的list + * @author susu + * @date 2024/2/20 + */ + private static int getTemplateLastRowIndexAfterDealTemplate(XSSFSheet sheet, int rowIndex, DynamicDataMapping dynamicDataMapping) { + if (dynamicDataMapping == null) { + return rowIndex; + } + int dataRows = dynamicDataMapping.getDataList().size(); + // 需要拷贝的行数(因为模板行本身占1行,所以-1) + int copyRows = dataRows - 1; + if (copyRows > 0) { + /** + * shiftRows: 从动态数据模版行(rowIndex)到最后一行,这些全部行都向下移copyRows行 + * 相当于模版行上面插入n行空行(n=copyRows) + */ + sheet.shiftRows(rowIndex, sheet.getLastRowNum(), copyRows, true, false); + // 拷贝策略 + CellCopyPolicy cellCopyPolicy = makeCellCopyPolicy(); + // 因为从模版行开始向下平移了copyRows行,所以这里 模板行=rowIndex + copyRows, + int templateDataRow = rowIndex + copyRows; + // 因为模版行上新增了空行,所以要把模板所在行的模版 拷贝到上面新增的空行 + for (int i = 0; i < copyRows; i++) { + //templateDataRow-模版行数据 rowIndex + i循环的当前空行 + sheet.copyRows(templateDataRow, templateDataRow, rowIndex + i, cellCopyPolicy); + } + } + // 循环模版行:动态替换模版行(将模版行里的模版替换成动态数据) + for (int j = rowIndex; j < rowIndex + dataRows; j++) { + Map dataMap = dynamicDataMapping.getDataList().get(j - rowIndex); + dealTemplateDataRow(sheet.getRow(j), dynamicDataMapping.getDataId(), dataMap); + } + return rowIndex + copyRows; + } + + /** + * @param + * @return org.apache.poi.ss.usermodel.CellCopyPolicy + * @description: 拷贝策略 + * @author susu + * @date 2024/2/20 + */ + public static CellCopyPolicy makeCellCopyPolicy() { + CellCopyPolicy cellCopyPolicy = new CellCopyPolicy(); + cellCopyPolicy.setCopyCellValue(true); + cellCopyPolicy.setCopyCellStyle(true); + return cellCopyPolicy; + } + + /** + * 检查行中是否有动态数据占位符 + */ + private static boolean hasDynamicPlaceholders(XSSFRow row) { + if (row == null) { + return false; + } + + int firstCellNum = row.getFirstCellNum(); + int lastCellNum = row.getLastCellNum(); + + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { + return false; + } + + for (int j = firstCellNum; j < lastCellNum; j++) { + XSSFCell cell = row.getCell(j); + if (cell == null) { + continue; + } + + String value = cell.getStringCellValue(); + if (value != null && value.contains("{{") && value.contains("}}")) { + return true; + } + } + return false; + } + + /** + * 检查行中是否有匹配的数据映射 + */ + private static boolean hasMatchingDataMapping(XSSFRow row, List dynamicDataMappingList) { + if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) { + return false; + } + + if (row == null) { + return false; + } + + int firstCellNum = row.getFirstCellNum(); + int lastCellNum = row.getLastCellNum(); + + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { + return false; + } + + for (int j = firstCellNum; j < lastCellNum; j++) { + XSSFCell cell = row.getCell(j); + if (cell == null) { + continue; + } + + String value = cell.getStringCellValue(); + if (value != null) { + for (DynamicDataMapping dynamicData : dynamicDataMappingList) { + if (value.startsWith("{{" + dynamicData.getDataId() + ".")) { + return true; + } + } + } + } + return false; + } + + /** + * 清理行中的动态数据占位符 + */ + private static void clearDynamicPlaceholders(XSSFRow row) { + if (row == null) { + return; + } + + int firstCellNum = row.getFirstCellNum(); + int lastCellNum = row.getLastCellNum(); + + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { + return; + } + + for (int j = firstCellNum; j < lastCellNum; j++) { + XSSFCell cell = row.getCell(j); + if (cell == null) { + continue; + } + + String value = cell.getStringCellValue(); + if (value != null && value.contains("{{") && value.contains("}}")) { + // 清理动态数据占位符,保留静态内容 + String cleanedValue = value.replaceAll("\\{\\{[^}]+\\}\\}", ""); + cell.setCellValue(cleanedValue); + } + } + } + + +} + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExportExcelByTemplateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExportExcelByTemplateUtils.java new file mode 100644 index 0000000..08e5557 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExportExcelByTemplateUtils.java @@ -0,0 +1,65 @@ +package com.ruoyi.common.poi; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description 根据模版导出Excel工具类 + * @Author susu + * @Date 2024/2/19 + */ +public class ExportExcelByTemplateUtils { + + /** + * @description: 根据模版导出Excel入口(单个list数据) + * @param templateFileName + * @param exportFilePathAndName + * @param staticDataMap + * @param dataId + * @param originDataList + * @return void + * @author susu + * @date 2024/2/21 + */ + public static void doExportExcelOneListByTemplate(String templateFileName, String exportFilePathAndName, + Map staticDataMap, + String dataId, + List originDataList) throws Exception{ + + List> exportDataList = MapObjectUtil.objListToMapList(originDataList); + // 只有一个list数据 + List dynamicDataMappingList = DynamicDataMapping.createOneDataList(dataId, exportDataList); + // 导出 + ExcelTemplateProc.doExportExcelByTemplateProc(templateFileName,exportFilePathAndName,staticDataMap,dynamicDataMappingList); + + } + + /** + * @description: 根据模版导出Excel入口(多个list数据) + * @param templateFileName + * @param exportFilePathAndName + * @param staticSource + * @param originDataMapList + * @return void + * @author susu + * @date 2024/2/20 + */ + public static void doExportExcelMoreListByTemplate(String templateFileName, + String exportFilePathAndName, + Map staticSource, + Map> originDataMapList) throws Exception{ + + Map>> transMap = new HashMap<>(); + originDataMapList.forEach((dataId,originDataList)->{ + List> transDataList = MapObjectUtil.objListToMapList(originDataList); + transMap.put(dataId,transDataList); + }); + // 多个list类型数据 + List dynamicDataMappingList = DynamicDataMapping.createMorDataList(transMap); + // 导出 + ExcelTemplateProc.doExportExcelByTemplateProc(templateFileName,exportFilePathAndName,staticSource,dynamicDataMappingList); + + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/poi/MapObjectUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/poi/MapObjectUtil.java new file mode 100644 index 0000000..29f9ec1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/poi/MapObjectUtil.java @@ -0,0 +1,89 @@ +package com.ruoyi.common.poi; + +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.beanutils.BeanMap; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description + * @Author susu + * @Date 2024/2/19 + */ +public class MapObjectUtil { + + /** + * @description: 将object的list数据 转换成 map的list(如:List>) + * @param objDataList + * @return java.util.List> + * @author susu + */ + public static List> objListToMapList(List objDataList){ + List> dataList = new ArrayList<>(); + if (objDataList==null || objDataList.size()<1){ + return null; + } + objDataList.forEach(obj->{ + try { + Map map = MapObjectUtil.objectToMap(obj); + dataList.add(map); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + return dataList; + } + + /** + * @description: 将object数据转换成map数据 + * @param obj + * @return java.util.Map + * @author susu + */ + public static Map objectToMap(Object obj) throws IllegalAccessException { + Map map = new HashMap(); + Class cla = obj.getClass(); + Field[] fields = cla.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + String keyName = field.getName(); + Object value = field.get(obj); + if (value == null) + value = ""; + map.put(keyName, value); + } + return map; + } + + /** + * @description: 使用 JSONObject 将object转换成map + * @param obj + * @return java.util.Map + * @author susu + */ + public static Map objectToMap2(Object obj) { + if (obj == null) + return null; + return JSONObject.parseObject(JSONObject.toJSONString(obj),Map.class); + } + + /** + * @description: 使用BeanMap将object转换成map + * @param obj + * @return java.util.Map + * @author susu + */ + public static Map objectToMap3(Object obj) { + if (obj == null) + return null; + return new BeanMap(obj); + } + + + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/SmbUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/SmbUtil.java index 842ecbc..f7ea18a 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/SmbUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/SmbUtil.java @@ -91,22 +91,28 @@ public class SmbUtil { return; } - // 指定要下载的文件名 - String targetFileName = targetFolder+"汇总表.xlsx"; + // 定义要下载的文件列表 + String[] fileNames = { + targetFolder + "汇总表.xlsx", + "RawDataTable.xlsx" + }; - // 构造远程文件路径 - SmbFile remoteFile = new SmbFile(remoteDir, targetFileName); + // 循环下载每个文件 + for (String fileName : fileNames) { + // 构造远程文件路径 + SmbFile remoteFile = new SmbFile(remoteDir, fileName); - if (!remoteFile.exists() || !remoteFile.isFile()) { - System.err.println("目标文件不存在: " + remoteFile.getCanonicalPath()); - return; + if (!remoteFile.exists() || !remoteFile.isFile()) { + System.err.println("目标文件不存在: " + remoteFile.getCanonicalPath()); + continue; // 继续下载其他文件 + } + + // 本地保存路径 + File localFile = new File(localDir, fileName); + + transferFile(remoteFile, localFile); } - // 本地保存路径:D:/file/项目生产数据表.xlsx - File localFile = new File(localDir, targetFileName); - - transferFile(remoteFile, localFile); - } catch (Exception e) { System.err.println("操作失败: " + e.getMessage()); e.printStackTrace(); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index d4a8996..8f9c2ec 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -68,7 +68,8 @@ public class ExcelUtil { } public static ExcelResult importExcelSheet6(InputStream is, Class clazz, boolean isValidate) { DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); - EasyExcel.read(is, clazz, listener).sheet(6).doRead(); + EasyExcel.read(is, clazz, listener).sheet(6) + .headRowNumber(2).doRead(); return listener.getExcelResult(); } public static ExcelResult importExcelSheet1(InputStream is, Class clazz, boolean isValidate) { @@ -76,6 +77,11 @@ public class ExcelUtil { EasyExcel.read(is, clazz, listener).sheet(0).doRead(); return listener.getExcelResult(); } + public static ExcelResult importExcelSheet2(InputStream is, Class clazz, boolean isValidate) { + DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); + EasyExcel.read(is, clazz, listener).sheet(2).doRead(); + return listener.getExcelResult(); + } /** * 使用自定义监听器 异步导入 自定义返回 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java index 602ba08..a965df1 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java @@ -16,8 +16,10 @@ import com.ruoyi.common.core.domain.R; import com.ruoyi.common.excel.ExcelResult; import com.ruoyi.common.utils.JdUtils; import com.ruoyi.system.domain.EleMaterials; +import com.ruoyi.system.domain.ImMaterial; import com.ruoyi.system.domain.dto.*; import com.ruoyi.system.domain.vo.ExcelVo; +import com.ruoyi.system.mapper.ImMaterialMapper; import com.ruoyi.system.runner.JdUtil; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; @@ -54,6 +56,7 @@ import org.springframework.web.multipart.MultipartFile; public class EleMaterialsController extends BaseController { private final IEleMaterialsService iEleMaterialsService; + private final ImMaterialMapper materialMapper; private static final Logger log = LoggerFactory.getLogger(EleMaterialsController.class); /** @@ -482,4 +485,47 @@ public class EleMaterialsController extends BaseController { return R.ok("更新成功"); } + + + @Log(title = "更新VMI仓位", businessType = BusinessType.IMPORT) + @SaCheckPermission("system:route:updaDateCangwei") + @PostMapping(value = "/updaDateCangwei12", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R updaDateCangwei1(@RequestParam("file") MultipartFile file) throws Exception { + String originalFilename = file.getOriginalFilename(); + log.info("读取文件名: " + originalFilename); + ExcelResult result = ExcelUtil.importExcelSheet1(file.getInputStream(), JdVMIVo.class, true); + List list = result.getList(); + + ExecutorService executor = Executors.newFixedThreadPool(10); + list.forEach(vmi -> executor.submit(() -> { + log.info("===============>开始查询物料编码: " + vmi.getMaterialCode() + "的entryID"); + List entryID = JdUtil.getEntryID2(vmi.getMaterialCode()); + entryID.forEach(jdEntryVmi -> { + int fmaterialid = jdEntryVmi.getFMATERIALID(); + int fEntryId1 = jdEntryVmi.getFEntryId1(); + int fEntryId3 = jdEntryVmi.getFEntryId3(); + try { + log.info("=====================>开始更新 " + vmi.getMaterialCode() + " 的VMI仓位"); + JdUtil.updateVMI(fmaterialid, fEntryId1, fEntryId3, vmi.getFStockId(), vmi.getFStockPlaceId()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + })); + + // 关闭线程池并等待所有任务完成 + executor.shutdown(); + try { + if (!executor.awaitTermination(60, TimeUnit.MINUTES)) { + executor.shutdownNow(); // 超时后强制关闭 + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); // 保留中断状态 + } + + return R.ok("更新成功"); + } + + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java index 4035b63..4f5921f 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java @@ -356,7 +356,7 @@ public class ImMaterialController extends BaseController { //请求参数,要求为json字符串 JsonObject json = new JsonObject(); json.addProperty("FormId", "BD_MATERIAL"); - json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus"); + json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus,FIsVmiBusiness"); JsonArray filterString = new JsonArray(); log.debug("构建查询条件..."); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java index 6e1e922..1fcd672 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java @@ -1,333 +1,1025 @@ -package com.ruoyi.system.controller; - -import java.io.*; -import java.net.URLEncoder; -import java.util.*; - -import com.alibaba.excel.EasyExcel; -import com.ruoyi.common.excel.DefaultExcelListener; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.file.SmbUtil; -import com.ruoyi.system.domain.MrpResultCheck; -import com.ruoyi.system.domain.ProcessRoute; -import com.ruoyi.system.domain.bo.FigureSaveBo; -import com.ruoyi.system.domain.vo.MrpResultCheckVo; -import com.ruoyi.system.domain.vo.OverdueProjectVo; -import com.ruoyi.system.service.IBomDetailsService; -import com.ruoyi.system.service.IMrpResultCheckService; -import com.ruoyi.system.service.impl.ProductionOrderServiceImpl; -import lombok.RequiredArgsConstructor; -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.vo.ProcessOrderProVo; -import com.ruoyi.system.domain.bo.ProcessOrderProBo; -import com.ruoyi.system.service.IProcessOrderProService; -import com.ruoyi.common.core.page.TableDataInfo; -import org.springframework.web.multipart.MultipartFile; - -/** - * 项目令号 - * @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 ProductionOrderServiceImpl productionOrderService; - private final IMrpResultCheckService iMrpResultCheckService; - private final IBomDetailsService iBomDetailsService; - /** - * 查询项目令号列表 - */ - @SaCheckPermission("system:orderPro:list") - @GetMapping("/list") - public TableDataInfo list(ProcessOrderProBo bo, PageQuery pageQuery) { - return iProcessOrderProService.queryPageList(bo, pageQuery); - } - @SaCheckPermission("system:orderPro:list") - @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) { - return toAjax(iProcessOrderProService.insertByBo(bo)); - } - - /** - * 修改项目令号 - */ - @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:plan:add") - @Log(title = "排产计划", businessType = BusinessType.INSERT) - @RepeatSubmit() - @PostMapping("/addProduct") - public R addProduct(@Validated(AddGroup.class) @RequestBody FigureSaveBo bo,@RequestBody ProcessOrderProBo orderPro) { - iProcessOrderProService.addProduct(bo,orderPro); - return R.ok(); - } - - @SaCheckPermission("system:plan:add") - @Log(title = "执行出图", businessType = BusinessType.INSERT) - @RepeatSubmit() - @PostMapping("/executDrawing") - public R executDrawing(@RequestBody ProcessOrderProBo orderPro) { - String s = iProcessOrderProService.executDrawing(orderPro); - return R.ok(s); - } - /** - * 上传dwg图纸 - * @param id - * @param filePath - * @return - */ - @PostMapping("/uploadDwg") - @ResponseBody - public R uploadContractPDF(@RequestParam("id") Integer id, @RequestParam("file") MultipartFile filePath) { - String originalFilename = filePath.getOriginalFilename(); - if (StringUtils.isEmpty(originalFilename)) { - return R.fail("获取文件名称错误!!"); - } - //校验文件后缀 jpg jpeg pdf 格式的文件不允许上传 - String suffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); - if (!"dwg".equals(suffix)) { - return R.fail("禁止非法文件上传!!"); - } - - String reslut = iProcessOrderProService.uploadContractPDF(id,originalFilename,filePath); - - return R.ok(reslut); - } - - /** - * 下载PDF并生成zip包 - */ - - @SaCheckPermission("system:processOrderPro:uploadPDF") - @Log(title = "上传PDF", businessType = BusinessType.UPDATE) - @GetMapping("/uploadPDF") - public void uploadPDF(@RequestParam Long id, HttpServletResponse response) { - try { - // 调用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())); - - // 写入文件流 - 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:processOrderPro: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(); - } - - @PostMapping("/getMRPResults/{id}") - public R> geMRPResults(@PathVariable Long id) { - return iMrpResultCheckService.getMRPResults(id); - } -} +package com.ruoyi.system.controller; + +import java.io.*; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.util.*; + +import com.alibaba.excel.EasyExcel; +import com.ruoyi.common.excel.DefaultExcelListener; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +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.bo.FigureSaveBo; +import com.ruoyi.system.domain.vo.*; +import com.ruoyi.system.domain.vo.BomDataVO; +import com.ruoyi.system.mapper.ProcessOrderProMapper; +import com.ruoyi.system.service.*; +import com.ruoyi.system.service.impl.ProductionOrderServiceImpl; +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; + +/** + * 项目令号 + * + * @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 ProductionOrderServiceImpl productionOrderService; + private final IMrpResultCheckService iMrpResultCheckService; + private final IBomDetailsService iBomDetailsService; + private final ProcessOrderProMapper processOrderProMapper; + private final IImMaterialService imMaterialService; + private final ISafetyStockService iSafetyStockService; + + /** + * 查询项目令号列表 + */ + @SaCheckPermission("system:orderPro:list") + @GetMapping("/list") + public TableDataInfo list(ProcessOrderProBo bo, PageQuery pageQuery) { + return iProcessOrderProService.queryPageList(bo, pageQuery); + } + + @SaCheckPermission("system:orderPro:list") + @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) { + return toAjax(iProcessOrderProService.insertByBo(bo)); + } + + /** + * 修改项目令号 + */ + @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:plan:add") + @Log(title = "排产计划", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/addProduct") + public R addProduct(@Validated(AddGroup.class) @RequestBody FigureSaveBo bo, @RequestBody ProcessOrderProBo orderPro) { + iProcessOrderProService.addProduct(bo, orderPro); + return R.ok(); + } + + @SaCheckPermission("system:plan:add") + @Log(title = "执行出图", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/executDrawing") + public R executDrawing(@RequestBody ProcessOrderProBo orderPro) { + String s = iProcessOrderProService.executDrawing(orderPro); + return R.ok(s); + } + + /** + * 上传dwg图纸 + * + * @param id + * @param filePath + * @return + */ + @PostMapping("/uploadDwg") + @ResponseBody + public R uploadContractPDF(@RequestParam("id") Integer id, @RequestParam("file") MultipartFile filePath) { + String originalFilename = filePath.getOriginalFilename(); + if (StringUtils.isEmpty(originalFilename)) { + return R.fail("获取文件名称错误!!"); + } + //校验文件后缀 jpg jpeg pdf 格式的文件不允许上传 + String suffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); + if (!"dwg".equals(suffix)) { + return R.fail("禁止非法文件上传!!"); + } + + String reslut = iProcessOrderProService.uploadContractPDF(id, originalFilename, filePath); + + return R.ok(reslut); + } + + /** + * 下载PDF并生成zip包 + */ + + @SaCheckPermission("system:processOrderPro:uploadPDF") + @Log(title = "上传PDF", businessType = BusinessType.UPDATE) + @GetMapping("/uploadPDF") + public void uploadPDF(@RequestParam Long id, HttpServletResponse response) { + try { + // 调用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())); + + // 写入文件流 + 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:processOrderPro: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(); + } + + @PostMapping("/getMRPResults/{id}") + public R> geMRPResults(@PathVariable Long id) { + return iMrpResultCheckService.getMRPResults(id); + } + + @SaCheckPermission("system:route: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); + + // 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 (drawingNo != null && 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.valueOf(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 = iSafetyStockService.isSafeCode(materialCode); + if (isSafeStock) { + // 属于安全库存,添加到工艺数据列表 + processDataList.add(item); + continue; // 已分类,跳过后续检查 + } else { + // 不属于安全库存,添加到电气外包列表 + 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 (materialCode != null && materialCode.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 = "EXCEL模板/生产及工艺计划模版.xlsx"; + String outputPath = "D:/file/" + orderPro.getProductionOrderNo() + "生产及工艺计划表.xlsx"; + + // 准备模板数据 + Map staticDataMap = new HashMap<>(); + staticDataMap.put("productionOrderNo", orderPro.getProductionOrderNo()); + staticDataMap.put("productionName", orderPro.getProductionName()); + + // 添加静态数据调试信息 + log.info("静态数据: {}", staticDataMap); + + // 准备动态数据映射 + 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)); + } + + // 使用模板导出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()); + } + } + + /** + * 读取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) { + 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.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; + } + } + + /** + * 获取单元格的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(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; + } + + /** + * 转换工艺VO为Map列表(用于模板) + */ + private List> convertRouteDataToMapList(List routeDataList) { + List> mapList = new ArrayList<>(); + int index = 1; + for (ProcessRouteVo 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("rawMaterialCode", item.getRawMaterialCode()); // 材料BOM物料编码 + map.put("rawMaterialName", item.getRawMaterialName()); // 材料BOM物料名称 + map.put("bomMaterial", item.getBomMaterial()); // BOM材质 + map.put("bomDanZhong", item.getBomDanZhong()); // 材料单重KG + map.put("discUsage", item.getDiscUsage()); // 用量 + map.put("bomUnit", item.getBomUnit()); // 单位 + 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", item.getPlanStartTime()); // 计划开始时间 + map.put("planEndTime", item.getPlanEndTime()); // 计划结束时间 + map.put("xuStartTime", item.getXuStartTime()); // 序开始时间 + map.put("xuEndTime", item.getXuEndTime()); // 序结束时间 + mapList.add(map); + index++; + } + return mapList; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java index c852fb9..ac69e6c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java @@ -379,7 +379,7 @@ public class ProcessRouteController extends BaseController { ExcelResult result = ExcelUtil.importExcelSheet6(file.getInputStream(), ProcessRouteVo.class, true); List list1 = result.getList(); // 读取总装部分sheet - ExcelResult result1 = ExcelUtil.importExcelSheet1(file.getInputStream(), ProductionOrderVo.class, true); + ExcelResult result1 = ExcelUtil.importExcelSheet2(file.getInputStream(), ProductionOrderVo.class, true); List list = result1.getList(); String productionOrderNo = list1.get(0).getRouteDescription(); List bomDetails = iBomDetailsService.selectByProjectNumber(list1.get(0).getRouteDescription()); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java index 5d22995..b3fce81 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java @@ -59,6 +59,7 @@ public class ImMaterial extends BaseEntity { /* 所属名称:danwei */ + @JsonProperty("FIsVmiBusiness") private String classificationName; /* 所属编号 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BomDataVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BomDataVO.java new file mode 100644 index 0000000..2951634 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BomDataVO.java @@ -0,0 +1,55 @@ +package com.ruoyi.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.math.BigDecimal; +/** + * 基础BOM明细 + * @author 田志阳 + * @date 2025-09-04 + */ +@Data +public class BomDataVO { + + /** 序号 */ + @ExcelProperty(value = "序号") + private String index; + + /** 图号 */ + @ExcelProperty(value = "图号") + private String drawingNo; + + /** 名称 */ + @ExcelProperty(value = "名称") + private String name; + + /** 数量 */ + @ExcelProperty(value = "数量") + private String quantity; + + /** 材料 */ + @ExcelProperty(value = "材料") + private String material; + + /** 单重 */ + @ExcelProperty(value = "单重") + private String unitWeight; + + /** 总重 */ + @ExcelProperty(value = "总重") + private String totalWeight; + + /** 备注 */ + @ExcelProperty(value = "备注") + private String remark; + + /** 部件 */ + @ExcelProperty(value = "部件") + private String component; + + /** 部件图号 */ + @ExcelProperty(value = "部件图号") + private String componentDrawingNo; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EVOProductsDataVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EVOProductsDataVO.java new file mode 100644 index 0000000..d79aabd --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EVOProductsDataVO.java @@ -0,0 +1,42 @@ +package com.ruoyi.system.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 伊特产品明细 + * @author 田志阳 + * @date 2025-09-04 + */ +@Data +public class EVOProductsDataVO +{ + /** 序号 */ + private Integer index; + + /** 图号 */ + private String drawingNo; + + /** 名称 */ + private String name; + + /** 数量 */ + private Integer quantity; + + /** 材料 */ + private String material; + + /** 单重 */ + private BigDecimal unitWeight; + + /** 总重 */ + private BigDecimal totalWeight; + + /** 备注 */ + private String remark; + + /** 批次数量 */ + private Integer batchQuantity; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java new file mode 100644 index 0000000..5bfc17b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java @@ -0,0 +1,41 @@ +package com.ruoyi.system.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +/** + * 机电外购明细 + * + * @author 田志阳 + * @date 2025-09-04 + */ +@Data +public class ElecOutDataVO { + /** 序号 */ + private Integer index; + + /** 图号 */ + private String drawingNo; + + /** 名称 */ + private String name; + + /** 数量 */ + private Integer quantity; + + /** 材料 */ + private String material; + + /** 单重 */ + private BigDecimal unitWeight; + + /** 总重 */ + private BigDecimal totalWeight; + + /** 备注 */ + private String remark; + + /** 批次数量 */ + private Integer batchQuantity; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/JdKingdeeAssetCardVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/JdKingdeeAssetCardVo.java index bb8aa6b..e450d7f 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/JdKingdeeAssetCardVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/JdKingdeeAssetCardVo.java @@ -7,7 +7,11 @@ import lombok.Data; import java.math.BigDecimal; import java.util.Date; - +/** + * 金蝶资产卡片 + * @author 田志阳 + * @date 2025-09-04 + */ @Data @ExcelIgnoreUnannotated public class JdKingdeeAssetCardVo { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ProductionOrderVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ProductionOrderVo.java index dc514e6..775bf11 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ProductionOrderVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ProductionOrderVo.java @@ -2,12 +2,9 @@ package com.ruoyi.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; -import com.ruoyi.common.annotation.ExcelDictFormat; -import com.ruoyi.common.convert.ExcelDictConvert; +import com.ruoyi.common.convert.KeepSpaceStringConverter; import lombok.Data; -import java.math.BigDecimal; - /** * 生产订单视图对象 production_order @@ -19,18 +16,17 @@ import java.math.BigDecimal; @ExcelIgnoreUnannotated public class ProductionOrderVo { - private static final long serialVersionUID = 1L; /** * ID */ - + @ExcelProperty(value = "序号") private Long id; /** * 生产令号 */ - @ExcelProperty(value = "生产令号") + private String productionOrderNo; /** @@ -50,16 +46,13 @@ public class ProductionOrderVo { */ @ExcelProperty(value = "数量") private Double quantity; - - @ExcelProperty(value = "批次数量") - private String batchQuantity; - /** * 生产材料 */ @ExcelProperty(value = "材料") private String material; + /** * 单重(kg) */ @@ -78,6 +71,9 @@ public class ProductionOrderVo { @ExcelProperty(value = "备注") private String remark; + @ExcelProperty(value = "批次数量") + private String batchQuantity; + /** * 部件名称 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SupProvidDataVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SupProvidDataVO.java new file mode 100644 index 0000000..0aec9f1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SupProvidDataVO.java @@ -0,0 +1,40 @@ +package com.ruoyi.system.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +/** + * 甲供件明细 + * @author 田志阳 + * @date 2025-09-04 + */ +@Data +public class SupProvidDataVO { + /** 序号 */ + private Integer index; + + /** 图号 */ + private String drawingNo; + + /** 名称 */ + private String name; + + /** 数量 */ + private Integer quantity; + + /** 材料 */ + private String material; + + /** 单重 */ + private BigDecimal unitWeight; + + /** 总重 */ + private BigDecimal totalWeight; + + /** 备注 */ + private String remark; + + /** 批次数量 */ + private Integer batchQuantity; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VMIDataVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VMIDataVO.java new file mode 100644 index 0000000..79fdb8d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VMIDataVO.java @@ -0,0 +1,41 @@ +package com.ruoyi.system.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +/** + * VMI明细 + * + * @author 田志阳 + * @date 2025-09-04 + */ +@Data +public class VMIDataVO { + /** 序号 */ + private Integer index; + + /** 图号 */ + private String drawingNo; + + /** 名称 */ + private String name; + + /** 数量 */ + private Integer quantity; + + /** 材料 */ + private String material; + + /** 单重 */ + private BigDecimal unitWeight; + + /** 总重 */ + private BigDecimal totalWeight; + + /** 备注 */ + private String remark; + + /** 批次数量 */ + private Integer batchQuantity; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISafetyStockService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISafetyStockService.java index 8deac15..63bb4cd 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISafetyStockService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISafetyStockService.java @@ -48,4 +48,6 @@ public interface ISafetyStockService { Boolean deleteWithValidByIds(Collection ids, Boolean isValid); List selectByType(String type1); + + Boolean isSafeCode(String materialCode); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java index 86639bf..b46a92d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java @@ -532,7 +532,8 @@ public class ImMaterialServiceImpl implements IImMaterialService { @Override public ImMaterial selectByCodeAndName(String materialCode, String materialName) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(ImMaterial::getMaterialCode, materialCode).eq(ImMaterial::getMaterialName, materialName); + queryWrapper.eq(ImMaterial::getMaterialCode, materialCode) + .eq(ImMaterial::getMaterialName, materialName); return baseMapper.selectOne(queryWrapper); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java index aab5496..e523efa 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java @@ -148,6 +148,21 @@ public class SafetyStockServiceImpl implements ISafetyStockService { return Collections.emptyList(); } + /** + * 查询此物料是否是安全库存 + * + * @param materialCode + * @return + */ + @Override + public Boolean isSafeCode(String materialCode) { + LambdaQueryWrapper safetyStockLambdaQueryWrapper = new LambdaQueryWrapper<>(); + safetyStockLambdaQueryWrapper.eq(SafetyStock::getMaterialCode,materialCode); + SafetyStock safetyStock = baseMapper.selectOne(safetyStockLambdaQueryWrapper); + return safetyStock != null; + + } + /** * 物料批量查询 */ diff --git a/ruoyi-system/src/main/resources/EXCEL模板/生产BOM表1.xlsx b/ruoyi-system/src/main/resources/EXCEL模板/生产BOM表1.xlsx deleted file mode 100644 index 5678dc90abc5b0fb2b67806b99071ba2bc88e398..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10081 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 diff --git a/ruoyi-system/src/main/resources/EXCEL模板/生产及工艺计划模版.xlsx b/ruoyi-system/src/main/resources/EXCEL模板/生产及工艺计划模版.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..95f5e4c48a334c0bb6b592174dd8217c329fed2e GIT binary patch literal 18631 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 literal 0 HcmV?d00001