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<FileObjectMapper, FileObject> implements IFileService {
|
|
/**
|
* 文件的数据操作类
|
*/
|
@Resource
|
private FileObjectMapper fileObjectMapper;
|
|
/**
|
* 对象的模版
|
*/
|
@Resource
|
private OssTemplate ossTemplate;
|
|
/**
|
* 文件服务的配置
|
*/
|
@Autowired
|
private OssProperties ossProperties;
|
|
/**
|
* 压缩文件的工具类
|
*/
|
@Autowired
|
private ZipUtil zipUtil;
|
|
/***
|
* 文件的规则
|
*/
|
@Autowired
|
private OssRule ossRule;
|
|
|
/**
|
* 下载,注意多个文件的时候,会自动下载为一个压缩包
|
*
|
* @param oids 主键集合
|
* @return 文件的信息
|
*/
|
@Override
|
public FileObjectBO downloadFileByOids(Collection<String> oids) {
|
VciBaseUtil.alertNotNull(oids,"文件的主键集合");
|
if(log.isDebugEnabled()){
|
log.debug("下载文件,",oids.stream().collect(Collectors.joining(",")));
|
}
|
Collection<FileObjectVO> 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<String> oids) {
|
VciBaseUtil.alertNotNull(oids,"文件的主键集合");
|
if(log.isDebugEnabled()){
|
log.debug("删除文件,",oids.stream().collect(Collectors.joining(",")));
|
}
|
Collection<FileObject> fileObjects = listByIds(oids);
|
if(!CollectionUtils.isEmpty(fileObjects)){
|
VciBaseUtil.switchCollectionForOracleIn(fileObjects).parallelStream().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<FileObjectVO> 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,null);
|
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<FileObjectVO> do2Vos(Collection<FileObject> fileObjects){
|
List<FileObjectVO> 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<List<FileObjectVO>> shareFiles(FileShareDTO shareDTO) {
|
VciBaseUtil.alertNotNull(shareDTO,"分享的信息",shareDTO.getTargetBus(),"目标的业务名称",shareDTO.getTargetBusOid(),"目标的业务数据主键");
|
List<FileObject> fileObjectList = new ArrayList<>();
|
if(!CollectionUtils.isEmpty(shareDTO.getOids())){
|
fileObjectList.addAll(listByIds(shareDTO.getOids()));
|
}
|
LambdaQueryWrapper<FileObject> queryWrapper = new LambdaQueryWrapper<FileObject>(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<FileObject> 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<List<FileObjectVO>> updateFileInfos(Collection<FileObjectDTO> fileDTOs) {
|
VciBaseUtil.alertNotNull(fileDTOs,"要更新的文件的信息");
|
if(fileDTOs.stream().anyMatch(s->!StringUtils.hasLength(s.getId()))){
|
throw new VciBaseException("要更新的文件信息的主键不能为空");
|
}
|
Map<String, FileObject> 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<FileObjectVO> listFiles(FileQuery fileQuery, Query query){
|
IPage<FileObject> pages = page(Condition.getPage(query), Condition.getQueryWrapper(fileQuery));
|
return pages.convert((fileObject -> do2Vo(fileObject)));
|
}
|
|
/**
|
* 使用文件的主键批量获取对象
|
*
|
* @param oids 文件的主键集合
|
* @return 文件的显示信息
|
*/
|
@Override
|
public List<FileObjectVO> listFilesByOids(Collection<String> oids) {
|
return do2Vos(listByIds(oids));
|
}
|
|
/**
|
* 文件生效
|
*
|
* @param oids 主键集合
|
* @return 执行结果
|
*/
|
@Override
|
@Transactional
|
public R releasedFiles(Collection<String> oids) {
|
Collection<FileObject> 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<FileReleaseDTO> releaseDTOS) {
|
VciBaseUtil.alertNotNull(releaseDTOS,"发布的信息");
|
List<FileObject> fileObjectList = new ArrayList<>();
|
List<String> 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<FileObject> 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<FileObjectVO> 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);
|
}
|
|
}
|