package com.gkhy.exam.system.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.gkhy.exam.common.config.FilePathConfig; import com.gkhy.exam.common.config.MinioConfig; import com.gkhy.exam.common.domain.entity.SysUser; import com.gkhy.exam.common.enums.ResourceTypeEnum; import com.gkhy.exam.common.enums.UserTypeEnum; import com.gkhy.exam.common.excel.StudentExcelData; import com.gkhy.exam.common.excel.StudentExcelDataListener; import com.gkhy.exam.common.exception.ApiException; import com.gkhy.exam.common.utils.*; import com.gkhy.exam.system.domain.ExResource; import com.gkhy.exam.system.domain.ExStudent; import com.gkhy.exam.system.domain.SysCompany; import com.gkhy.exam.system.domain.vo.UploadObjectVO; import com.gkhy.exam.system.mapper.ExResourceMapper; import com.gkhy.exam.system.service.ExStudentService; import com.gkhy.exam.system.service.SysCommonService; import com.gkhy.exam.system.service.SysCompanyService; import com.gkhy.exam.system.service.SysUserService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import sun.misc.BASE64Decoder; import javax.annotation.Resource; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CountDownLatch; @Service @Slf4j public class SysCommonServiceImpl implements SysCommonService { @Autowired private MinioConfig minioConfig; @Value("${image.upload_image}") private String uploadPath; @Autowired private M3u8Utils m3u8Utils; @Autowired private MinioUtils minioUtils; @Autowired private ExResourceMapper resourceMapper; @Resource private FilePathConfig filePathConfig; @Autowired private SysCompanyService companyService; @Autowired private ExStudentService studentService; @Autowired private SysUserService userService; // 使用HashSet存储允许的文件格式,提高查找效率 private final HashSet FORMATSET = new HashSet<>(Arrays.asList(".mp4",".doc", ".docx", ".ppt", ".pptx", ".pdf")); @Resource(name = "threadPoolTaskExecutor") private ThreadPoolTaskExecutor poolTaskExecutor; @Override public UploadObjectVO uploadFile(MultipartFile file) { if(file==null){ throw new ApiException("上传文件不能为空"); } UploadObjectVO uploadObjectVO=doUpload(file); return uploadObjectVO; } @Override public boolean removeFile(String path) { String systemDir=System.getProperty("user.dir"); String filePath=systemDir+File.separator+path; File dirFile=new File(filePath); if(!dirFile.exists()){ throw new ApiException("文件不存在"); } if(!dirFile.isFile()){ throw new ApiException("非文件,不能删除"); } dirFile.delete(); return true; } @Override public String uploadVideo2M3u8(MultipartFile file) throws Exception { String path=m3u8Utils.mediaFileToM3u8(file); String name= upload2M3u8(path,null); if(StringUtils.isBlank(name)){ throw new ApiException("文件上传失败"); } return name; } public String localVideo2M3u8(String path,String mainName) throws Exception { String paths=m3u8Utils.localFileToM3u8(path,mainName); return upload2M3u8(paths,mainName); } /** * 上传转码后得视频至OSS或minIOn * @param path * @return 路径 * @throws Exception */ public synchronized String upload2M3u8(String path,String filePrex) throws Exception { //存储转码后文件 String realPath = path.substring(0, path.lastIndexOf("/")); log.info("视频解析后的 realPath {}", realPath); String name = path.substring(path.lastIndexOf("/") + 1); log.info("解析后视频 name {}", name); File allFile = new File(realPath); File[] files = allFile.listFiles(); if (null == files || files.length == 0) { return null; } List errorFile = new ArrayList<>(); long start = System.currentTimeMillis(); //替换m3u8文件中的路径 // FileUtil.replaceTextContent(path, name.substring(0, name.lastIndexOf(".")), // aliOssProperties.getMyHostUrl() + filePath.getProxy() + patch + // name.substring(0, name.lastIndexOf("."))); //开始上传 CountDownLatch countDownLatch = new CountDownLatch(files.length); Arrays.stream(files).forEach(li -> poolTaskExecutor.execute(() -> { try (FileInputStream fileInputStream = new FileInputStream(li)) { String objectKey=li.getName(); if(StringUtils.isNotBlank(filePrex)){ objectKey=filePrex+"/"+li.getName(); } minioUtils.fileUploader(objectKey, fileInputStream); log.info("文件:{} 正在上传", li.getName()); } catch (Exception e) { errorFile.add(li); log.error("文件:{}上传失败",li.getName()); e.printStackTrace(); } finally { countDownLatch.countDown(); } })); countDownLatch.await(); long end = System.currentTimeMillis(); log.info("解析文件上传成功,共计:{} 个文件,失败:{},共耗时: {}ms", files.length, errorFile.size(), end - start); //异步移除所有文件 poolTaskExecutor.execute(() -> { FileUtil.del(realPath); log.warn("临时目录 {}已删除", realPath); }); if (CollectionUtils.isEmpty(errorFile)) { return StringUtils.isNotBlank(filePrex)? filePrex+"/"+name:name; } return ""; } @Override public boolean uploadSlice( String fileMd5, String chunkName, MultipartFile file) { String systemDir=System.getProperty("user.dir"); String filePath=systemDir+filePathConfig.getTempPath()+"chunk/"+fileMd5; File dirFile=new File(filePath); if(!dirFile.exists()){ dirFile.mkdirs(); } filePath=filePath+File.separator+chunkName; try { file.transferTo(new File(filePath)); } catch (FileNotFoundException e) { throw new ApiException("找不到文件"); } catch (IOException e) { throw new ApiException("发生错误,请联系管理员"); } return false; } @Override public UploadObjectVO uploadMerge(String fileMd5, String fileName) throws Exception { String subfix=fileName.substring(fileName.lastIndexOf("."),fileName.length()); checkFileFormat(subfix); UploadObjectVO uploadObjectVO=mergeFile(fileMd5,fileName); String localPath=uploadObjectVO.getPath(); String minioPath=""; if(".mp4".equalsIgnoreCase(subfix)){ String m3u8Path=m3u8Utils.localFileToM3u8(localPath, fileMd5); //计算mp4时长 uploadObjectVO.setResourceLength(VideoUtils.getMp4Duration(m3u8Path)); uploadObjectVO.setResourceType(ResourceTypeEnum.VIDEO.getCode()); minioPath= upload2M3u8(m3u8Path,fileMd5); }else{ if(".mp3".equalsIgnoreCase(subfix)){ uploadObjectVO.setResourceType(ResourceTypeEnum.AUDIO.getCode()); //计算mp3时长 uploadObjectVO.setResourceLength(VideoUtils.getMp3Duration(localPath)); }else{ uploadObjectVO.setResourceType(ResourceTypeEnum.DOC.getCode()); //计算文件页数 uploadObjectVO.setDocPage(DocUtils.getDocPageCount(fileName,Files.newInputStream(Paths.get(localPath)))); } minioPath=minioUtils.fileUploader(uploadObjectVO.getFilename(), Files.newInputStream(Paths.get(localPath))); } if(StringUtils.isBlank(minioPath)){ throw new ApiException("上传文件失败"); } //移除文件 poolTaskExecutor.execute(() -> { FileUtil.del(new File(localPath).getParent()); log.warn("临时目录 {}已删除", new File(localPath).getParent()); }); uploadObjectVO.setPath(minioPath); uploadObjectVO.setUrl(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/"+minioPath); return uploadObjectVO; } private void checkFileFormat(String subfix){ if(!FORMATSET.contains(subfix)){ throw new ApiException(String.format("文件格式<%s>暂时不支持",subfix)); } } @Override public void removeMinioFile(Long resourceId,String path) { String md5=""; if(path.indexOf("/")!=-1){ md5=path.substring(0,path.indexOf("/")); }else{ md5=path.substring(0,path.lastIndexOf(".")); } LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(). eq(true, ExResource::getMd5, md5); if(resourceId!=null){ lambdaQueryWrapper.ne(ExResource::getId,resourceId); } lambdaQueryWrapper.last(" limit 1"); ExResource resource=resourceMapper.selectOne(lambdaQueryWrapper); if(resource==null){//不存在共用md5文件 //删除minio文件 if(path.endsWith(".m3u8")){ minioUtils.removeFolder(md5); }else{ minioUtils.removeFile(path); } } } @Override @Transactional(rollbackFor = RuntimeException.class) public void importStudent() { String path="/home/java/multi_system/back/安全教育学员模板.xlsx"; // String path="F:/kzy/乱七八糟/安全教育学员模板.xlsx"; List studentExcelDataList=EasyExcel.read(path, StudentExcelData.class,new StudentExcelDataListener()).sheet().doReadSync(); List students=new ArrayList<>(); List errorStudents=new ArrayList<>(); SysCompany company=companyService.getOne(Wrappers.lambdaQuery().eq(SysCompany::getName,studentExcelDataList.get(0).getCompanyName()) .eq(SysCompany::getDelFlag,0)); if(company==null){ throw new ApiException("公司不存在"); } for(StudentExcelData studentExcelData:studentExcelDataList){ String errorMessage=validateData(studentExcelData); if(StringUtils.isNotBlank(errorMessage)){ studentExcelData.setRemark(errorMessage); errorStudents.add(studentExcelData); continue; } ExStudent dbStudent=studentService.getOne(Wrappers.lambdaQuery().eq(ExStudent::getPhone,studentExcelData.getPhone())); if(dbStudent!=null){ if(!dbStudent.getName().equals(studentExcelData.getName())){ studentExcelData.setRemark("序号"+studentExcelData.getIndex()+"学员用户已存在"); errorStudents.add(studentExcelData); } continue; } ExStudent student=new ExStudent(); BeanUtils.copyProperties(studentExcelData,student,new String[]{"sex"}); if("男".equals(studentExcelData.getSex())){ student.setSex(0); }else{ student.setSex(1); } String departName=studentExcelData.getDeptName(); List users=userService.list(Wrappers.lambdaQuery().eq(SysUser::getName,departName).eq(SysUser::getUserType, UserTypeEnum.DEPART_USER.getCode()).eq(SysUser::getDelFlag,0).last(" limit 1")); if(users.isEmpty()){ studentExcelData.setRemark("序号"+studentExcelData.getIndex()+"未找到相应的部门账号"); errorStudents.add(studentExcelData); continue; } student.setPassword(SecurityUtils.encryptPassword("123456@a")); student.setCompanyId(company.getId()); student.setCreateId(users.get(0).getId()); students.add(student); } if(!errorStudents.isEmpty()){ EasyExcel.write("error.xlsx").head(StudentExcelData.class) .sheet("异常用户列表") .doWrite(errorStudents); } studentService.saveBatch(students); } public String validateData(StudentExcelData studentExcelData){ if(StringUtils.isBlank(studentExcelData.getName())){ return "序号"+studentExcelData.getIndex()+"姓名为空"; } if(StringUtils.isBlank(studentExcelData.getSex())){ return "序号"+studentExcelData.getIndex()+"性别为空"; } if(StringUtils.isBlank(studentExcelData.getIdNo())||studentExcelData.getIdNo().length()!=18){ return "序号"+studentExcelData.getIndex()+"身份证为空或者长度不正确"; } if(StringUtils.isBlank(studentExcelData.getPhone())||studentExcelData.getPhone().length()!=11){ return "序号"+studentExcelData.getIndex()+"手机号为空或者长度不正确"; } if(StringUtils.isBlank(studentExcelData.getDeptName())){ return "序号"+studentExcelData.getIndex()+"部门为空"; } return ""; } public UploadObjectVO mergeFile(String fileMd5,String fileName){ String subfix = fileName.substring(fileName.lastIndexOf(".")); String systemDir=System.getProperty("user.dir"); String filePath=systemDir+filePathConfig.getTempPath()+"chunk/"+fileMd5; File parentFileDir=new File(filePath); if(!parentFileDir.exists()||!parentFileDir.isDirectory()){ throw new ApiException("切片文件合并失败,文件不存在"); } String destPath=systemDir+filePathConfig.getTempPath()+fileMd5; File destTempFile=new File(destPath,fileMd5+subfix); if(!destTempFile.exists()){ destTempFile.getParentFile().mkdirs(); } try{ //destTempFile.createNewFile(); for(int i=0;i