lyh
2 天以前 b508ec38ddf9ed93d4435e8f1e3c4effef798aaa
起落架DNC项目更改sql 更改yml配置 集成3DE接口
已添加1个文件
已修改8个文件
8707 ■■■■ 文件已修改
db/lxzn_qlj_nc.sql 7509 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/config/WebServiceConfig.java 124 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/framework/domain/webservice/request/ThirdDeProgramSource.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/nc/service/IDocRelativeService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/nc/service/impl/DocRelativeServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/webservice/DncWebService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/webservice/controller/DncWebController.java 294 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/lxzn/webservice/impl/DncWebServiceImpl.java 631 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
db/lxzn_qlj_nc.sql
ÎļþÌ«´ó
src/main/java/com/lxzn/config/WebServiceConfig.java
@@ -1,62 +1,62 @@
package com.lxzn.config;
import com.lxzn.webservice.DncWebService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
/**
 * @Description:
 * @Author: zhangherong
 * @Date: Created in 2020/12/24 17:09
 * @Version: 1.0
 * @Modified By:
 */
@Configuration
public class WebServiceConfig {
    @Autowired
    private DncWebService dncWebService;
    /**
     * Apache CXF æ ¸å¿ƒæž¶æž„是以BUS为核心,整合其他组件。
     * Bus是CXF的主干, ä¸ºå…±äº«èµ„源提供一个可配置的场所,作用类似于Spring的ApplicationContext,这些共享资源包括
     * WSDl管理器、绑定工厂等。通过对BUS进行扩展,可以方便地容纳自己的资源,或者替换现有的资源。默认Bus实现基于Spring架构,
     * é€šè¿‡ä¾èµ–注入,在运行时将组件串联起来。BusFactory负责Bus的创建。默认的BusFactory是SpringBusFactory,对应于默认
     * çš„Bus实现。在构造过程中,SpringBusFactory会搜索META-INF/cxf(包含在 CXF çš„jar中)下的所有bean配置文件。
     * æ ¹æ®è¿™äº›é…ç½®æ–‡ä»¶æž„建一个ApplicationContext。开发者也可以提供自己的配置文件来定制Bus。
     */
    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }
    /**
     * æ­¤æ–¹æ³•作用是改变项目中服务名的前缀名,此处127.0.0.1或者localhost不能访问时,请使用ipconfig查看本机ip来访问
     * æ­¤æ–¹æ³•被注释后, å³ä¸æ”¹å˜å‰ç¼€å(默认是services), wsdl访问地址为 http://127.0.0.1:8080/services/ws/api?wsdl
     * åŽ»æŽ‰æ³¨é‡ŠåŽwsdl访问地址为:http://127.0.0.1:8080/soap/ws/api?wsdl
     * http://127.0.0.1:8080/soap/列出服务列表 æˆ– http://127.0.0.1:8080/soap/ws/api?wsdl æŸ¥çœ‹å®žé™…的服务
     * æ–°å»ºServlet记得需要在启动类添加注解:@ServletComponentScan
     *
     * å¦‚果启动时出现错误:not loaded because DispatcherServlet Registration found non dispatcher servlet dispatcherServlet
     * å¯èƒ½æ˜¯springboot与cfx版本不兼容。
     * åŒæ—¶åœ¨spring boot2.0.6之后的版本与xcf集成,不需要在定义以下方法,直接在application.properties配置文件中添加:
     * cxf.path=/service(默认是services)
     */
    //@Bean
    //public ServletRegistrationBean dispatcherServlet() {
    //    return new ServletRegistrationBean(new CXFServlet(), "/soap/*");
    //}
    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(springBus(), dncWebService);
        endpoint.publish("/ws/dnc");
        return endpoint;
    }
}
//package com.lxzn.config;
//
//import com.lxzn.webservice.DncWebService;
//import org.apache.cxf.Bus;
//import org.apache.cxf.bus.spring.SpringBus;
//import org.apache.cxf.jaxws.EndpointImpl;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
//import javax.xml.ws.Endpoint;
//
///**
// * @Description:
// * @Author: zhangherong
// * @Date: Created in 2020/12/24 17:09
// * @Version: 1.0
// * @Modified By:
// */
//@Configuration
//public class WebServiceConfig {
//
//    @Autowired
//    private DncWebService dncWebService;
//
//    /**
//     * Apache CXF æ ¸å¿ƒæž¶æž„是以BUS为核心,整合其他组件。
//     * Bus是CXF的主干, ä¸ºå…±äº«èµ„源提供一个可配置的场所,作用类似于Spring的ApplicationContext,这些共享资源包括
//     * WSDl管理器、绑定工厂等。通过对BUS进行扩展,可以方便地容纳自己的资源,或者替换现有的资源。默认Bus实现基于Spring架构,
//     * é€šè¿‡ä¾èµ–注入,在运行时将组件串联起来。BusFactory负责Bus的创建。默认的BusFactory是SpringBusFactory,对应于默认
//     * çš„Bus实现。在构造过程中,SpringBusFactory会搜索META-INF/cxf(包含在 CXF çš„jar中)下的所有bean配置文件。
//     * æ ¹æ®è¿™äº›é…ç½®æ–‡ä»¶æž„建一个ApplicationContext。开发者也可以提供自己的配置文件来定制Bus。
//     */
//    @Bean(name = Bus.DEFAULT_BUS_ID)
//    public SpringBus springBus() {
//        return new SpringBus();
//    }
//
//    /**
//     * æ­¤æ–¹æ³•作用是改变项目中服务名的前缀名,此处127.0.0.1或者localhost不能访问时,请使用ipconfig查看本机ip来访问
//     * æ­¤æ–¹æ³•被注释后, å³ä¸æ”¹å˜å‰ç¼€å(默认是services), wsdl访问地址为 http://127.0.0.1:8080/services/ws/api?wsdl
//     * åŽ»æŽ‰æ³¨é‡ŠåŽwsdl访问地址为:http://127.0.0.1:8080/soap/ws/api?wsdl
//     * http://127.0.0.1:8080/soap/列出服务列表 æˆ– http://127.0.0.1:8080/soap/ws/api?wsdl æŸ¥çœ‹å®žé™…的服务
//     * æ–°å»ºServlet记得需要在启动类添加注解:@ServletComponentScan
//     *
//     * å¦‚果启动时出现错误:not loaded because DispatcherServlet Registration found non dispatcher servlet dispatcherServlet
//     * å¯èƒ½æ˜¯springboot与cfx版本不兼容。
//     * åŒæ—¶åœ¨spring boot2.0.6之后的版本与xcf集成,不需要在定义以下方法,直接在application.properties配置文件中添加:
//     * cxf.path=/service(默认是services)
//     */
//    //@Bean
//    //public ServletRegistrationBean dispatcherServlet() {
//    //    return new ServletRegistrationBean(new CXFServlet(), "/soap/*");
//    //}
//
//    @Bean
//    public Endpoint endpoint() {
//        EndpointImpl endpoint = new EndpointImpl(springBus(), dncWebService);
//        endpoint.publish("/ws/dnc");
//        return endpoint;
//    }
//}
src/main/java/com/lxzn/framework/domain/webservice/request/ThirdDeProgramSource.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package com.lxzn.framework.domain.webservice.request;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import org.apache.commons.io.FilenameUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
 * é›†æˆ3DE
 * @author clown
 * * @date 2022/9/19
 */
@Data
public class ThirdDeProgramSource implements Serializable {
    @TableId(value = "id")
    private String id;
    /*零件名称*/
    @TableField(value = "part_name")
    private String partName;
    /*零件图号*/
    @TableField(value = "part_no")
    private String partNo;
    /*版本号(工艺规程版本号)*/
    @TableField(value = "revision_no")
    private String revisionNo;
    /*车间名称*/
    @TableField(value = "nc_plant_name")
    private String ncPlantName;
    /*车间编号*/
    @TableField(value = "nc_plant_no")
    private String ncPlantNo;
    /*工序编号*/
    @TableField(value = "skgx_id")
    private String skgxId;
    /*工序名称*/
    @TableField(value = "skgx_name")
    private String skgxName;
    /*机床型号 nc_jcid*/
    @TableField(value = "nc_jcid")
    private String ncJcid;
    /*机床操作系统*/
    @TableField(value = "nc_os")
    private String ncOs;
    /*设备编号*/
    @TableField(value = "equipment_id")
    private String equipmentId;
    /*程序文件名(可能多个)*/
    @TableField(value = "nc_file_name")
    private String ncFileName;
    private transient Map<String,  byte[]> ncFiles;
    // ä¿®å¤æ–‡ä»¶å­—段定义
    private List<FileInfo> files;
    // æ·»åŠ æ–‡ä»¶ä¿¡æ¯ç±»
    @Data
    public static class FileInfo {
        private String file_name;
        // æ·»åŠ å…¶ä»–å¯èƒ½å­—æ®µ
        private String file_type;
        private Long file_size;
    }
    // æ·»åŠ æ–‡ä»¶è·¯å¾„å’Œåç§°å­—æ®µ
    private String filePath;
    private String fileName;
    // æ·»åŠ è®¾ç½®NC文件的方法
    public void setNcFiles(Map<String, byte[]> ncFiles) {
        this.ncFiles = ncFiles;
        this.files = new ArrayList<>();
        for (Map.Entry<String, byte[]> entry : ncFiles.entrySet()) {
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFile_name(entry.getKey());
            fileInfo.setFile_size((long) entry.getValue().length);
            fileInfo.setFile_type(FilenameUtils.getExtension(entry.getKey()));
            this.files.add(fileInfo);
        }
    }
}
src/main/java/com/lxzn/nc/service/IDocRelativeService.java
@@ -47,4 +47,14 @@
     * @return
     */
    List<DeviceInfo> findDeviceByDocId(String docId);
    /**
     * æŸ¥è¯¢å…³è”关系
     * @param docId
     * @param classificationId
     * @param attributionType
     * @param attributionId
     * @return
     */
    DocRelative findDocRelative(String docId, String classificationId, Integer attributionType, String attributionId);
}
src/main/java/com/lxzn/nc/service/impl/DocRelativeServiceImpl.java
@@ -157,4 +157,22 @@
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        return super.getBaseMapper().findDeviceByDocId(docId);
    }
    /**
     * æŸ¥è¯¢å…³è”关系
     * @param docId
     * @param classificationId
     * @param attributionType
     * @param attributionId
     * @return
     */
    @Override
    public DocRelative findDocRelative(String docId, String classificationId, Integer attributionType, String attributionId){
        LambdaQueryWrapper<DocRelative> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(DocRelative::getDocId, docId);
        lambdaQueryWrapper.eq(DocRelative::getAttributionType, attributionType);
        lambdaQueryWrapper.eq(DocRelative::getAttributionId, attributionId);
        lambdaQueryWrapper.eq(DocRelative::getClassificationId, classificationId);
        return super.getOne(lambdaQueryWrapper);
    }
}
src/main/java/com/lxzn/webservice/DncWebService.java
@@ -3,6 +3,7 @@
import com.lxzn.framework.domain.webservice.PlmProgramSourceInfo;
import com.lxzn.framework.domain.webservice.ProcedureFinish;
import com.lxzn.framework.domain.webservice.request.PlmProgramSource;
import com.lxzn.framework.domain.webservice.request.ThirdDeProgramSource;
import com.lxzn.webservice.ext.MesResultModel;
import javax.jws.WebMethod;
@@ -14,7 +15,7 @@
 * @CreateTime: 2025-02-27
 * @Description:
 */
@WebService(name = "DncWebService", targetNamespace = "http://lxzn.webservice.com")
//@WebService(name = "DncWebService", targetNamespace = "http://lxzn.webservice.com")
public interface DncWebService {
    /**
@@ -26,32 +27,20 @@
    @WebMethod
    MesResultModel syncPlmNcLogProgram(@WebParam(name = "msg") String msg);
    public MesResultModel setTree(PlmProgramSource plmProgramSource);
    /**
     * NC程序齐套接口(MES)-DNC系统接收到PLM传的NC程序,将程序包对应的零件号、工序号、设备编号传给MES系统
     * å®šæ—¶æ‰«æPlmProgramSourceInfo表中的数据,进行数据同步,每分钟同步一次
     * é›†æˆPLM挂树
     * @param plmProgramSource
     * @return
     */
    void syncWorkmanship();
    public MesResultModel setPlmTree(PlmProgramSource plmProgramSource);
    /**
     * æ´¾å·¥ä»»åŠ¡ä¸‹å‘æŽ¥å£(MES)
     *
     * @param msg
     * é›†æˆ3DE挂树
     * @param thirdDeProgramSource
     * @return
     */
    @WebMethod
    String issuedDispatchTask(@WebParam(name = "msg") String msg);
    public MesResultModel processThirdDEProgram(ThirdDeProgramSource thirdDeProgramSource);
    /**
     * å®Œå·¥æŽ¥å£(MES)
     *
     * @param msg
     * @return
     */
    @WebMethod
    String issuedProcedureFinish(@WebParam(name = "msg") String msg);
    String readFileName(String itemCode);
      String readFileName(String itemCode);
}
src/main/java/com/lxzn/webservice/controller/DncWebController.java
@@ -1,11 +1,14 @@
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;
@@ -17,7 +20,6 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -29,16 +31,22 @@
    @Autowired
    private DncWebService dncWebService;
    @Value("${ncPdm.file_path}")
    @Value("${fileHomePath}")
    private String filePath;
    // å®šä¹‰å…³é”®æ–‡ä»¶ç±»åž‹
    private static final String INNER_ZIP_PREFIX = "【公开】NC程序文件 ";
    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("接收到的压缩文件为空");
@@ -47,7 +55,7 @@
            // åˆ›å»ºä¸´æ—¶ç›®å½•
            String timestamp = String.valueOf(System.currentTimeMillis());
            Path workDir = Paths.get(filePath, timestamp);
            workDir = Paths.get(filePath, "plm_" + timestamp);
            Files.createDirectories(workDir);
            // ä¿å­˜åŽŸå§‹æ–‡ä»¶
@@ -92,7 +100,6 @@
                for (Map.Entry<String, byte[]> entry : ncFiles.entrySet()) {
                    String fileName = entry.getKey();
                    if (allNcFiles.containsKey(fileName)) {
                        // ä¿®æ”¹ç‚¹5:记录重复警告(实际业务中可能需要更严格处理)
                        log.warn("检测到重复NC文件名: {}", fileName);
                    }
                    allNcFiles.put(fileName, entry.getValue());
@@ -108,19 +115,183 @@
            // ä¸šåŠ¡å¤„ç†
            return JSONObject.toJSONString(
                    dncWebService.setTree(plmPrograms)
                    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());
@@ -131,17 +302,9 @@
                    if (entry.isDirectory()) continue;
                    try (InputStream is = zipFile.getInputStream(entry)) {
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        byte[] buffer = new byte[8192];
                        int len;
                        while ((len = is.read(buffer)) > 0) {
                            baos.write(buffer, 0, len);
                        }
                        // ä¿ç•™åŽŸå§‹æ–‡ä»¶å
                        String name = entry.getName();
                        contents.put(name, baos.toByteArray());
                        log.debug("提取文件: {} ({} å­—节)", name, baos.size());
                        byte[] data = IOUtils.toByteArray(is);
                        contents.put(entry.getName(), data);
                        log.debug("提取文件: {} ({} å­—节)", entry.getName(), data.length);
                    }
                }
            }
@@ -175,14 +338,15 @@
    private List<byte[]> findAllFilesByExtension(Map<String, byte[]> contents, String extension) {
        String extLower = extension.toLowerCase();
        return contents.entrySet().stream()
                .filter(e -> {
                    String fileName = e.getKey();
                    // ä½¿ç”¨å°å†™æ¯”较避免大小写敏感问题
                    return fileName.toLowerCase().endsWith(extLower);
                })
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
        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解析方法
@@ -213,57 +377,25 @@
     * æ ¹æ®æ‰©å±•名查找文件
     */
    private byte[] findFileByExtension(Map<String, byte[]> contents, String extension) {
        return contents.entrySet().stream()
                .filter(e -> e.getKey().toLowerCase().endsWith(extension))
                .map(Map.Entry::getValue)
                .findFirst()
                .orElse(null);
        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) {
        return contents.keySet().stream()
                .filter(k -> k.toLowerCase().endsWith(DncWebController.JSON_EXT))
                .findFirst()
                .orElse("Unknown");
    }
    /**
     * æŸ¥æ‰¾ä¸ŽæŒ‡å®šå‰ç¼€åŒ¹é…çš„æ–‡ä»¶å†…容
    * @param contents æ–‡ä»¶å†…容映射(文件名 -> æ–‡ä»¶å†…容)
    * @param prefix è¦åŒ¹é…çš„æ–‡ä»¶åå‰ç¼€
    * @return åŒ¹é…çš„第一个文件内容,未找到返回 null
    */
    private byte[] findFileByPrefix(Map<String, byte[]> contents, String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            log.warn("文件前缀不能为空");
            return null;
        for (String key : contents.keySet()) {
            if (key.toLowerCase().endsWith(JSON_EXT)) {
                return key;
            }
        }
        return contents.entrySet().stream()
                .filter(entry -> {
                    String fileName = entry.getKey();
                    // è·¯å¾„规范化处理
                    String normalizedKey = fileName.replace('\\', '/');
                    // èŽ·å–çº¯æ–‡ä»¶å
                    int lastSlash = normalizedKey.lastIndexOf('/');
                    String pureFileName = (lastSlash >= 0) ?
                            normalizedKey.substring(lastSlash + 1) :
                            normalizedKey;
                    // è°ƒè¯•日志
                    log.debug("检查文件: [原始: {}], [纯文件名: {}], å‰ç¼€: {}",
                            fileName, pureFileName, prefix);
                    return pureFileName.contains("NC");
                })
                .map(Map.Entry::getValue)
                .findFirst()
                .orElse(null);
        return "Unknown";
    }
    /**
@@ -274,4 +406,26 @@
        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());
        }
    }
}
src/main/java/com/lxzn/webservice/impl/DncWebServiceImpl.java
@@ -8,14 +8,14 @@
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.lxzn.activiti.service.IAssignFileStreamService;
import com.lxzn.framework.domain.activiti.response.ActivitiCode;
import com.lxzn.framework.domain.filesystem.response.FileUploadResult;
import com.lxzn.framework.domain.nc.*;
import com.lxzn.framework.domain.nc.response.DocumentCode;
import com.lxzn.framework.domain.webservice.DispatchInfo;
import com.lxzn.framework.domain.webservice.PlmProgramSourceInfo;
import com.lxzn.framework.domain.webservice.ProcedureFinish;
import com.lxzn.framework.domain.webservice.request.NcProgramInfo;
import com.lxzn.framework.domain.webservice.request.PlmProgramSource;
import com.lxzn.framework.domain.webservice.request.*;
import com.lxzn.framework.exception.ExceptionCast;
import com.lxzn.framework.model.response.CommonCode;
import com.lxzn.framework.utils.CxfClientUtil;
@@ -24,9 +24,8 @@
import com.lxzn.framework.utils.ZipUtil;
import com.lxzn.framework.utils.file.FileUtil;
import com.lxzn.nc.service.*;
import com.lxzn.nc.service.impl.DeviceInfoServiceImpl;
import com.lxzn.webservice.DncWebService;
import com.lxzn.framework.domain.webservice.request.WsDispatch;
import com.lxzn.framework.domain.webservice.request.WsProcedureFinish;
import com.lxzn.webservice.ext.MesResultModel;
import com.lxzn.webservice.service.IDispatchInfoService;
import com.lxzn.webservice.service.IPlmProgramSourceInfoService;
@@ -62,40 +61,12 @@
 * @Description:
 */
@Service
@WebService(name = "DncWebService",
        targetNamespace = "http://lxzn.webservice.com",
        endpointInterface = "com.lxzn.webservice.DncWebService"
)
//@WebService(name = "DncWebService",
//        targetNamespace = "http://lxzn.webservice.com",
//        endpointInterface = "com.lxzn.webservice.DncWebService"
//)
@Slf4j
public class DncWebServiceImpl implements DncWebService {
    @Value("${ncPdm.file_path}")
    private String filePath;
    /**
     * url
     */
    @Value("${webservice.url}")
    private String url;
    /**
     * namespace
     */
    @Value("${webservice.namespace}")
    private String namespace;
    /**
     * method
     */
    @Value("${webservice.method}")
    private String method;
    @Resource
    private IDispatchInfoService dispatchInfoService;
    @Resource
    private IProcedureFinishService procedureFinishService;
    @Resource
    private IDeviceInfoService deviceInfoService;
    @Resource
    private IAssignFileStreamService assignFileStreamService;
    @Resource
    private IPlmProgramSourceInfoService iPlmProgramSourceInfoService;
    @Autowired
@@ -126,112 +97,10 @@
    private IDocClassificationService docClassificationService;
    @Autowired
    private IDeviceManagementService deviceManagementService;
    /**
     * æ´¾å·¥ä»»åŠ¡ä¸‹å‘æŽ¥å£
     * @param msg
     * @return
     */
    @Override
    public String issuedDispatchTask(@WebParam(name = "msg") String msg){
        log.info("MES派工任务下发原始数据 === {}", msg);
        List<WsDispatch> wsDispatchList = JSONObject.parseArray(msg, WsDispatch.class);
        for (WsDispatch wsDispatch : wsDispatchList) {
            DispatchInfo dispatchInfo = new DispatchInfo();
            BeanUtils.copyProperties(wsDispatch, dispatchInfo);
            dispatchInfoService.save(dispatchInfo);
            Map<Boolean,String> ncResult = disPatchTaskNc(dispatchInfo);
            if (ncResult.containsKey(false)) {
                // è¿”回具体的错误信息
                return MesResultModel.error("NC任务下发失败: " + ncResult.get(false));
            }
        }
        return MesResultModel.success("成功");
    }
    /**
     * ä¸‹å‘程序
     */
    public Map<Boolean,String> disPatchTaskNc(DispatchInfo dispatchInfo) {
        String processCode = dispatchInfo.getOperationSeqNo() + "_" + dispatchInfo.getEquipmentId();
        return assignFileStreamService.disPatchTaskNc(dispatchInfo.getEquipmentId(),
                dispatchInfo.getWorkshop(),dispatchInfo.getMdsItemCode(),processCode,dispatchInfo.getRevisionNo(),dispatchInfo.getId());
    }
    /**
     * å®Œå·¥
     *
     * @param msg
     * @return
     */
    @Override
    public String issuedProcedureFinish(String msg) {
        log.info("MES完工原始数据 === {}", msg);
        List<WsProcedureFinish> wsProcedureFinishList = JSONObject.parseArray(msg, WsProcedureFinish.class);
        if (CollectionUtils.isEmpty(wsProcedureFinishList)) {
            return MesResultModel.error("MesResultModel");
        }
        List<ProcedureFinish> procedureFinishList = wsProcedureFinishList.stream()
                .map(source -> {
                    ProcedureFinish target = new ProcedureFinish();
                    BeanUtils.copyProperties(source, target);
                    return target;
                })
                .collect(Collectors.toList());
        procedureFinishService.saveBatch(procedureFinishList);
        //DNC系统依据工单号删除设备结构树上对应的NC程序及工控网NC程序
        for (ProcedureFinish procedureFinish : procedureFinishList) {
            //DNC系统依据工单号删除设备结构树上对应的NC程序及工控网NC程序
            if (procedureFinish.getEndineStatus().equals("3")){
                //删除设备结构树上对应的NC程序及工控网NC程序
                ProductInfo productInfo = productInfoService.getByProductNo("PLM");
                if (productInfo == null) {
                    log.error("======= äº§å“å±‚级异常,请联系开发人员=======");
                    return MesResultModel.error("产品层级异常,请联系开发人员");
                }
                List<ComponentInfo> componentInfoList = componentInfoService.getByProductId(productInfo.getProductId());
                List<String
                        > comlist = new ArrayList<>();
                if (componentInfoList != null && !componentInfoList.isEmpty()) {
                    for (ComponentInfo cc : componentInfoList) {
                        comlist.add(cc.getComponentId());
                    }
                } else {
                    return MesResultModel.error("无部件信息,请联系开发人员");
                }
                String processCode=procedureFinish.getMdsItemCode();
                PartsInfo part = partsInfoService.getByCodeAndComIdList(procedureFinish.getMdsItemCode(),comlist);
                if (part == null) {
                    return MesResultModel.error("无此零件信息"+"__" + processCode+",请联系开发人员");
                }
                processCode = processCode.split("_")[0];
                //工序
                ProcessStream stream = processStreamService.findByProcessEquipment(procedureFinish.getEquipmentId(),
                        processCode,procedureFinish.getRevisionNo(),productInfo.getProductId(),part.getPartsId());
                if (stream == null) {
                    return MesResultModel.error("无此工序信息"+"__" + processCode+",请联系开发人员");
                }
                //查询对应NC程序
                List<ProcessStream> processStreamList = new ArrayList<>();
                processStreamList.add(stream);
                List<DocInfo> docFileList = docInfoService.getByProcessIds(processStreamList);
                if (docFileList == null || docFileList.isEmpty()) {
                    return MesResultModel.error("无此NC程序" + "__" + processCode + ",请联系开发人员");
                }
                docFileList.forEach(docFile -> {
                    docInfoService.deleteDocInfo(docFile.getDocId());
                });
            }
        }
        log.info("接收MES完工原始数据成功!");
        return MesResultModel.success("成功");
    }
    @Autowired
    private DeviceInfoServiceImpl deviceInfoService;
    @Autowired
    private IDeviceGroupService deviceGroupService;
    /**
     * èŽ·å–NC文件及属性信息(集成PLM)
     *
@@ -246,7 +115,7 @@
        List<PlmProgramSource> plmProgramSourceList = JSONObject.parseArray(msg, PlmProgramSource.class);
        AtomicReference<MesResultModel> mesResultModel= new AtomicReference<>(new MesResultModel());
        plmProgramSourceList.forEach(item -> {
            mesResultModel.set(setTree(item));
            mesResultModel.set(setPlmTree(item));
        });
        return mesResultModel.get();
    }
@@ -262,7 +131,7 @@
     * @param plmProgramSource
     * @return
     */
    public MesResultModel setTree(PlmProgramSource plmProgramSource) {
    public MesResultModel setPlmTree(PlmProgramSource plmProgramSource) {
        MesResultModel model = new MesResultModel();
        model.setSuccFlag("成功");
        PlmProgramSourceInfo plmProgramSourceInfo = new PlmProgramSourceInfo();
@@ -485,76 +354,407 @@
        return model;
    }
    /**
     * å°è£…树结构
     * 1. äº§å“ é›†æˆæ•°æ®
     * 2、部件 æŒ‰ç…§éƒ¨é—¨ï¼ˆè½¦é—´ nc_plant_name ï¼‰ç»´åº¦å»ºç«‹
     * 3、零件 å›¾å·ï¼ˆé›¶ä»¶å·ã€é›¶ä»¶åç§°ï¼‰ part_no
     * 4、工序  å·¥è‰ºID(工艺规程编号/临规编号)
     * 5. nc文件 ï¼ˆå·¥åºä¸‹ï¼‰
     * 6.机床型号与机床操作系统组成设备类与工序进行绑定
     * é›†æˆ3DE挂树
     * @param thirdDeProgramSource
     * @return
     */
    @Override
    @Transactional
    public MesResultModel processThirdDEProgram(ThirdDeProgramSource thirdDeProgramSource) {
        MesResultModel model = new MesResultModel();
        model.setSuccFlag("成功");
        try {
            log.info("3DE NC文件及属性信息原始数据 === {}", JSONObject.toJSONString(thirdDeProgramSource));
            // äº§å“ - ä½¿ç”¨é»˜è®¤çš„3DE产品
            ProductInfo productInfo = productInfoService.getByProductNo("3DE");
            if (productInfo == null) {
                return createErrorModel(model, "3DE产品层级异常,请联系开发人员");
            }
            // éƒ¨ä»¶ - æŒ‰ç…§è½¦é—´ç»´åº¦å»ºç«‹
            ComponentInfo componentInfo = componentInfoService.getByCode(thirdDeProgramSource.getNcPlantNo());
            if (componentInfo == null) {
                componentInfo = new ComponentInfo();
                componentInfo.setComponentId(IdWorker.getIdStr());
                componentInfo.setComponentCode(thirdDeProgramSource.getNcPlantNo());
                componentInfo.setComponentName(thirdDeProgramSource.getNcPlantName());
                componentInfo.setComponentStatus(1);
                componentInfo.setRankLevel(1);
                componentInfo.setParentId(productInfo.getProductId());
                componentInfo.setProductId(productInfo.getProductId());
                if (!componentInfoService.save(componentInfo)) {
                    return createErrorModel(model, "添加3DE部件层级失败,请联系开发人员");
                }
                if (!savePermissionList(componentInfo, productInfo.getProductId())) {
                    return createErrorModel(model, "添加3DE部件权限失败,请联系开发人员");
                }
            }
            // é›¶ä»¶ - å›¾å·ï¼ˆé›¶ä»¶å·ã€é›¶ä»¶åç§°ï¼‰
            PartsInfo partsInfo = partsInfoService.getByCode(thirdDeProgramSource.getPartNo());
            if (partsInfo == null) {
                partsInfo = new PartsInfo();
                partsInfo.setPartsId(IdWorker.getIdStr());
                partsInfo.setComponentId(componentInfo.getComponentId());
                partsInfo.setProductId(productInfo.getProductId());
                partsInfo.setPartsStatus(1);
                partsInfo.setPartsCode(thirdDeProgramSource.getPartNo());
                partsInfo.setPartsName(thirdDeProgramSource.getPartName());
                partsInfo.setPartsModel(thirdDeProgramSource.getPartNo());
                if (!partsInfoService.save(partsInfo)) {
                    return createErrorModel(model, "添加3DE零件层级失败,请联系开发人员");
                }
                // æ·»åŠ é›¶ä»¶æƒé™
                if (!savePartsPermission(partsInfo, productInfo.getProductId())) {
                    return createErrorModel(model, "添加3DE零件权限失败,请联系开发人员");
                }
            }
            // å·¥åº - å·¥è‰ºID(工艺规程编号/临规编号)
            ProcessStream processStream = processStreamService.findByProcessNoAndPartsId(
                    thirdDeProgramSource.getSkgxId(), partsInfo.getPartsId());
            List<ProcessStream> addProcessList = new ArrayList<>();
            List<ProcessStream> updateProcessList = new ArrayList<>();
            if (processStream == null) {
                processStream = new ProcessStream();
                processStream.setProcessId(IdWorker.getIdStr());
                processStream.setProductId(productInfo.getProductId());
                processStream.setComponentId(componentInfo.getComponentId());
                processStream.setPartsId(partsInfo.getPartsId());
                processStream.setProcessCode(thirdDeProgramSource.getSkgxId());
                processStream.setProcessName(thirdDeProgramSource.getSkgxName());
                processStream.setCraftVersion(thirdDeProgramSource.getRevisionNo()); // è®¾ç½®å·¥è‰ºç‰ˆæœ¬
                // è®¾å¤‡ä¿¡æ¯å¤„理
                DeviceManagement deviceManagement = deviceManagementService.findEquipmentIdsFromEqId(
                        thirdDeProgramSource.getNcPlantName(), thirdDeProgramSource.getEquipmentId());
                if (deviceManagement != null && StringUtils.isNotEmpty(deviceManagement.getEquipmentIds())) {
                    processStream.setProcessingEquipmentCode(deviceManagement.getEquipmentIds());
                } else {
                    processStream.setProcessingEquipmentCode(thirdDeProgramSource.getEquipmentId());
                }
                processStream.setProcessingEquipmentOs(thirdDeProgramSource.getNcOs());
                processStream.setProcessingEquipmentModel(thirdDeProgramSource.getNcJcid());
                addProcessList.add(processStream);
            } else {
                // æ›´æ–°æ—¶æ£€æŸ¥ç‰ˆæœ¬æ˜¯å¦å˜æ›´
                if (!Objects.equals(processStream.getCraftVersion(), thirdDeProgramSource.getRevisionNo())) {
                    processStream.setCraftVersion(thirdDeProgramSource.getRevisionNo());
                }
                processStream.setProcessName(thirdDeProgramSource.getSkgxName());
                updateProcessList.add(processStream);
            }
            if (!addProcessList.isEmpty() && !processStreamService.saveBatch(addProcessList)) {
                return createErrorModel(model, "添加3DE工序失败,请联系开发人员");
            }
            if (!updateProcessList.isEmpty() && !processStreamService.updateBatchById(updateProcessList)) {
                return createErrorModel(model, "更新3DE工序失败,请联系开发人员");
            }
            // æ–‡æ¡£åˆ†ç±»
            DocClassification ncDocClass = docClassificationService.getByCode("NC");
            if (ncDocClass == null) {
                return createErrorModel(model, "NC文档分类不存在,请联系开发人员");
            }
            // send文档分类
            DocClassification sendDocClass = docClassificationService.getByCode("SEND");
            if (sendDocClass == null) {
                return createErrorModel(model, "SEND文档分类不存在,请联系开发人员");
            }
            // å¤„理NC文件
            if (thirdDeProgramSource.getNcFiles() != null && !thirdDeProgramSource.getNcFiles().isEmpty()) {
                for (Map.Entry<String, byte[]> entry : thirdDeProgramSource.getNcFiles().entrySet()) {
                    String fileName = entry.getKey();
                    byte[] fileContent = entry.getValue();
                    String suffix = FileUtil.getFileSuffix(fileName);
                    String baseName = FileUtil.getFilenameNonSuffix(fileName);
                    // æŸ¥æ‰¾çŽ°æœ‰æ–‡æ¡£
                    DocInfo docInfo = docInfoService.findByAttrAndDocName(baseName, 5,
                            processStream.getProcessId(), suffix);
                    FileUploadResult fileUploadResult = FileUtil.uploadFileByFileNameAndFis(
                            fileName, new ByteArrayInputStream(fileContent));
                    if (fileUploadResult == null) {
                        return createErrorModel(model, "上传3DE NC文件失败");
                    }
                    boolean success;
                    if (docInfo == null) {
                        // æ–°å¢žæ–‡æ¡£
                        docInfo = new DocInfo();
                        String docId = IdWorker.getIdStr();
                        docInfo.setDocId(docId);
                        docInfo.setDocName(fileUploadResult.getFileName());
                        docInfo.setDocSuffix(fileUploadResult.getFileSuffix());
                        docInfo.setDocStatus(5);
                        // åˆ›å»ºæ–‡æ¡£å…³è”
                        DocRelative docRelative = new DocRelative();
                        docRelative.setAttributionId(processStream.getProcessId());
                        docRelative.setDocId(docInfo.getDocId());
                        docRelative.setAttributionType(5);
                        docRelative.setClassificationId(ncDocClass.getClassificationId());
                        if (!docRelativeService.save(docRelative)) {
                            return createErrorModel(model, "创建3DE文档关联失败");
                        }
                        // ä¿å­˜æ–‡æ¡£æ–‡ä»¶
                        if (getFile(docInfo, fileUploadResult, docFileService)) {
                            return createErrorModel(model, "保存3DE文档文件失败");
                        }
                        success = docInfoService.save(docInfo);
                        // èŽ·å–æœ€æ–°æ–‡æ¡£æ–‡ä»¶
                        DocFile docFile = docFileService.getDocFileNearest(docId);
                        // æ·»åŠ åˆ°è®¾å¤‡
                        if (!linkToDevice(thirdDeProgramSource.getEquipmentId(), docInfo.getDocId(), docFile, sendDocClass)) {
                            return createErrorModel(model, "添加设备文档关联失败");
                        }
                    } else {
                        // æ›´æ–°æ–‡æ¡£ - å¤„理版本控制
                        if (StringUtils.isNotEmpty(processStream.getCraftVersion()) &&
                                StringUtils.isNotEmpty(thirdDeProgramSource.getRevisionNo())) {
                            // ç‰ˆæœ¬å˜æ›´ï¼Œä¿å­˜æ—§ç‰ˆæœ¬æ–‡ä»¶
                            if (!Objects.equals(processStream.getCraftVersion(), thirdDeProgramSource.getRevisionNo())) {
                                this.createFileOldNcPathAndCopyFor3DE(docInfo, thirdDeProgramSource);
                            }
                        }
                        DocFile docFile = getDocFile(docInfo, fileUploadResult);
                        success = docFileService.addDocFile(docFile);
                        if (success) {
                            docInfo.setPublishVersion(docFile.getDocVersion());
                            docInfo.setPublishFileId(docFile.getFileId());
                            success = docInfoService.updateById(docInfo);
                            // æ›´æ–°è®¾å¤‡å…³è”
                            if (!updateDeviceLink(thirdDeProgramSource.getEquipmentId(), docInfo.getDocId(), sendDocClass)) {
                                return createErrorModel(model, "更新设备文档关联失败");
                            }
                        }
                    }
                    if (!success) {
                        return createErrorModel(model, "处理3DE NC文件失败");
                    }
                }
            }
            return model;
        } catch (Exception e) {
            log.error("处理3DE程序数据异常: {}", e.getMessage(), e);
            return createErrorModel(model, "处理3DE程序数据异常: " + e.getMessage());
        }
    }
    /**
     * æ·»åŠ åˆ°è®¾å¤‡
     */
    private boolean linkToDevice(String equipmentId, String docId, DocFile docFile, DocClassification sendDocClass) {
        try {
            DeviceInfo deviceInfo = deviceInfoService.getByDeviceNo(equipmentId);
            if (deviceInfo == null) {
                log.error("DNC中设备不存在: {}", equipmentId);
                return false;
            }
            DocRelative deviceDocRelative = new DocRelative();
            deviceDocRelative.setAttributionId(deviceInfo.getDeviceId());
            deviceDocRelative.setDocId(docId);
            deviceDocRelative.setAttributionType(4);
            deviceDocRelative.setClassificationId(sendDocClass.getClassificationId());
            boolean saved = docRelativeService.save(deviceDocRelative);
            if (!saved) {
                log.error("保存设备文档关联失败: device={}, doc={}", equipmentId, docId);
                return false;
            }
            // å¤åˆ¶æ–‡ä»¶åˆ°è®¾å¤‡è·¯å¾„
            List<String> groupPaths = deviceGroupService.findListParentTreeAll(deviceInfo.getGroupId());
            if (groupPaths != null && !groupPaths.isEmpty()) {
                String devicePath = String.join("/", groupPaths) + "/" + deviceInfo.getDeviceNo();
                // å¤åˆ¶æ–‡ä»¶
                boolean copySuccess = FileUtil.copyFileNc(
                        docFile.getFilePath(),
                        devicePath,
                        docFile.getFileEncodeName(),
                        docFile.getFileName(),
                        docFile.getFileSuffix());
                if (!copySuccess) {
                    log.error("文件复制失败: {} -> {}", docFile.getFilePath(), devicePath);
                    return false;
                }
                // åˆ é™¤æ—§ç‰ˆæœ¬æ–‡ä»¶
                FileUtil.deleteZipFromToSend(
                        devicePath,
                        docFile.getFileName(),
                        docFile.getFileSuffix());
            }
            return true;
        } catch (Exception e) {
            log.error("添加设备文档关联异常: {}", e.getMessage(), e);
            return false;
        }
    }
    /**
     * æ›´æ–°è®¾å¤‡å…³è”
     */
    private boolean updateDeviceLink(String equipmentId, String docId, DocClassification sendDocClass) {
        try {
            DeviceInfo deviceInfo = deviceInfoService.getByDeviceNo(equipmentId);
            if (deviceInfo == null) {
                log.error("DNC中设备不存在: {}", equipmentId);
                return false;
            }
            // æ£€æŸ¥å…³è”是否已存在
            DocRelative existingRel = docRelativeService.findDocRelative(docId,sendDocClass.getClassificationId(), 4,deviceInfo.getDeviceId());
            if (existingRel == null) {
                // åˆ›å»ºæ–°å…³è”
                DocRelative deviceDocRelative = new DocRelative();
                deviceDocRelative.setAttributionId(deviceInfo.getDeviceId());
                deviceDocRelative.setDocId(docId);
                deviceDocRelative.setAttributionType(4);
                deviceDocRelative.setClassificationId(sendDocClass.getClassificationId());
                return docRelativeService.save(deviceDocRelative);
            }
            return true;
        } catch (Exception e) {
            log.error("更新设备关联异常: {}", e.getMessage(), e);
            return false;
        }
    }
    /**
     * ä¸º3DE数据创建旧版本NC文件备份
     */
    private boolean createFileOldNcPathAndCopyFor3DE(DocInfo docInfo, ThirdDeProgramSource thirdDeProgramSource) {
        DocFile docFile = docFileService.getDocFileNearest(docInfo.getDocId());
        if (docFile == null) {
            log.error("未找到最近版本的3DE文档文件,docId: {}", docInfo.getDocId());
            return false;
        }
        String directoryPath = String.join(File.separator,
                "3de_bak",
                thirdDeProgramSource.getNcPlantName(),
                thirdDeProgramSource.getPartNo(),
                thirdDeProgramSource.getSkgxId(),
                "");
        File directory = new File(directoryPath);
        if (!directory.exists() && !directory.mkdirs()) {
            log.error("创建3DE目录失败: {}", directoryPath);
            return false;
        }
        // æž„建目标文件路径
        String targetFileName = buildTargetFileName(docFile);
        String targetFilePath = directoryPath + targetFileName;
        try {
            Path sourcePath = Paths.get(docFile.getFilePath());
            Path targetPath = Paths.get(targetFilePath);
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            log.info("3DE文件复制成功: {} -> {}", docFile.getFilePath(), targetFilePath);
            return true;
        } catch (IOException e) {
            log.error("3DE文件复制失败: {}", e.getMessage(), e);
            if (directory.exists() && isDirectoryEmpty(directory)) {
                directory.delete();
            }
            return false;
        }
    }
    /**
     * ä¿å­˜é›¶ä»¶æƒé™
     */
    private boolean savePartsPermission(PartsInfo partsInfo, String productId) {
        List<PermissionStream> oldPermissionList = permissionStreamService.getAllByProductId(productId);
        if (CollectionUtils.isEmpty(oldPermissionList)) {
            return true;
        }
        List<PartsDepartment> partsDepartPermList = new ArrayList<>();
        List<PermissionStream> partsPermList = new ArrayList<>();
        List<PartsPermission> partsUserPermList = new ArrayList<>();
        for (PermissionStream p : oldPermissionList) {
            if (ValidateUtil.validateString(p.getUserId())) {
                // ç”¨æˆ·æƒé™
                PartsPermission permission = new PartsPermission();
                permission.setPartsId(partsInfo.getPartsId());
                permission.setUserId(p.getUserId());
                partsUserPermList.add(permission);
                PermissionStream stream = new PermissionStream();
                stream.setUserId(p.getUserId());
                stream.setProductId(partsInfo.getProductId());
                stream.setComponentId(partsInfo.getComponentId());
                stream.setPartsId(partsInfo.getPartsId());
                partsPermList.add(stream);
            }
            if (ValidateUtil.validateString(p.getDepartId())) {
                // éƒ¨é—¨æƒé™
                PartsDepartment department = new PartsDepartment();
                department.setPartsId(partsInfo.getPartsId());
                department.setDepartId(p.getDepartId());
                partsDepartPermList.add(department);
                PermissionStream stream = new PermissionStream();
                stream.setProductId(partsInfo.getProductId());
                stream.setComponentId(partsInfo.getComponentId());
                stream.setPartsId(partsInfo.getPartsId());
                stream.setDepartId(p.getDepartId());
                partsPermList.add(stream);
            }
        }
        boolean success = true;
        if (!partsUserPermList.isEmpty()) {
            success = success && partsPermissionService.saveBatch(partsUserPermList);
        }
        if (!partsDepartPermList.isEmpty()) {
            success = success && partsDepartmentService.saveBatch(partsDepartPermList);
        }
        if (!partsPermList.isEmpty()) {
            success = success && permissionStreamService.saveBatch(partsPermList);
        }
        return success;
    }
    private MesResultModel createErrorModel(MesResultModel model, String errorMsg) {
        model.setSuccFlag("失败");
        model.setError(errorMsg);
        return model;
    }
    /**
     * * NC程序齐套接口(MES)-DNC系统接收到PLM传的NC程序,将程序包对应的零件号、工序号、设备编号传给MES系统
     * å®šæ—¶æ‰«æPlmProgramSourceInfo表中的数据,进行数据同步,每分钟同步一次
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    @Transactional(rollbackFor = Exception.class)
    public void syncWorkmanship() {
        // èŽ·å–æ‰€æœ‰éœ€è¦åŒæ­¥çš„æ•°æ®
        List<PlmProgramSourceInfo> plmProgramSourceInfos = iPlmProgramSourceInfoService.list(
                new LambdaQueryWrapper<PlmProgramSourceInfo>()
                        .eq(PlmProgramSourceInfo::getMesStatus, "0")
                        .orderByAsc(PlmProgramSourceInfo::getId)
        );
        if (plmProgramSourceInfos.isEmpty()) {
            log.info("无待同步数据,跳过处理");
            return;
        }
        List<NcProgramInfo> ncProgramInfos = plmProgramSourceInfos.stream().map(item -> {
            NcProgramInfo ncProgramInfo = new NcProgramInfo();
            ncProgramInfo.setMdsItemCode(item.getCzkAe8nPartNo());
            ncProgramInfo.setRevisionNo(item.getRevisionNo());
            ncProgramInfo.setNcProgramName(item.getFileName());
            ncProgramInfo.setOpreationSeqNo(item.getCzkAe8nOpNo());
            ncProgramInfo.setEquipmentId(item.getCzkAe8nEquipNo());
            ncProgramInfo.setWorkshop(item.getCzkAe8nDivision());
//            ncProgramInfo.setMesId(item.getId()); // ç¡®ä¿ç”Ÿæˆç¬¦åˆè¦æ±‚çš„mesId
//            ncProgramInfo.setOperFlag();
            return ncProgramInfo;
        }).collect(Collectors.toList());
        log.info("WebService-NC程序齐套接口(MES)同步开始,时间: {}", new Date());
        try {
            String jsonPayload = JSONObject.toJSONString(ncProgramInfos);
            log.debug("上报数据: {}", jsonPayload);
            // è°ƒç”¨WebService并获取详细响应
            String result = CxfClientUtil.invokeService(url, jsonPayload, namespace, method);
            log.info("接口响应: {}", result);
            // è§£æžå“åº”结果,只会有成功和失败
            if (result.contains("成功")) {
                List<PlmProgramSourceInfo> toUpdate = new ArrayList<>();
                plmProgramSourceInfos.forEach(item -> {
                    item.setMesStatus("1");
                    toUpdate.add(item);
                });
                // æ‰¹é‡æ›´æ–°çŠ¶æ€
                if (!toUpdate.isEmpty()) {
                    iPlmProgramSourceInfoService.updateBatchById(toUpdate);
                    log.info("成功更新{}条数据状态", toUpdate.size());
                }
            }else {
                log.error("同步MES系统失败: {}", result);
            }
        } catch (Exception e) {
            log.error("同步MES系统失败: {}", e.getMessage(), e);
            // ä»…标记处理失败的记录
            plmProgramSourceInfos.forEach(item -> item.setMesStatus("0"));
            iPlmProgramSourceInfoService.updateBatchById(plmProgramSourceInfos);
            throw new RuntimeException("同步失败,已回滚状态", e); // è§¦å‘事务回滚
        }
    }
    /**
@@ -644,17 +844,6 @@
        }
        String[] files = directory.list();
        return files == null || files.length == 0;
    }
    private static String buildRequestBody(NcProgramInfo ncProgramInfo) {
        StringBuilder body = new StringBuilder();
        body.append("mesId=").append(ncProgramInfo.getMesId())
                .append("&mdsItemCode=").append(ncProgramInfo.getMdsItemCode())
                .append("&revisionNo=").append(ncProgramInfo.getRevisionNo())
                .append("&opreationSeqNo=").append(ncProgramInfo.getOpreationSeqNo())
                .append("&equipmentId=").append(ncProgramInfo.getEquipmentId())
                .append("&workshop=").append(ncProgramInfo.getWorkshop());
        return body.toString();
    }
src/main/resources/application.yml
@@ -118,9 +118,6 @@
dnc:
  server:
    url: http://localhost:8199
ncPdm:
  file_path:
    d://lxzn_storage/plm
fileOldNcPath: dnc//plm_bak  #旧文件记录地址
webservice:
  url: http://20.10.193.21:9308/webservice/mes/eqiupment/v1/mdc2mes.asmx?wsdl