lyh
2 天以前 b508ec38ddf9ed93d4435e8f1e3c4effef798aaa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
package com.lxzn.webservice.controller;
 
import com.lxzn.framework.domain.webservice.request.ThirdDeProgramSource;
import com.lxzn.framework.domain.webservice.request.PlmProgramSource;
import com.lxzn.webservice.DncWebService;
import com.lxzn.webservice.ext.MesResultModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.multipart.MultipartFile;
 
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
 
@RestController
@Slf4j
@RequestMapping("/plmOuter")
public class DncWebController {
 
    @Autowired
    private DncWebService dncWebService;
 
    @Value("${fileHomePath}")
    private String filePath;
 
    // 定义关键文件类型
    private static final String JSON_EXT = ".json";
    private static final String TXT_EXT = ".txt";
 
    /**
     * 集成plm数据,压缩包(里面是NC压缩包与JSON文件)
     * @param file
     * @return
     */
    @PostMapping(value = "/syncPlmNcLogProgram", consumes = "multipart/form-data")
    @Transactional
    public String processNcPackage(MultipartFile file) {
        Path workDir = null;
        try {
            if (file == null || file.isEmpty()) {
                log.error("接收到的压缩文件为空");
                return MesResultModel.error("接收到的压缩文件为空");
            }
 
            // 创建临时目录
            String timestamp = String.valueOf(System.currentTimeMillis());
            workDir = Paths.get(filePath, "plm_" + timestamp);
            Files.createDirectories(workDir);
 
            // 保存原始文件
            Path originalPath = workDir.resolve(
                    Objects.requireNonNull(file.getOriginalFilename())
            );
            Files.copy(file.getInputStream(), originalPath, StandardCopyOption.REPLACE_EXISTING);
            log.info("原始文件已保存: {} (大小: {} 字节)",
                    originalPath,
                    Files.size(originalPath));
 
            // 解析外层压缩包
            Map<String, byte[]> packageContents = extractOuterZipContents(originalPath);
            log.info("外层压缩包解析成功,包含 {} 个文件", packageContents.size());
 
            // 不再查找单个ZIP,改为查找所有ZIP文件
            byte[] jsonData = findFileByExtension(packageContents, JSON_EXT);
            // 使用扩展名".zip"查找所有ZIP压缩包
            List<byte[]> zipFiles = findAllFilesByExtension(packageContents, ".zip");
 
            // 检查是否存在至少一个ZIP文件
            if (jsonData == null || zipFiles.isEmpty()) {
                String message = "未找到必要文件: " +
                        (jsonData == null ? "JSON文件" : "") +
                        (zipFiles.isEmpty() ? (jsonData == null ? " 和 " : "") + "NC程序压缩包" : "");
                return MesResultModel.error(message);
            }
 
            // 解析JSON
            String jsonFileName = getFileNameByExtension(packageContents);
            PlmProgramSource plmPrograms = parseJsonFile(jsonData);
            log.info("解析JSON文件成功: {}", jsonFileName);
 
            // 创建Map存储所有NC文件(文件名->内容)
            Map<String, byte[]> allNcFiles = new HashMap<>();
 
            //遍历并解压所有找到的ZIP文件
            for (byte[] zipData : zipFiles) {
                Map<String, byte[]> ncFiles = extractInnerZipContents(zipData);
 
                // 合并文件并检查重复
                for (Map.Entry<String, byte[]> entry : ncFiles.entrySet()) {
                    String fileName = entry.getKey();
                    if (allNcFiles.containsKey(fileName)) {
                        log.warn("检测到重复NC文件名: {}", fileName);
                    }
                    allNcFiles.put(fileName, entry.getValue());
                }
            }
            log.info("共解析 {} 个内部压缩包,合并得到 {} 个NC文件",
                    zipFiles.size(), allNcFiles.size());
 
            // 设置NC文件集合
            plmPrograms.setNcfiles(allNcFiles);
            plmPrograms.setFilePath(originalPath.getParent().toString());
            plmPrograms.setFileName(originalPath.toString());
 
            // 业务处理
            return JSONObject.toJSONString(
                    dncWebService.setPlmTree(plmPrograms)
            );
        } catch (Exception e) {
            log.error("处理NC文件包失败", e);
            return MesResultModel.error("文件处理失败: " + e.getMessage());
        } finally {
            // 清理临时文件
            if (workDir != null) {
                deleteDirectory(workDir.toFile());
            }
        }
    }
 
    /**
    * 集成3DE数据,压缩包(单个压缩包包含多个NC文件、一个工艺数据.TXT)
     * @param file 上传的压缩包文件
     * @return 处理结果
     */
    @PostMapping(value = "/integration3DEData", consumes = "multipart/form-data")
    @Transactional
    public String integration3DEData(MultipartFile file) {
        Path workDir = null;
        try {
            if (file == null || file.isEmpty()) {
                log.error("接收到的3DE压缩文件为空");
                return MesResultModel.error("接收到的3DE压缩文件为空");
            }
            // 创建临时工作目录
            String timestamp = String.valueOf(System.currentTimeMillis());
            workDir = Paths.get(filePath, "3de_" + timestamp);
            Files.createDirectories(workDir);
            // 保存原始文件
            Path originalPath = workDir.resolve(
                    Objects.requireNonNull(file.getOriginalFilename())
            );
            Files.copy(file.getInputStream(), originalPath, StandardCopyOption.REPLACE_EXISTING);
            log.info("3DE原始文件已保存: {} (大小: {} 字节)",
                    originalPath, Files.size(originalPath));
            // 解压ZIP包获取所有文件
            Map<String, byte[]> packageContents = extractOuterZipContents(originalPath);
            log.info("3DE压缩包解析成功,包含 {} 个文件", packageContents.size());
 
            // 查找工艺数据TXT文件
            byte[] processData = findFileByExtension(packageContents, TXT_EXT);
            if (processData == null) {
                return MesResultModel.error("未找到工艺数据TXT文件");
            }
 
            // 解析工艺数据TXT文件
            ThirdDeProgramSource thirdDeProgramSource = parseProcessTxtFile(processData);
            if (thirdDeProgramSource == null) {
                return MesResultModel.error("解析工艺数据TXT文件失败");
            }
 
            // 提取所有NC文件
            Map<String, byte[]> ncFiles = extractNcFiles(packageContents);
            if (ncFiles.isEmpty()) {
                return MesResultModel.error("未找到NC程序文件");
            }
            log.info("找到 {} 个NC程序文件", ncFiles.size());
 
            // 设置NC文件集合和文件路径信息
            thirdDeProgramSource.setNcFiles(ncFiles);
            thirdDeProgramSource.setFilePath(workDir.toString());
            thirdDeProgramSource.setFileName(originalPath.getFileName().toString());
 
            // 调用服务处理3DE数据
            return JSONObject.toJSONString(
                    dncWebService.processThirdDEProgram(thirdDeProgramSource)
            );
        } catch (Exception e) {
            log.error("处理3DE数据包失败", e);
            return MesResultModel.error("3DE文件处理失败: " + e.getMessage());
        } finally {
            // 清理临时文件
            if (workDir != null) {
                deleteDirectory(workDir.toFile());
            }
        }
    }
 
    /**
     * 解析工艺数据TXT文件
     * @param txtData TXT文件内容
     * @return ThirdDeProgramSource对象
     */
    private ThirdDeProgramSource parseProcessTxtFile(byte[] txtData) {
        try {
            String content = new String(txtData, StandardCharsets.UTF_8);
            log.info("工艺数据TXT内容:\n{}", content);
 
            // 使用FastJSON解析JSON格式的TXT
            JSONObject json = JSONObject.parseObject(content);
            if (json == null) {
                log.error("解析工艺数据失败: 不是有效的JSON格式");
                return null;
            }
 
            // 创建对象并设置属性
            ThirdDeProgramSource programSource = new ThirdDeProgramSource();
            programSource.setPartName(json.getString("part_name"));
            programSource.setPartNo(json.getString("part_no"));
            programSource.setRevisionNo(json.getString("revision_no"));
            programSource.setSkgxId(json.getString("skgx_id"));
            programSource.setSkgxName(json.getString("skgx_name"));
            programSource.setNcJcid(json.getString("nc_jcid"));
            programSource.setNcOs(json.getString("nc_os"));
            programSource.setEquipmentId(json.getString("equipment_id"));
            programSource.setNcFileName(json.getString("nc_file_name"));
            programSource.setNcPlantNo(json.getString("nc_plant_no"));
            programSource.setNcPlantName(json.getString("nc_plant_name"));
            log.debug("解析的工艺数据: partName={}, partNo={}, revisionNo={}, skgxId={}, skgxName={}, " +
                            "ncJcid={}, ncOs={}, equipmentId={}, ncFileName={}, ncPlantNo={}, ncPlantName={}",
                    programSource.getPartName(), programSource.getPartNo(),
                    programSource.getRevisionNo(), programSource.getSkgxId(),
                    programSource.getSkgxName(), programSource.getNcJcid(),
                    programSource.getNcOs(), programSource.getEquipmentId(),
                    programSource.getNcFileName(), programSource.getNcPlantNo(),
                    programSource.getNcPlantName());
 
            return programSource;
        } catch (Exception e) {
            log.error("解析工艺数据TXT文件失败", e);
            return null;
        }
    }
 
    /**
     * 解析键值对格式的TXT文件
     * @param content TXT文件内容
     * @return 键值对映射
     */
    private Map<String, String> parseKeyValueTxt(String content) {
        Map<String, String> result = new HashMap<>();
        String[] lines = content.split("\\r?\\n");
 
        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("#") || line.startsWith("//")) {
                continue; // 跳过空行和注释
            }
 
            int separatorIndex = line.indexOf('=');
            if (separatorIndex > 0) {
                String key = line.substring(0, separatorIndex).trim();
                String value = line.substring(separatorIndex + 1).trim();
                result.put(key, value);
            }
        }
 
        return result;
    }
 
    /**
     * 从文件内容中提取NC文件
     * @param packageContents 包内容映射
     * @return NC文件映射
     */
    private Map<String, byte[]> extractNcFiles(Map<String, byte[]> packageContents) {
        Map<String, byte[]> ncFiles = new HashMap<>();
 
        for (Map.Entry<String, byte[]> entry : packageContents.entrySet()) {
            String fileName = entry.getKey();
            String fileExt = FilenameUtils.getExtension(fileName).toLowerCase();
            if (fileExt.equals("nc")) {
                ncFiles.put(fileName, entry.getValue());
                log.debug("找到NC文件: {}", fileName);
            }
        }
 
        return ncFiles;
    }
 
    // 优化后的解压方法(使用commons-compress)
    private Map<String, byte[]> extractOuterZipContents(Path filePath) throws IOException {
        Map<String, byte[]> contents = new HashMap<>();
        log.info("开始解析ZIP文件: {}", filePath);
        try {
            try (ZipFile zipFile = new ZipFile(filePath.toFile())) {
                log.info("ZIP文件格式检测: {}", zipFile.getEncoding());
                Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
 
                while (entries.hasMoreElements()) {
                    ZipArchiveEntry entry = entries.nextElement();
                    if (entry.isDirectory()) continue;
 
                    try (InputStream is = zipFile.getInputStream(entry)) {
                        byte[] data = IOUtils.toByteArray(is);
                        contents.put(entry.getName(), data);
                        log.debug("提取文件: {} ({} 字节)", entry.getName(), data.length);
                    }
                }
            }
            log.info("Apache Commons解析成功");
        } catch (Exception e) {
            log.warn("Apache Commons解析失败,尝试标准库回退: {}", e.getMessage());
            try (InputStream is = Files.newInputStream(filePath);
                 ZipInputStream zis = new ZipInputStream(is, StandardCharsets.ISO_8859_1)) {
 
                ZipEntry entry;
                while ((entry = zis.getNextEntry()) != null) {
                    if (entry.isDirectory()) continue;
 
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[8192];
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        baos.write(buffer, 0, len);
                    }
 
                    String name = entry.getName();
                    contents.put(name, baos.toByteArray());
                    log.debug("回退提取文件: {}", name);
                }
            } catch (Exception ex) {
                throw new IOException("无法解析ZIP文件: " + filePath, ex);
            }
        }
        return contents;
    }
 
    private List<byte[]> findAllFilesByExtension(Map<String, byte[]> contents, String extension) {
        String extLower = extension.toLowerCase();
        List<byte[]> result = new ArrayList<>();
 
        for (Map.Entry<String, byte[]> entry : contents.entrySet()) {
            String fileName = entry.getKey().toLowerCase();
            if (fileName.endsWith(extLower)) {
                result.add(entry.getValue());
            }
        }
        return result;
    }
 
    // 内部ZIP解析方法
    private Map<String, byte[]> extractInnerZipContents(byte[] zipData) throws IOException {
        Map<String, byte[]> files = new HashMap<>();
 
        try (ByteArrayInputStream bais = new ByteArrayInputStream(zipData);
             ZipInputStream zis = new ZipInputStream(bais)) {
 
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                if (entry.isDirectory()) continue;
 
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[8192];
                int len;
                while ((len = zis.read(buffer)) != -1) {
                    baos.write(buffer, 0, len);
                }
 
                files.put(entry.getName(), baos.toByteArray());
            }
        }
        return files;
    }
 
    /**
     * 根据扩展名查找文件
     */
    private byte[] findFileByExtension(Map<String, byte[]> contents, String extension) {
        String extLower = extension.toLowerCase();
        for (Map.Entry<String, byte[]> entry : contents.entrySet()) {
            if (entry.getKey().toLowerCase().endsWith(extLower)) {
                return entry.getValue();
            }
        }
        return null;
    }
 
    /**
     * 获取文件名(根据扩展名)
     */
    private String getFileNameByExtension(Map<String, byte[]> contents) {
        for (String key : contents.keySet()) {
            if (key.toLowerCase().endsWith(JSON_EXT)) {
                return key;
            }
        }
        return "Unknown";
    }
 
    /**
     * 解析JSON文件内容
     */
    private PlmProgramSource parseJsonFile(byte[] jsonData) {
        String json = new String(jsonData, StandardCharsets.UTF_8);
        return JSONObject.parseObject(json, PlmProgramSource.class);
    }
 
    /**
     * 递归删除目录
     */
    private void deleteDirectory(File directory) {
        if (!directory.exists()) return;
 
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    if (!file.delete()) {
                        log.warn("无法删除文件: {}", file.getAbsolutePath());
                    }
                }
            }
        }
        if (!directory.delete()) {
            log.warn("无法删除目录: {}", directory.getAbsolutePath());
        }
    }
}