package org.springblade.mdm.program.service; import com.alibaba.fastjson.JSONObject; import io.netty.util.internal.StringUtil; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.mp.base.BizEntity; import org.springblade.core.mp.base.BizServiceImpl; import org.springblade.core.oss.OssTemplate; import org.springblade.core.oss.model.BladeFile; import org.springblade.core.redis.cache.BladeRedis; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.FileUtil; import org.springblade.core.tool.utils.Func; import org.springblade.mdm.flow.entity.FlowProgramFile; import org.springblade.mdm.flow.service.CureFlowService; import org.springblade.mdm.flow.service.FlowCommonService; import org.springblade.mdm.flow.service.FlowProgramFileService; import org.springblade.mdm.program.entity.NcNode; import org.springblade.mdm.program.entity.NcProgramExchange; import org.springblade.mdm.program.mapper.NcProgramExchangeMapper; import org.springblade.mdm.program.vo.DncSendBackData; import org.springblade.mdm.utils.EntityUtil; import org.springblade.mdm.utils.FileContentUtil; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.time.Duration; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * DNC回传文件处理服务 * * @author yangys */ @Slf4j @Service @AllArgsConstructor public class DNCSendBackService extends BizServiceImpl { private final CureFlowService cureFlowService; private final FlowProgramFileService flowProgramFileService; private final NcNodeService ncNodeService; private final OssTemplate ossTemplate; private final BladeRedis bladeRedis; private final FlowCommonService flowCommonService; private String getFileKey(){ return "dncexpfile-"+ AuthUtil.getUserId(); } /** * dnc回传文件上传 * @param file DNC回传文件 * @return 压缩包内程序包名的列表 */ public List dncSendBackUpload(MultipartFile file) { List list; if(file == null || file.isEmpty()){ throw new ServiceException("文件为空"); } if(!StringUtils.endsWith(file.getOriginalFilename(),".zip")){ throw new ServiceException("文件必须为zip包"); } try { BladeFile bfile = ossTemplate.putFile(file);//上传,供后续入库使用 //设置一个缓存,2小时过期 bladeRedis.setEx(getFileKey(),bfile.getName(), Duration.ofHours(2)); InputStream zipFileInputStream = file.getInputStream();//test list = parseProgramListFromZip(zipFileInputStream); } catch (IOException e) { log.error("上传dnc回传文件失败",e); throw new ServiceException("解析DNC回传数据失败"); } return list; } /** * 从压缩包 解析回传程序列表,这里解析目录即可,目录就是程序包名 * @param inputStream 压缩包输入流 * @return 回传程序列表 * @throws IOException 文件操作异常 */ List parseProgramListFromZip(InputStream inputStream) throws IOException { List list = new ArrayList<>(); Path tempZipFile = createTempFile(inputStream); List fileEntryNameList = new ArrayList<>(); List dirEntryNameList = new ArrayList<>(); try (ZipFile zipFile = new ZipFile(tempZipFile.toFile())) { Enumeration zipEntries = zipFile.entries(); ZipEntry entry; //获取所有的entry名称 while (zipEntries.hasMoreElements()) { entry = zipEntries.nextElement(); if(entry.isDirectory()){ dirEntryNameList.add(entry.getName()); }else{ fileEntryNameList.add(entry.getName()); } } NcNode programPackageNode; //目录列表,即程序包列表 for(String entryName : dirEntryNameList){ DncSendBackData progData = new DncSendBackData(); String packageName = StringUtils.removeEnd(entryName,"/"); progData.setProgramName(packageName); String statusLine = ""; Optional optFilename = fileEntryNameList.stream().filter(n -> n.startsWith(packageName)).findFirst(); if(optFilename.isPresent()){ entry = zipFile.getEntry(optFilename.get()); InputStream ins = zipFile.getInputStream(entry); progData.setFileBackTime(DateUtil.fromInstant(entry.getLastModifiedTime().toInstant())); statusLine = FileContentUtil.readLineAt(ins,2); if(statusLine.contains("SQ")){ //试切 programPackageNode = ncNodeService.getLastEditionTryingProgramPackage(packageName); }else if(statusLine.contains("GH")){ //固化 programPackageNode = ncNodeService.getLastEditionCuredProgramPackage(packageName); }else if(statusLine.contains("PL")){ //偏离 programPackageNode =ncNodeService.getLastEditionDeviationProgramPackage(packageName); }else{ throw new ServiceException("未找到程序文件中的状态注释"); } if(programPackageNode != null) { progData.setId(programPackageNode.getId()); progData.setProgramNo(programPackageNode.getProgramNo()); list.add(progData); }else{ throw new ServiceException("找不到程序包名:"+packageName+statusLine); } }else{ throw new ServiceException(entryName+"包下未找到文件"+statusLine); } } } return list; } /** * 入库回传文件,并启动固化流程 * @param ids id列表逗号分隔,程序包名 节点的id */ @Transactional public void dncFileAccept(String ids) throws IOException { List acceptIdList = Func.toLongList(ids); // NcProgramExchange exchange; String filekey = getFileKey(); String zipFileName = bladeRedis.get(filekey); log.info("filekey={},文件名={}",filekey,zipFileName); Map> programPackageSubMap = new HashMap<>(); Map> pkgIdFileMap = dealWithBackFile(zipFileName,acceptIdList); ///List newProgramPackageList = updateNodeDataByDNCBackData(pkgFileName,idList,programPackageSubMap); //updateNodeDataByDNCBackData(pkgFileName,idList,programPackageSubMap); /* log.info("需要启动固化流程的程序包名数量:{}",newProgramPackageList.size()); for(NcNode pkgNode :newProgramPackageList){ exchange = new NcProgramExchange(); exchange.setName(pkgNode.getName()); exchange.setExchangeType(2);//回传 exchange.setNcNodeId(pkgNode.getId()); this.save(exchange); }*/ bladeRedis.del(filekey); this.ossTemplate.removeFile(zipFileName); log.info("删除oss文件:{}",zipFileName); //cureFlowService.startCure(newProgramPackageList,programPackageSubMap); cureFlowService.startCureNew(pkgIdFileMap); } private Map> dealWithBackFile(String ossFileName, List acceptIdList) throws IOException{ Map> pkgIdFileMap = new HashMap<>(); InputStream inputStream = this.ossTemplate.statFileStream(ossFileName); Path tempZipFile = createTempFile(inputStream); List entryNameList = new ArrayList<>(); ZipEntry entry; try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(tempZipFile.toFile())) { Enumeration entries = zipFile.entries(); while(entries.hasMoreElements()) { entry = entries.nextElement(); entryNameList.add(entry.getName()); } log.info("allentrynames:{}",entryNameList); List allAcceptPackages = this.ncNodeService.lambdaQuery().in(NcNode::getId,acceptIdList).list(); //根据内部文件,读取和分析程序包和程序文件数据 List dirList = entryNameList.stream().filter(s -> s.endsWith("/")).toList(); for(String dir : dirList){ String programPackageName = StringUtils.removeEnd(dir,"/"); Optional optPackageNode = allAcceptPackages.stream().filter(node -> StringUtils.equals(node.getName(),programPackageName)).findFirst(); if(optPackageNode.isEmpty()){ throw new ServiceException("找不到程序"+programPackageName); } NcNode packageNode = optPackageNode.get(); if(packageNode.hasCured()) { throw new ServiceException(programPackageName + "已经固化,请勿重复入库。"); } //检查是否在审批过程中 //根据节点信息查询流程 boolean active = flowCommonService.isProcessInstanceActive(packageNode.getProcessInstanceId()); if(active){ throw new ServiceException(programPackageName+"正在审批中,请勿重复入库。"); } List flowFiles = new ArrayList<>(); //查找包下的文件数据, entryNameList.stream().filter(s -> s.startsWith(dir)).forEach(entryName -> { log.info("{}下的文件:{}",dir,entryName); if(!entryName.endsWith("/")){ //实际的文件 String fileName = StringUtils.removeStart(entryName,dir);//去除文件名路径部分 try { FlowProgramFile newFlowFile = new FlowProgramFile(); newFlowFile.setProgramName(packageNode.getName()); newFlowFile.setProcessInstanceId(null);//先置为空,启动流程后设置该值 newFlowFile.setFileType("program"); InputStream ins = zipFile.getInputStream(zipFile.getEntry(entryName)); BladeFile newOssFile = ossTemplate.putFile("mdm",fileName,ins); newFlowFile.setOssName(newOssFile.getName()); flowFiles.add(newFlowFile); } catch (IOException e) { throw new RuntimeException(e); } } }); pkgIdFileMap.put(packageNode.getId(),flowFiles); } } return pkgIdFileMap; } /** * 更新节点,主要是创建 程序包名 的新版本。 * @param pkgFileName zip文件名 * @param programPackageIdList 程序包名 节点的id列表 * @param programPackageSubMap 新的 程序包节点id -> =文件列表 map,用于回传数据 * @throws IOException 访问文件异常 */ List updateNodeDataByDNCBackData(String pkgFileName, List programPackageIdList,Map> programPackageSubMap) throws IOException { InputStream inputStream = this.ossTemplate.statFileStream(pkgFileName); Path tempZipFile = createTempFile(inputStream); List newProgramPackageNodeList = new ArrayList<>(); List entryNameList = new ArrayList<>(); ZipEntry entry; try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(tempZipFile.toFile())) { Enumeration entries = zipFile.entries(); while(entries.hasMoreElements()) { entry = entries.nextElement(); entryNameList.add(entry.getName()); } log.info("allentrynames:{}",entryNameList); List allAcceptPackages = this.ncNodeService.lambdaQuery().in(NcNode::getId,programPackageIdList).list(); //根据内部文件,读取和分析程序包和程序文件数据 List dirList = entryNameList.stream().filter(s -> s.endsWith("/")).toList(); for(String dir : dirList){ String programPackageName = StringUtils.removeEnd(dir,"/"); Optional optPackageNode = allAcceptPackages.stream().filter(node -> StringUtils.equals(node.getName(),programPackageName)).findFirst(); if(optPackageNode.isEmpty()){ throw new ServiceException("找不到程序"+programPackageName); } NcNode packageNode = optPackageNode.get(); if(packageNode.hasCured()) { throw new ServiceException(programPackageName + "已经固化,请勿重复入库。"); } //检查是否在审批过程中 //根据节点信息查询流程 boolean active = flowCommonService.isProcessInstanceActive(packageNode.getProcessInstanceId()); if(active){ throw new ServiceException(programPackageName+"正在审批中,请勿重复入库。"); } NcNode newProgramPkg = new NcNode(); BeanUtils.copyProperties(packageNode, newProgramPkg); EntityUtil.clearBaseProperties(newProgramPkg); newProgramPkg.setIsLastEdition(1); ncNodeService.save(newProgramPkg); newProgramPackageNodeList.add(newProgramPkg); //旧数据更新为老版本 packageNode.setIsLocked(1);//旧版自动锁定 packageNode.setIsLastEdition(0);; ncNodeService.updateById(packageNode); //List newFlowFiles = new ArrayList<>(); List newProgramNodes = new ArrayList<>(); //查找包下的文件数据, entryNameList.stream().filter(s -> s.startsWith(dir)).forEach(entryName -> { log.info("{}下的文件:{}",dir,entryName); if(!entryName.endsWith("/")){ //实际的文件 String fileName = StringUtils.removeStart(entryName,dir);//去除文件名路径部分 NcNode oldProgramNode = this.ncNodeService.getLastEditionProgramFile(fileName,packageNode.getId()); if(oldProgramNode == null){ log.info("{}找不到程序文件",entryName); throw new ServiceException(programPackageName+"下找不到程序文件"+fileName); } //创建新版本的程序节点 NcNode newProgramNode = new NcNode(); BeanUtils.copyProperties(oldProgramNode, newProgramNode); EntityUtil.clearBaseProperties(newProgramNode); newProgramNode.setIsLastEdition(1); newProgramNode.setParentId(newProgramPkg.getId()); newProgramNode.setParentIds(newProgramPkg.getParentIds()+","+newProgramPkg.getId()); FlowProgramFile oldFlowFile = flowProgramFileService.getById(newProgramNode.getFlowProgramFileId()); FlowProgramFile newFlowFile = new FlowProgramFile(); BeanUtils.copyProperties(oldFlowFile, newFlowFile); newFlowFile.setProcessInstanceId(null);//先置为空,启动流程后设置该值 EntityUtil.clearBaseProperties(newFlowFile); try { InputStream ins = zipFile.getInputStream(zipFile.getEntry(entryName)); BladeFile newOssFile = ossTemplate.putFile("mdm",fileName,ins); newFlowFile.setOssName(newOssFile.getName()); } catch (IOException e) { throw new RuntimeException(e); } newProgramNode.setFlowProgramFile(newFlowFile); newProgramNode.setVersionNumber(oldProgramNode.genNewVersionNumber()); newProgramNodes.add(newProgramNode); //旧节点处理,咋办?如果导出工控网 重复导出呢?,isLastEdition不用设置了,因为 程序包节点 是新的 } }); programPackageSubMap.put(newProgramPkg.getId(),newProgramNodes); } } return newProgramPackageNodeList; } /** * 创建一个临时zip文件 * @param inputStream 文件的输入流 * @return path 文件 * @throws IOException */ Path createTempFile(InputStream inputStream) throws IOException { byte[] zipData = FileUtil.copyToByteArray(inputStream); Path tempFile = Files.createTempFile("tempzip"+System.currentTimeMillis(), ".zip"); // 写入字节数据到临时文件 Files.write(tempFile, zipData, StandardOpenOption.WRITE); return tempFile; } void setBaseProperties(BizEntity entity, JSONObject jsonObject){ entity.setCreateTime(jsonObject.getDate("createTime")); entity.setUpdateTime(jsonObject.getDate("updateTime")); entity.setStatus(jsonObject.getInteger("status")); entity.setCreateUser(jsonObject.getLong("createUser")); entity.setUpdateUser(jsonObject.getLong("updateUser")); } /* void clearBaseProperties(BizEntity entity){ entity.setId(null); entity.setCreateTime(null); entity.setUpdateTime(null); entity.setStatus(null); entity.setCreateUser(null); entity.setUpdateUser(null); }*/ }