package com.vci.ubcs.resource.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.vci.ubcs.resource.bo.FileObjectBO; import com.vci.ubcs.resource.dto.FileObjectDTO; import com.vci.ubcs.resource.dto.FileReleaseDTO; import com.vci.ubcs.resource.dto.FileShareDTO; import com.vci.ubcs.resource.entity.Attach; import com.vci.ubcs.resource.entity.FileObject; import com.vci.ubcs.resource.life.FileLife; import com.vci.ubcs.resource.mapper.FileObjectMapper; import com.vci.ubcs.resource.query.FileQuery; import com.vci.ubcs.resource.service.IFileService; import com.vci.ubcs.resource.utils.FileUtil; import com.vci.ubcs.resource.utils.ZipUtil; import com.vci.ubcs.resource.vo.FileObjectVO; import com.vci.ubcs.starter.exception.VciBaseException; import com.vci.ubcs.starter.util.DefaultAttrAssimtUtil; import com.vci.ubcs.starter.util.MdmBtmTypeConstant; import com.vci.ubcs.starter.web.enumpck.BaseEnum; import com.vci.ubcs.starter.web.enumpck.UserSecretEnum; import com.vci.ubcs.starter.web.util.BeanUtil; import com.vci.ubcs.starter.web.util.VciBaseUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.oss.MinioTemplate; import org.springblade.core.oss.OssTemplate; import org.springblade.core.oss.model.BladeFile; import org.springblade.core.oss.model.OssFile; import org.springblade.core.oss.props.OssProperties; import org.springblade.core.oss.rule.OssRule; import org.springblade.core.secure.BladeUser; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tool.api.R; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; /** * 文件服务 * @author weidy * @date 2023/2/1 */ @Service @Slf4j public class FileServiceImpl extends ServiceImpl implements IFileService { /** * 文件的数据操作类 */ @Resource private FileObjectMapper fileObjectMapper; /** * 对象的模版 */ @Autowired(required=true) private OssTemplate ossTemplate; /** * 文件服务的配置 */ @Autowired private OssProperties ossProperties; /** * 压缩文件的工具类 */ @Autowired private ZipUtil zipUtil; /*** * 文件的规则 */ @Autowired private OssRule ossRule; /** * 下载,注意多个文件的时候,会自动下载为一个压缩包 * * @param oids 主键集合 * @return 文件的信息 */ @Override public FileObjectBO downloadFileByOids(Collection oids) { VciBaseUtil.alertNotNull(oids,"文件的主键集合"); if(log.isDebugEnabled()){ log.debug("下载文件,",oids.stream().collect(Collectors.joining(","))); } Collection fileVOs = listFilesByOids(oids); FileObjectBO fileObjectBO = new FileObjectBO(); if(fileVOs.size()==1){ //只有一个 FileObjectVO objectVO = fileVOs.stream().findFirst().get(); BeanUtil.convert(objectVO,fileObjectBO); OssFile ossFile = ossTemplate.statFile(objectVO.getBucketName(), objectVO.getObjectName()); if(ossFile == null || !StringUtils.hasLength(ossFile.getLink())){ throw new VciBaseException("文件在存储里没有找到",oids.toArray(new String[0])); } URL url = null; try { url = new URL(ossFile.getLink()); } catch (MalformedURLException e) { throw new VciBaseException("文件的获取路径有问题",new String[]{ossFile.getLink()},e); } try { fileObjectBO.setInputStream(url.openStream()); }catch (Throwable e){ throw new VciBaseException("获取文件的流有问题",new String[]{ossFile.getLink()},e); } }else{ //有多个,需要使用zip进行压缩 String tempFolder = FileUtil.getDefaultTempFolder(); fileVOs.stream().forEach(fileVO->{ String fileName = tempFolder + File.separator + fileVO.getName() + "." + fileVO.getFileExtension(); File file = new File(fileName); try { if(!file.exists()) { file.createNewFile(); } }catch (Throwable e){ throw new VciBaseException("创建文件出错,{0}",new String[]{fileName}); } OssFile ossFile = ossTemplate.statFile(fileVO.getBucketName(), fileVO.getObjectName()); if(ossFile == null || !StringUtils.hasLength(ossFile.getLink())){ throw new VciBaseException("文件在存储里没有找到",oids.toArray(new String[0])); } URL url = null; try { url = new URL(ossFile.getLink()); } catch (MalformedURLException e) { throw new VciBaseException("文件的获取路径有问题",new String[]{ossFile.getLink()},e); } try(OutputStream os = new FileOutputStream(file); InputStream ins = url.openStream(); ){ IOUtils.copy(ins,os); }catch (Throwable e){ throw new VciBaseException("下载文件到临时文件夹里出错,{0}",new String[]{fileName}); } }); String zipName = new File(tempFolder).getPath() + File.separator + fileVOs.stream().findFirst().get().getName() + "等"+fileVOs.size() + "个文件.zip"; zipUtil.folderToZipFile(tempFolder,zipName); fileObjectBO.setFileLocalPath(zipName); if(log.isDebugEnabled()){ log.debug("下载文件的信息,",zipName); } } return fileObjectBO; } /** * 删除文件 * * @param oids 主键集合 * @return 执行结果 */ @Override @Transactional(rollbackFor = Exception.class) public void deleteFiles(Collection oids) { VciBaseUtil.alertNotNull(oids,"文件的主键集合"); if(log.isDebugEnabled()){ log.debug("删除文件,",oids.stream().collect(Collectors.joining(","))); } Collection fileObjects = listByIds(oids); if(!CollectionUtils.isEmpty(fileObjects)){ VciBaseUtil.switchCollectionForOracleIn(fileObjects).stream().forEach(fileDOs->{ fileObjectMapper.deleteBatchIds(fileDOs.stream().map(FileObject::getOid).collect(Collectors.toList())); }); //删除minio里的文件,这儿多条记录删除使用parallelStream多线程时可能会出现tennantId无法拼接上的问题,所以还是用Stream fileObjects.stream().forEach(fileObject -> { ossTemplate.removeFile(fileObject.getBucketName(),fileObject.getObjectName()); }); } } /** * 上传文件 * * @param file 文件的信息 * @param fileObjectDTO 文件的相关信息 * @return 文件上传后的信息 */ @Override @Transactional public R uploadFile(MultipartFile file, FileObjectDTO fileObjectDTO) { VciBaseUtil.alertNotNull(fileObjectDTO,"文件的信息"); String sourceFileName = file.getOriginalFilename(); String fileExtension = FileUtil.getFileExtensionByName(sourceFileName); String fileNameNoExtension = FileUtil.getFileNameUnHasExtensionByName(sourceFileName); if(fileObjectDTO.getUpdateFileFlag()==null || !fileObjectDTO.getUpdateFileFlag()) { return R.data(add(fileObjectDTO,file)); }else{ //修改,需要先获取 VciBaseUtil.alertNotNull(fileObjectDTO.getOid(),"文件的主键"); FileObject fileObject = getById(fileObjectDTO.getOid()); if (StringUtils.hasLength(fileObjectDTO.getId())) { fileObject.setId(fileNameNoExtension); } fileObject.setName(fileNameNoExtension); fileObject.setFileExtension(fileExtension); fileObject.setFileSize(file.getSize()); //以前的信息先获取 String oldObjectName = fileObject.getObjectName(); String oldBucketName = fileObject.getBucketName(); String bucketName = StringUtils.hasLength(fileObjectDTO.getBucketName())?fileObjectDTO.getBucketName():ossProperties.getBucketName(); BladeFile bladeFile = ossTemplate.putFile(bucketName,file.getOriginalFilename(),file); fileObject.setBucketName(bucketName); fileObject.setObjectName(bladeFile.getName()); fileObject.setFileLink(bladeFile.getLink()); fileObjectMapper.updateById(fileObject); //删除以前的 ossTemplate.removeFile(oldBucketName,oldObjectName); if(log.isDebugEnabled()){ log.debug("修改文件",fileObject.toString(),oldObjectName,oldBucketName); } FileObjectVO fileObjectVO = do2Vo(fileObject); return R.data(fileObjectVO); } } private FileObjectVO add(FileObjectDTO fileObjectDTO,MultipartFile file){ String sourceFileName = file.getOriginalFilename(); String fileExtension = FileUtil.getFileExtensionByName(sourceFileName); String fileNameNoExtension = FileUtil.getFileNameUnHasExtensionByName(sourceFileName); //存储业务数据与文件信息 FileObject fileObject = new FileObject(); BeanUtil.convert(fileObjectDTO, fileObject); if (!StringUtils.hasLength(fileObject.getId())) { fileObject.setId(fileNameNoExtension); } if (!StringUtils.hasLength(fileObject.getName())) { fileObject.setName(fileNameNoExtension); } fileObject.setFileExtension(fileExtension); fileObject.setFileSize(file.getSize()); if (!StringUtils.hasLength(fileObject.getOid())) { fileObject.setOid(VciBaseUtil.getPk()); } if (fileObjectDTO.getEffect() != null && fileObjectDTO.getEffect()) { fileObject.setLcStatus(FileLife.FILE_RELEASE.getValue()); } else { fileObject.setLcStatus(FileLife.TRANS_FINISH.getValue()); } if (fileObject.getSecretGrade() == null || fileObject.getSecretGrade() == 0) { // VciBaseUtil.getCurrentUserSecret()还需要处理,在进入服务的时候,根据当前用户的信息获取用户的对象 fileObject.setSecretGrade( VciBaseUtil.getCurrentUserSecret()); } String bucketName = StringUtils.hasLength(fileObjectDTO.getBucketName())?fileObjectDTO.getBucketName():ossProperties.getBucketName(); //先存到Minio后再说 BladeFile bladeFile = ossTemplate.putFile(bucketName,file.getOriginalFilename(),file); fileObject.setBucketName(bucketName); fileObject.setObjectName(bladeFile.getName()); fileObject.setFileLink(bladeFile.getLink()); // 添加之前赋默认值 DefaultAttrAssimtUtil.addDefaultAttrAssimt(fileObject, MdmBtmTypeConstant.FILE_OBJECT); fileObjectMapper.insert(fileObject); if(log.isDebugEnabled()){ log.debug("新增文件",fileObject.toString()); } FileObjectVO fileObjectVO = do2Vo(fileObject); return fileObjectVO; } /** * 数据对象转显示对象 * @param fileObject 文件的数据对象 * @return 显示对象 */ @Override public FileObjectVO do2Vo(FileObject fileObject) { FileObjectVO fileObjectVO = new FileObjectVO(); BeanUtil.convert(fileObject,fileObjectVO); fileObjectVO.setLcStatusText(BaseEnum.getTextByValue(FileLife.class,fileObjectVO.getLcStatus())); //fileObjectVO.setSecretGradeText(UserSecretEnum); return fileObjectVO; } @Override public List do2Vos(Collection fileObjects){ List fileObjectVOList = new ArrayList<>(); Optional.ofNullable(fileObjects).orElseGet(ArrayList::new).stream().forEach(fo->{ fileObjectVOList.add(do2Vo(fo)); }); return fileObjectVOList; } /** * 分享文件到其他数据中 * * @param shareDTO 来源业务 * @return 分享后的文件信息 */ @Override @Transactional public R> shareFiles(FileShareDTO shareDTO) { VciBaseUtil.alertNotNull(shareDTO,"分享的信息",shareDTO.getTargetBus(),"目标的业务名称",shareDTO.getTargetBusOid(),"目标的业务数据主键"); List fileObjectList = new ArrayList<>(); if(!CollectionUtils.isEmpty(shareDTO.getOids())){ fileObjectList.addAll(listByIds(shareDTO.getOids())); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(FileObject.class); if(StringUtils.hasLength(shareDTO.getSourceBus())) { queryWrapper.in(FileObject::getOwnBtmName, VciBaseUtil.str2List(shareDTO.getSourceBus())); } if(StringUtils.hasLength(shareDTO.getSourceBusOid())){ queryWrapper.in(FileObject::getOwnbizOid,VciBaseUtil.str2List(shareDTO.getSourceBusOid())); } fileObjectList.addAll(fileObjectMapper.selectList(queryWrapper)); if(CollectionUtils.isEmpty(fileObjectList)){ throw new VciBaseException("没有找到可以分享的文件"); } BladeUser user = AuthUtil.getUser(); Date now = new Date(); List newFileObjects = new ArrayList<>(); fileObjectList.stream().forEach(fileObject -> { FileObject newFileObject = new FileObject(); BeanUtil.convert(fileObject,newFileObject); newFileObject.setId(VciBaseUtil.getPk()); newFileObject.setNameOid(VciBaseUtil.getPk()); newFileObject.setRevisionOid(VciBaseUtil.getPk()); newFileObject.setCreator(user.getAccount()); newFileObject.setCreateTime(now); newFileObject.setLastModifier(user.getAccount()); newFileObject.setLastModifyTime(now); newFileObject.setTs(now); newFileObject.setOwnBtmName(shareDTO.getTargetBus()); newFileObject.setOwnbizOid(shareDTO.getTargetBusOid()); newFileObject.setObjectName(ossRule.fileName( "." + fileObject.getFileExtension())); //下载文件后,上传文件 ossTemplate.copyFile(fileObject.getBucketName(),fileObject.getObjectName(),fileObject.getBucketName(),newFileObject.getObjectName()); newFileObject.setFileLink(ossTemplate.fileLink(newFileObject.getBucketName(),newFileObject.getObjectName())); newFileObjects.add(newFileObject); }); newFileObjects.stream().forEach(fileObject -> { fileObjectMapper.insert(fileObject); }); return R.data(do2Vos(newFileObjects)); } /** * 修改文件的信息(不会改objectName和filePath) * * @param fileDTOs 文件的信息 * @return 修改后的信息 */ @Override @Transactional public R> updateFileInfos(Collection fileDTOs) { VciBaseUtil.alertNotNull(fileDTOs,"要更新的文件的信息"); if(fileDTOs.stream().anyMatch(s->!StringUtils.hasLength(s.getId()))){ throw new VciBaseException("要更新的文件信息的主键不能为空"); } Map fileObjectMap = Optional.ofNullable(listByIds(fileDTOs.stream().map(FileObjectDTO::getId).collect(Collectors.toList()))).orElseGet(ArrayList::new).stream().collect(Collectors.toMap(s -> s.getOid(), t -> t, (o1, o2) -> o1)); BladeUser user = AuthUtil.getUser(); Date now = new Date(); fileDTOs.stream().forEach(fileDTO->{ if(!fileObjectMap.containsKey(fileDTO.getOid())){ throw new VciBaseException("名称为{0}的文件在系统里不存在",new String[]{StringUtils.hasLength(fileDTO.getId())?fileDTO.getId():fileDTO.getName()}); } FileObject fileObject = fileObjectMap.get(fileDTO.getId()); String oldObjectName = fileObject.getObjectName(); String oldBucketName = fileObject.getBucketName(); String filePath = fileObject.getFileLink(); BeanUtil.convert(fileDTO,fileObject); fileObject.setObjectName(oldObjectName); fileObject.setBucketName(oldBucketName); fileObject.setFileLink(filePath); fileObject.setLastModifier(user.getAccount()); fileObject.setLastModifyTime(now); fileObject.setTs(now); }); fileObjectMap.values().stream().forEach(fileObject -> { fileObjectMapper.updateById(fileObject); }); return R.data(do2Vos(fileObjectMap.values())); } /** * 文件列表查询 * * @param fileQuery 文件查询 * @return 文件的显示信息 */ @Override public IPage listFiles(FileQuery fileQuery, Query query){ IPage pages = page(Condition.getPage(query), Condition.getQueryWrapper(fileQuery)); return pages.convert((fileObject -> do2Vo(fileObject))); } /** * 使用文件的主键批量获取对象 * * @param oids 文件的主键集合 * @return 文件的显示信息 */ @Override public List listFilesByOids(Collection oids) { return do2Vos(listByIds(oids)); } /** * 文件生效 * * @param oids 主键集合 * @return 执行结果 */ @Override @Transactional public R releasedFiles(Collection oids) { Collection fileObjects = listByIds(oids); if(!CollectionUtils.isEmpty(fileObjects)){ fileObjects.stream().forEach(obj->{ obj.setLcStatus(FileLife.FILE_RELEASE.getValue()); fileObjectMapper.updateById(obj); }); } return R.success("生效成功"); } /** * 批量指定文件业务数据后生效 * * @param releaseDTOS 生效的相关信息 * @return 执行结果 */ @Override @Transactional public R releasedFilesForOwn(Collection releaseDTOS) { VciBaseUtil.alertNotNull(releaseDTOS,"发布的信息"); List fileObjectList = new ArrayList<>(); List oids = new ArrayList<>(); releaseDTOS.stream().filter(s->!CollectionUtils.isEmpty(s.getOids())).forEach(dtos->{ oids.addAll(dtos.getOids()); }); if(!CollectionUtils.isEmpty(oids)){ fileObjectList.addAll(listByIds(oids)); } releaseDTOS.stream().forEach(dto->{ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(FileObject.class); if(StringUtils.hasLength(dto.getOwnBtmName())) { queryWrapper.in(FileObject::getOwnBtmName, VciBaseUtil.str2List(dto.getOwnBtmName())); } if(StringUtils.hasLength(dto.getOwnbizOid())) { queryWrapper.in(FileObject::getOwnbizOid, VciBaseUtil.str2List(dto.getOwnbizOid())); } fileObjectList.addAll(fileObjectMapper.selectList(queryWrapper)); }); if(!CollectionUtils.isEmpty(fileObjectList)){ fileObjectList.stream().forEach(objs->{ objs.setLcStatus(FileLife.FILE_RELEASE.getValue()); fileObjectMapper.updateById(objs); }); } return R.success("生效成功"); } /** * 在上传文件后,保存文件的信息 * * @param fileObjectDTO 文件信息传输对象 * @return 文件信息显示对象 */ @Override @Transactional public R saveFileAfterUpload(FileObjectDTO fileObjectDTO) { if(fileObjectDTO.getEffect() !=null && fileObjectDTO.getEffect() && !StringUtils.hasLength(fileObjectDTO.getLcStatus())){ fileObjectDTO.setLcStatus(FileLife.FILE_RELEASE.getValue()); } return R.data(addOnlyInfo(fileObjectDTO)); } /** * 获取对象 * * @param oid 主键 * @return 文件的显示对象 */ @Override public FileObjectVO get(String oid) { return do2Vo(getById(oid)); } /** * 仅添加信息 * @param fileObjectDTO 文件的数据传输对象 * @return 显示对象 */ public FileObjectVO addOnlyInfo(FileObjectDTO fileObjectDTO){ //存储业务数据与文件信息 FileObject fileObject = new FileObject(); BeanUtil.convert(fileObjectDTO, fileObject); if (!StringUtils.hasLength(fileObject.getOid())) { fileObject.setOid(VciBaseUtil.getPk()); } if (fileObject.getSecretGrade() == null || fileObject.getSecretGrade() == 0) { // VciBaseUtil.getCurrentUserSecret()还需要处理,在进入服务的时候,根据当前用户的信息获取用户的对象 fileObject.setSecretGrade( VciBaseUtil.getCurrentUserSecret()); } fileObjectMapper.insert(fileObject); if(log.isDebugEnabled()){ log.debug("新增文件",fileObject.toString()); } return do2Vo(fileObject); } }