From 8458e64aab474c0fc2f49ae4ff22fb11ce5cf6e2 Mon Sep 17 00:00:00 2001 From: “djh” <“3298565835@qq.com”> Date: 星期一, 11 十一月 2024 16:55:28 +0800 Subject: [PATCH] 批次新增学员查询条件,新增题目导入接口 --- exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 258 insertions(+), 33 deletions(-) diff --git a/exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java b/exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java index 0ccb5d1..9d8f52d 100644 --- a/exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java +++ b/exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java @@ -1,25 +1,44 @@ 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.M3u8Utils; -import com.gkhy.exam.common.utils.MinioUtils; +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 javax.servlet.http.HttpServletRequest; import java.io.*; -import java.time.LocalDateTime; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -27,19 +46,32 @@ @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 filePath; + private FilePathConfig filePathConfig; + @Autowired + private SysCompanyService companyService; + @Autowired + private ExStudentService studentService; + @Autowired + private SysUserService userService; + + // 使用HashSet存储允许的文件格式,提高查找效率 + private final HashSet<String> FORMATSET = new HashSet<>(Arrays.asList(".mp4",".doc", ".docx", ".ppt", ".pptx", ".pdf")); + @Resource(name = "threadPoolTaskExecutor") private ThreadPoolTaskExecutor poolTaskExecutor; - - String projectUrl = System.getProperty("user.dir").replaceAll("\\\\", "/"); @Override public UploadObjectVO uploadFile(MultipartFile file) { @@ -68,7 +100,16 @@ @Override public String uploadVideo2M3u8(MultipartFile file) throws Exception { String path=m3u8Utils.mediaFileToM3u8(file); - return upload2M3u8(path); + 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); } @@ -78,7 +119,7 @@ * @return 路径 * @throws Exception */ - public String upload2M3u8(String path) throws Exception { + public synchronized String upload2M3u8(String path,String filePrex) throws Exception { //存储转码后文件 String realPath = path.substring(0, path.lastIndexOf("/")); log.info("视频解析后的 realPath {}", realPath); @@ -89,11 +130,10 @@ if (null == files || files.length == 0) { return null; } - String patch = DateUtil.format(LocalDateTime.now(), "yyyy/MM/") + name.substring(0, name.lastIndexOf(".")) + "/"; List<File> errorFile = new ArrayList<>(); - long start = System.currentTimeMillis(); -// //替换m3u8文件中的路径 + //替换m3u8文件中的路径 + // FileUtil.replaceTextContent(path, name.substring(0, name.lastIndexOf(".")), // aliOssProperties.getMyHostUrl() + filePath.getProxy() + patch + // name.substring(0, name.lastIndexOf("."))); @@ -101,11 +141,15 @@ CountDownLatch countDownLatch = new CountDownLatch(files.length); Arrays.stream(files).forEach(li -> poolTaskExecutor.execute(() -> { try (FileInputStream fileInputStream = new FileInputStream(li)) { - minioUtils.fileUploader(patch + li.getName(), fileInputStream); - + 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(); @@ -114,42 +158,223 @@ countDownLatch.await(); long end = System.currentTimeMillis(); log.info("解析文件上传成功,共计:{} 个文件,失败:{},共耗时: {}ms", files.length, errorFile.size(), end - start); - // try { - // minioComponent.mkBucket("m3u8"); - // } catch (Exception e) { - // log.error("创建Bucket失败!"); - // } - //异步移除所有文件 poolTaskExecutor.execute(() -> { - deleteFile(projectUrl+filePath.getTempPath()); + FileUtil.del(realPath); + log.warn("临时目录 {}已删除", realPath); }); if (CollectionUtils.isEmpty(errorFile)) { - return filePath.getProxy() + patch + name; + return StringUtils.isNotBlank(filePrex)? filePrex+"/"+name:name; } return ""; } - public static void deleteFile(String path){ - File dest = new File(path); - if (dest.isFile() && dest.exists()) { - dest.delete(); + + @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 boolean uploadSlice(HttpServletRequest request, String guid, Integer chunk, MultipartFile file) { - return false; + 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<ExResource> lambdaQueryWrapper = Wrappers.<ExResource>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 - public String uploadVideoMerge(String guid, String fileName) { - return null; + @Transactional(rollbackFor = RuntimeException.class) + public void importStudent() { + String path="/home/java/train_exam/back/安全教育学员模板.xlsx"; + // String path="F:/kzy/乱七八糟/安全教育学员模板.xlsx"; + List<StudentExcelData> studentExcelDataList=EasyExcel.read(path, StudentExcelData.class,new StudentExcelDataListener()).sheet().doReadSync(); + List<ExStudent> students=new ArrayList<>(); + List<StudentExcelData> errorStudents=new ArrayList<>(); + SysCompany company=companyService.getOne(Wrappers.<SysCompany>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.<ExStudent>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<SysUser> users=userService.list(Wrappers.<SysUser>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); } - @Override - public String uploadMerge(String guid, String fileName) { - return null; + + + + + 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<Objects.requireNonNull(parentFileDir.listFiles()).length;i++){ + File partFile=new File(parentFileDir,fileMd5+"-"+i); + FileOutputStream destTempfos = new FileOutputStream(destTempFile, true); + //遍历"所有分片文件"到"最终文件"中 + FileUtils.copyFile(partFile, destTempfos); + destTempfos.close(); + } + FileUtil.del(parentFileDir); + }catch (Exception e){ + log.error("切片文件合并,失败原因e:{}", e.getMessage()); + throw new ApiException("切片文件合并失败:"+e.getMessage()); + } + UploadObjectVO uploadObjectVO=new UploadObjectVO(); + uploadObjectVO.setSize(destTempFile.length()); + uploadObjectVO.setPath(destTempFile.getPath()); + uploadObjectVO.setOriginName(fileName); + uploadObjectVO.setFilename(fileMd5+subfix); + uploadObjectVO.setMd5(fileMd5); + return uploadObjectVO; } @Override -- Gitblit v1.9.2