|
package org.springblade.mdm.program.service;
|
|
import lombok.AllArgsConstructor;
|
import lombok.Data;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springblade.core.log.exception.ServiceException;
|
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.basesetting.machine.entity.Machine;
|
import org.springblade.mdm.basesetting.machine.service.MachineService;
|
import org.springblade.mdm.commons.service.ParamService;
|
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.program.entity.DncBackFile;
|
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.service.programannotation.*;
|
import org.springblade.mdm.program.vo.DncSendBackData;
|
import org.springblade.mdm.program.vo.DncSendBackFile;
|
import org.springblade.mdm.program.vo.ProgramAnnotation;
|
import org.springblade.mdm.utils.FileContentUtil;
|
import org.springblade.mdm.utils.ZipTextFileContentUtil;
|
import org.springblade.system.pojo.entity.DictBiz;
|
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<NcProgramExchangeMapper, NcProgramExchange> {
|
private final CureFlowService cureFlowService;
|
private final ProgramAnnotationService programAnnotationService;
|
private final NcNodeService ncNodeService;
|
private final OssTemplate ossTemplate;
|
private final BladeRedis bladeRedis;
|
private final FlowCommonService flowCommonService;
|
private final DncBackFileService dncBackFileService;
|
private final MachineService machineService;
|
private final ParamService paramService;
|
|
/**
|
* 偏离单文件末尾的模式:P+数字
|
*/
|
private static final String P_NUMBER_PATTERN = "(?i)P\\d+";
|
|
private String getFileKey(){
|
return "dncimpfile-"+ AuthUtil.getUserId();
|
}
|
/**
|
* dnc回传文件上传
|
* @param file DNC回传文件
|
* @return 压缩包内程序包名的列表
|
*/
|
public List<DncSendBackData> dncSendBackUpload(MultipartFile file) {
|
List<DncSendBackData> 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));
|
|
try(InputStream zipFileInputStream = ossTemplate.statFileStream(bfile.getName());) {
|
list = parseProgramListFromZip(zipFileInputStream);
|
}
|
} catch (IOException e) {
|
log.error("上传dnc回传文件失败",e);
|
throw new ServiceException("解析DNC回传数据失败");
|
}
|
return list;
|
}
|
|
|
/**
|
* 从压缩包 解析回传程序列表,这里解析目录即可,目录就是程序包名
|
* @param inputStream 压缩包输入流
|
* @return 回传程序列表
|
* @throws IOException 文件操作异常
|
*/
|
List<DncSendBackData> parseProgramListFromZip(InputStream inputStream) throws IOException {
|
List<DncSendBackData> list = new ArrayList<>();
|
Path tempZipFile = createTempFile(inputStream);
|
|
List<String> fileEntryNameList = new ArrayList<>();
|
List<String> dirEntryNameList = new ArrayList<>();//程序包名+工序版次 作为文件夹名
|
try (ZipFile zipFile = new ZipFile(tempZipFile.toFile())) {
|
|
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
|
ZipEntry entry;
|
//获取所有的entry名称
|
while (zipEntries.hasMoreElements()) {
|
entry = zipEntries.nextElement();
|
|
if(entry.isDirectory()){
|
dirEntryNameList.add(entry.getName());
|
}else{
|
fileEntryNameList.add(entry.getName());
|
}
|
}
|
|
List<DictBiz> annotionDictList = programAnnotationService.getAnnotionDictList();
|
NcNode programPackageNode;
|
//目录列表,即程序包列表
|
for(String entryName : dirEntryNameList){
|
DncSendBackData progData = new DncSendBackData();
|
String packageName = StringUtils.removeEnd(entryName,"/");
|
|
progData.setProgramName(packageName);
|
Optional<String> optFilename = fileEntryNameList.stream().filter(n -> n.startsWith(entryName)).findFirst();
|
if(optFilename.isPresent()){
|
entry = zipFile.getEntry(optFilename.get());
|
InputStream ins = zipFile.getInputStream(entry);
|
ByteArrayInputStream bais = new ByteArrayInputStream(IOUtils.toByteArray(ins));;
|
progData.setFileBackTime(DateUtil.fromInstant(entry.getLastModifiedTime().toInstant()));
|
|
AnnotationProperties defAnnoProperties =AnnotationProperties.getDefault();
|
String statusLine = FileContentUtil.readLineAt(bais,defAnnoProperties.getStatusLineIndex());//状态注释行
|
bais.reset();
|
String sendPathLine = FileContentUtil.readLineAt(bais,defAnnoProperties.getSendPathLineIndex());//状态注释行
|
bais.reset();
|
|
if(statusLine.contains("GH")){
|
//固化,不应回传,忽略
|
log.warn("状态{},不应回传,忽略",statusLine);
|
continue;
|
}
|
|
Machine machine = this.machineService.getMachineBySendPathAnnotation(sendPathLine);
|
if(machine == null){
|
throw new ServiceException("根据下发路径未找到程序对应的机床:"+sendPathLine);
|
}
|
|
if(statusLine.contains(AnnotationUtil.SQ)){
|
//试切
|
programPackageNode = ncNodeService.getLastEditionTryingProgramPackage(packageName);//TODO 还需根据机床组(如何获取?根据下发路径获取机床,进而获取),,processEdition
|
}else if(statusLine.contains(AnnotationUtil.LG)){
|
//临时更改单
|
programPackageNode =ncNodeService.getLastEditionDeviationProgramPackage(packageName);
|
}else{
|
throw new ServiceException("状态注释不在范围内:"+statusLine+",仅试切、临时更改单可以回传");
|
}
|
|
if(programPackageNode != null) {
|
progData.setId(programPackageNode.getId());
|
progData.setProgramNo(programPackageNode.getProgramNo());
|
|
List<String> fileEntryNames = fileEntryNameList.stream().filter(n -> n.startsWith(packageName)).toList();
|
List<DncSendBackFile> programFiles = new ArrayList<>();
|
fileEntryNames.forEach( filePath ->{
|
DncSendBackFile backFile = new DncSendBackFile();
|
backFile.setEntryName(filePath);
|
backFile.setName(StringUtils.removeStart(filePath,entryName));
|
programFiles.add(backFile);
|
});
|
|
progData.setFiles(programFiles);
|
list.add(progData);
|
}else{
|
throw new ServiceException("找不到程序包名:"+packageName);
|
}
|
}else{
|
throw new ServiceException(entryName+"包下未找到文件");
|
}
|
|
}
|
}
|
|
return list;
|
}
|
|
/**
|
* 从文件夹名解析出程序包名和和工序版次
|
* @param folderName 文件夹名
|
* @return 结构数据
|
*/
|
/*
|
PackageAndProcessEdition parseProgramPackageFromFolderName(String folderName){
|
int index = StringUtils.lastIndexOf(folderName,'-');
|
String processEditon = "";
|
String packageName = "";
|
if(index != -1){
|
processEditon = folderName.substring(index+1);
|
packageName = folderName.substring(0,index);
|
}
|
|
PackageAndProcessEdition result = new PackageAndProcessEdition();;
|
result.setProgramPackageName(packageName);
|
result.setProcessEdition(processEditon);
|
return result;
|
}*/
|
|
/**
|
* 入库回传文件,并启动固化流程
|
* @param ids id列表逗号分隔,程序包名 节点的id
|
*/
|
@Transactional
|
public void dncFileAccept(String ids) throws IOException {
|
List<Long> acceptIdList = Func.toLongList(ids);
|
//
|
NcProgramExchange exchange;
|
String filekey = getFileKey();
|
String zipFileName = bladeRedis.get(filekey);
|
log.info("filekey={},文件名={}",filekey,zipFileName);
|
|
Map<Long,List<FlowProgramFile>> pkgIdFileMap = dealWithBackFile(zipFileName,acceptIdList);
|
|
cureFlowService.startCureNew(pkgIdFileMap);
|
|
}
|
|
/**
|
* 处理回传文件
|
* @param ossFileName
|
* @param acceptIdList
|
* @return
|
* @throws IOException
|
*/
|
private Map<Long, List<FlowProgramFile>> dealWithBackFile(String ossFileName, List<Long> acceptIdList) throws IOException{
|
Map<Long, List<FlowProgramFile>> pkgIdFileMap = new HashMap<>();
|
|
InputStream inputStream = this.ossTemplate.statFileStream(ossFileName);
|
Path tempZipFile = createTempFile(inputStream);
|
List<String> entryNameList = new ArrayList<>();
|
|
ZipEntry entry;
|
try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(tempZipFile.toFile())) {
|
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
while(entries.hasMoreElements()) {
|
entry = entries.nextElement();
|
entryNameList.add(entry.getName());
|
}
|
log.info("allentrynames:{}",entryNameList);
|
|
List<NcNode> allAcceptPackages = this.ncNodeService.lambdaQuery().in(NcNode::getId,acceptIdList).list();
|
//根据内部文件,读取和分析程序包和程序文件数据
|
List<String> dirList = entryNameList.stream().filter(s -> s.endsWith("/")).toList();
|
for(String dir : dirList){
|
String programPackageName = StringUtils.removeEnd(dir,"/");
|
|
Optional<NcNode> 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 + "已经固化,请勿重复入库。");
|
}
|
//偏离程序判断是否重复回传
|
if(packageNode.isDeviationProgram() && packageNode.hasLocked()) {
|
throw new ServiceException(programPackageName + "已锁定的程序不可以再次回传。");
|
}
|
//检查是否在审批过程中
|
boolean active = flowCommonService.isProcessInstanceActive(packageNode.getProcessInstanceId());
|
if(active){
|
throw new ServiceException(programPackageName+"正在审批中,请勿等待审批完成。");
|
}
|
|
//验证都过了,保存dncbackFile
|
DncBackFile backFile = new DncBackFile();
|
backFile.setNcNodeId(packageNode.getId());
|
backFile.setOssName(ossFileName);
|
dncBackFileService.save(backFile);
|
|
List<FlowProgramFile> 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);//去除文件名路径部分
|
fileName = removeDeviationPart(fileName);
|
try {
|
FlowProgramFile newFlowFile = new FlowProgramFile();
|
newFlowFile.setProgramName(packageNode.getName());
|
newFlowFile.setProcessInstanceId(null);//先置为空,启动流程后设置该值
|
newFlowFile.setFileType(FlowProgramFile.TYPE_PROGRAM);
|
newFlowFile.setName(fileName);
|
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;
|
}
|
|
String removeDeviationPart(String filename){
|
String finalFilename = filename;
|
//去掉文件名中可能带有的偏离单部分:-P[序号]
|
String ext = FilenameUtils.getExtension(filename);
|
String dotExt = StringUtils.isNotBlank(ext)?"."+ext:ext;//带点的扩展名
|
String notExtName = StringUtils.removeEnd(filename,dotExt);
|
int idx = notExtName.lastIndexOf("-");
|
if(idx != -1){
|
String endPart = notExtName.substring(idx+1);
|
//Pattern.CASE_INSENSITIVE
|
boolean containsPld = endPart.matches(P_NUMBER_PATTERN);
|
if(containsPld){
|
finalFilename = notExtName.substring(0, idx)+dotExt;
|
}
|
}
|
|
return finalFilename;
|
}
|
/**
|
* 创建一个临时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;
|
}
|
|
/**
|
* 获取回传文件的内容
|
* @param entryName 文件在压缩包内的路径
|
* @return 文件内容文本
|
*/
|
public String getEntryFileContent(String entryName) throws IOException {
|
String result = "";
|
String zipFileName = bladeRedis.get(getFileKey());
|
return ZipTextFileContentUtil.getTextContent(this.ossTemplate.statFileStream(zipFileName),entryName);
|
|
/*
|
try(InputStream inputStream = this.ossTemplate.statFileStream(zipFileName);){
|
Path tempZipFile = createTempFile(inputStream);
|
|
ZipEntry entry;
|
try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(tempZipFile.toFile())) {
|
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
while (entries.hasMoreElements()) {
|
entry = entries.nextElement();
|
if (!entryName.equals(entry.getName())) {
|
continue;
|
}
|
try (InputStream fileIns = zipFile.getInputStream(zipFile.getEntry(entryName))) {
|
ByteArrayInputStream bos = new ByteArrayInputStream(fileIns.readAllBytes());
|
boolean isText = StringUtils.endsWithIgnoreCase(entryName,".txt") || StringUtils.endsWithIgnoreCase(entryName,".nc")|| StringUtils.endsWithIgnoreCase(entryName,".xml");
|
if(!isText) {
|
isText = FileContentUtil.isTextFile(bos);
|
}
|
if (isText) {
|
bos.reset();
|
result = FileContentUtil.getContentFromStream(bos);
|
} else {
|
result = "<非文本文件>";
|
}
|
}
|
|
}
|
}
|
}
|
|
return result;*/
|
}
|
|
}
|
|
@Data
|
class PackageAndProcessEdition{
|
private String programPackageName;
|
private String processEdition;
|
|
public String programName(){
|
return programPackageName+"-"+processEdition;
|
}
|
}
|