package org.jeecg.modules.dnc.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.commons.lang3.StringUtils; import org.jeecg.modules.dnc.dto.ComponentHierarchy; import org.jeecg.modules.dnc.dto.TransferPackage; import org.jeecg.modules.dnc.entity.*; import org.jeecg.modules.dnc.exception.ExceptionCast; import org.jeecg.modules.dnc.response.ActivitiCode; import org.jeecg.modules.dnc.response.DocumentCode; import org.jeecg.modules.dnc.service.IDocClassificationService; import org.jeecg.modules.dnc.service.IDocInfoService; import org.jeecg.modules.dnc.service.IDocRelativeService; import org.jeecg.modules.dnc.utils.JsonUtils; import org.jeecg.modules.dnc.utils.file.FileUtilS; import org.jeecg.modules.mdc.entity.MdcEquipment; import org.jeecg.modules.mdc.mapper.MdcEquipmentMapper; import org.jeecg.modules.system.service.IMdcProductionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; @Service public class FileFerryService { private final DataPackageService dataPackageService; private final SecurityService securityService; private static final Logger logger = LoggerFactory.getLogger(FileFerryService.class); @Value("${deploy.secretFolder}") private String ferryPath; @Value("${fileHomePath}") private String fileHomePath; @Autowired private MdcEquipmentMapper mdcEquipmentMapper; @Autowired private IMdcProductionService mdcProductionService; @Autowired private IDocClassificationService classificationService; @Autowired private IDocRelativeService docRelativeService; @Autowired private IDocInfoService docInfoService; @Autowired public FileFerryService(DataPackageService dataPackageService, SecurityService securityService) { this.dataPackageService = dataPackageService; this.securityService = securityService; } public String exportData(TransferPackage.DataType type, String id,String fileName) { // 1. 获取封装数据 TransferPackage transferPackage = dataPackageService.packageData(type, id); // 2. 压缩层级结构 compressHierarchy(transferPackage); // 3. JSON序列化 String json = JsonUtils.toJson(transferPackage); // // 4. 压缩加密 // byte[] compressed = CompressionUtils.gzipCompress(json.getBytes(StandardCharsets.UTF_8)); // byte[] encrypted = securityService.encrypt(compressed); //暂时不加密 byte[] compressed = json.getBytes(StandardCharsets.UTF_8); // 5. 生成文件 Path filePath = Paths.get(ferryPath,fileName); try { Files.createDirectories(filePath.getParent()); Files.write(filePath, compressed); return filePath.toString(); } catch (IOException e) { throw new RuntimeException("文件写入失败", e); } } public TransferPackage importData(String filePath) { try { // 1. 读取文件 Path path = Paths.get(filePath); String fileName = path.getFileName().toString(); byte[] encrypted = Files.readAllBytes(path); logger.debug("读取文件完成, 大小: {} 字节", encrypted.length); // 2. 解密 (当前已注释) // byte[] compressed = securityService.decrypt(encrypted); // 3. 解压缩 // byte[] jsonBytes = CompressionUtils.gzipDecompress(encrypted); String json = new String(encrypted, StandardCharsets.UTF_8); logger.debug("解压缩完成, JSON长度: {} 字符", json.length()); // 记录JSON内容用于调试 logger.trace("原始JSON内容:\n{}", json); // 4. JSON反序列化 logger.debug("开始反序列化..."); TransferPackage pkg = JsonUtils.fromJson(json, TransferPackage.class); // 5. 处理文件名 - 示例: 10A20250614000026_3102038 String[] split = fileName.split("_"); if (split.length < 2) { throw new IllegalArgumentException("无效的文件名格式: " + fileName); } String id = split[0]; String equipmentId = split[1].split("\\.")[0]; // 提取前缀和数字部分 int aIndex = id.indexOf("A"); if (aIndex == -1 || aIndex == id.length() - 1) { throw new IllegalArgumentException("无效的ID格式: " + id); } String prefix = id.substring(0, aIndex + 1); String numericPart = id.substring(aIndex + 1); // 计算前一个文件名 long number = Long.parseLong(numericPart); number--; // 获取前一个序列号 // 保持相同位数格式 String newNumeric = String.format("%0" + numericPart.length() + "d", number); String ncFileName = prefix + newNumeric + "_" + equipmentId+".NC"; String ncFilePath = path.getParent().resolve(ncFileName).toString(); // 6. 获取文件复制目标路径 DocFile docFile = pkg.getTraceChain().getDocFile(); DocInfo docInfo = pkg.getTraceChain().getDocInfo(); if (docFile == null) { throw new IllegalStateException("传输包中缺少文档文件信息"); } // 构建目标路径 String targetDirectory = fileHomePath + docFile.getFilePath(); String targetPath = Paths.get(targetDirectory, docFile.getFileEncodeName()).toString(); // 确保目标目录存在 File targetDir = new File(targetDirectory); if (!targetDir.exists() && !targetDir.mkdirs()) { throw new IOException("无法创建目标目录: " + targetDirectory); } // 7. 复制文件并重命名 logger.info("复制文件: {} → {}", ncFilePath, targetPath); Path source = Paths.get(ncFilePath); Files.copy(source, Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING); // 8. 查询设备id MdcEquipment mdcEquipment=mdcEquipmentMapper.selectOne(new QueryWrapper().eq("equipment_id",equipmentId)); if (mdcEquipment == null) { throw new IllegalArgumentException("无效的设备ID: " + equipmentId); } // 9.传输文件到设备下 List strings = mdcProductionService.findListParentTreeAll(mdcEquipment.getId()); if (strings != null && !strings.isEmpty()) { DocInfo deviceDoc = docInfoService.getByDocAttrAndDocId(docInfo.getDocId(), 7, mdcEquipment.getId()); if (deviceDoc == null) { DocClassification classification = classificationService.getByCode("send"); if(classification == null) ExceptionCast.cast(DocumentCode.DOC_CLASS_ERROR); DocRelative docRelative = new DocRelative(); docRelative.setDocId(docInfo.getDocId()); docRelative.setClassificationId(classification.getClassificationId()); docRelative.setAttributionType(7); docRelative.setAttributionId(mdcEquipment.getId()); docRelativeService.save(docRelative); } String sendPath = StringUtils.join(strings.toArray(), "/"); boolean copyFileNc = FileUtilS.copyFileNc(docFile.getFilePath(), sendPath + "/" + mdcEquipment.getEquipmentId(), docFile.getFileEncodeName(), docFile.getFileName(), docFile.getFileSuffix()); if (!copyFileNc) { ExceptionCast.cast(ActivitiCode.ACT_FILE_ERROR); } else { FileUtilS.deleteZipFromToSend(sendPath + "/" + mdcEquipment.getEquipmentId(), docFile.getFileName(), docFile.getFileSuffix()); } } else { throw new RuntimeException("文件传输路径获取失败"); } // 10.删除临时NC文件与json文件 logger.info("删除临时文件: {}", ncFilePath); Files.delete(source); Files.delete(path); return JsonUtils.fromJson(json, TransferPackage.class); } catch (NumberFormatException e) { throw new RuntimeException("文件名中的数字格式无效: " + e.getMessage(), e); } catch (IOException e) { throw new RuntimeException("文件操作失败: " + e.getMessage(), e); } catch (Exception e) { logger.error("文件导入失败 [路径: {}]", filePath, e); throw new RuntimeException("文件导入失败: " + e.getMessage(), e); } } private void compressHierarchy(TransferPackage pkg) { if (pkg.getTraceChain() == null || pkg.getTraceChain().getComponentHierarchy() == null || pkg.getTraceChain().getComponentHierarchy().getComponents().size() < 4) { return; } ComponentHierarchy hierarchy = pkg.getTraceChain().getComponentHierarchy(); List compressed = new ArrayList<>(); // 保留根部件 compressed.add(hierarchy.getComponents().get(0)); // 保留关键中间节点 int step = Math.max(1, hierarchy.getComponents().size() / 3); for (int i = step; i < hierarchy.getComponents().size() - 1; i += step) { compressed.add(hierarchy.getComponents().get(i)); } // 保留叶子部件 compressed.add(hierarchy.getLeafComponent()); // 更新层级 hierarchy.getComponents().clear(); hierarchy.getComponents().addAll(compressed); } private String generateFilename(TransferPackage.DataType type, String id) { return String.format("%s_%s_%d.ferry", type.name().toLowerCase(), id, System.currentTimeMillis()); } }