package org.springblade.mdm.program.service; import com.alibaba.fastjson.JSONObject; 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.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.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 String getFileKey(){ return "dncexpfile-"+ AuthUtil.getUserId(); } /** * dnc回传文件上传 * @param file DNC回传文件 * @return 压缩包内程序包名的列表 */ public List dncSendBackUpload(MultipartFile file) { List list; try { BladeFile bfile = ossTemplate.putFile(file);//上传,供后续入库使用 //设置一个缓存,2小时过期 bladeRedis.setEx(getFileKey(),bfile.getName(), Duration.ofHours(2)); InputStream zipFileInputStream = file.getInputStream();//test //byte[] bytes = FileUtil.copyToByteArray(zipFileInputStream); list = parseDncZipFromByteArray(zipFileInputStream); } catch (IOException e) { log.error("上传dnc回传文件失败",e); throw new ServiceException("解析DNC回传数据失败"); } return list; } /** * 从压缩包 解析回传程序列表,这里解析目录即可,目录就是程序包名 * @param inputStream 压缩包输入流 * @return 回传程序列表 * @throws IOException 文件操作异常 */ List parseDncZipFromByteArray(InputStream inputStream) throws IOException { List list = new ArrayList<>(); Path tempZipFile = createTempFile(inputStream); try (ZipFile zipFile = new ZipFile(tempZipFile.toFile())) { ZipEntry entry; Enumeration zipEntris = zipFile.entries(); while (zipEntris.hasMoreElements()) { entry = zipEntris.nextElement(); DncSendBackData progData = new DncSendBackData(); String entryName = entry.getName(); if (entry.isDirectory()){ //目录,才是程序包, //查询数据库,对应上才能确认时有效的程序包 String packageName = StringUtils.removeEnd(entryName,"/"); List pkgList = ncNodeService.lambdaQuery().eq(NcNode::getNodeType,NcNode.TYPE_PROGRAM_PACKAGE).eq(NcNode::getName, packageName).eq(NcNode::getIsLastEdition,1).list(); if(!pkgList.isEmpty()) { NcNode programPackageNode = pkgList.get(0); progData.setId(programPackageNode.getId()); progData.setProgramName(packageName); progData.setFileBackTime(DateUtil.fromInstant(entry.getLastModifiedTime().toInstant())); progData.setProgramNo(programPackageNode.getProgramNo()); list.add(progData); } } } } return list; } /** * 入库回传文件,并启动固化流程 * @param ids id列表逗号分隔,程序包名 节点的id */ @Transactional public void dncFileAccept(String ids) throws IOException { List idList = Func.toLongList(ids); // NcProgramExchange exchange; String filekey = getFileKey(); String pkgFileName = bladeRedis.get(filekey); log.info("filekey={},文件名={}",filekey,pkgFileName); Map> programPackageSubMap = new HashMap<>(); List newProgramPckageList = updateNodeDataByDNCBackData(pkgFileName,idList,programPackageSubMap); log.info("需要启动固化流程的程序包名数量:{}",newProgramPckageList.size()); for(NcNode pkgNode :newProgramPckageList){ exchange = new NcProgramExchange(); exchange.setName(pkgNode.getName()); exchange.setExchangeType(2);//回传 exchange.setNcNodeId(pkgNode.getId()); this.save(exchange); } bladeRedis.del(filekey); this.ossTemplate.removeFile(pkgFileName); log.info("删除oss文件:{}",pkgFileName); cureFlowService.startCure(newProgramPckageList,programPackageSubMap); } /** * 更新节点,主要是创建 程序包名 的新版本。 * @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 entris = zipFile.entries(); while(entris.hasMoreElements()) { entry = entris.nextElement(); entryNameList.add(entry.getName()); } //根据内部文件,读取和分析程序包和程序文件数据 List dirList = entryNameList.stream().filter(s -> s.endsWith("/")).toList(); for(String dir : dirList){ String programPackageName = StringUtils.removeEnd(dir,"/"); NcNode oriProgramPkg = this.ncNodeService.getLastEditionProgramPackage(programPackageName); if(oriProgramPkg == null){ log.warn("未发现匹配的程序包名{}",programPackageName); continue; } if(!programPackageIdList.contains(oriProgramPkg.getId())){ //不在勾选的范围内 log.info("{}不在勾选范围内",programPackageName); continue; } NcNode newProgramPkg = new NcNode(); BeanUtils.copyProperties(oriProgramPkg, newProgramPkg); clearBaseProperties(newProgramPkg); newProgramPkg.setIsLastEdition(1); ncNodeService.save(newProgramPkg); newProgramPackageNodeList.add(newProgramPkg); //旧数据更新为老版本 oriProgramPkg.setIsLocked(1);//旧版自动锁定 oriProgramPkg.setIsLastEdition(0);; ncNodeService.updateById(oriProgramPkg); //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,oriProgramPkg.getId()); if(oldProgramNode == null){ log.info("{}找不到程序文件",entryName); return; } NcNode newProgramNode = new NcNode(); BeanUtils.copyProperties(oldProgramNode, newProgramNode); clearBaseProperties(newProgramNode); newProgramNode.setIsLastEdition(1); newProgramNode.setParentId(newProgramPkg.getId()); newProgramNode.setParentIds(newProgramPkg.getParentIds()+","+newProgramPkg.getId()); //ncNodeService.save(newProgramNode); //将回传文件上传,并记录到数据库 FlowProgramFile oldFlowFile = flowProgramFileService.getById(newProgramNode.getFlowProgramFileId()); FlowProgramFile newFlowFile = new FlowProgramFile();//TODO BeanUtils.copyProperties(oldFlowFile, newFlowFile); newFlowFile.setProcessInstanceId(null); 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); oldProgramNode.setIsLastEdition(0); oldProgramNode.setIsLocked(1); ncNodeService.updateById(oldProgramNode); } }); 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); } }