package nju.seec.helper.service.impl; import com.google.common.collect.ImmutableSet; import lombok.Cleanup; import lombok.SneakyThrows; import nju.seec.helper.dao.ChooseDAO; import nju.seec.helper.dao.CourseDAO; import nju.seec.helper.dao.SlideDAO; import nju.seec.helper.dto.LoginUser; import nju.seec.helper.dto.ModifySlideDTO; import nju.seec.helper.dto.SlideDTO; import nju.seec.helper.dto.SlideStateDTO; import nju.seec.helper.entity.Course; import nju.seec.helper.entity.Slide; import nju.seec.helper.service.AuthUtil; import nju.seec.helper.service.SlideService; import nju.seec.helper.util.CacheUtils; import nju.seec.helper.util.Consts; import nju.seec.helper.util.FileUtils; import nju.seec.helper.util.enums.ExceptionType; import nju.seec.helper.util.enums.SlideState; import nju.seec.helper.util.exception.HelperException; import nju.seec.helper.vo.SlideVO; import org.apache.pdfbox.pdmodel.PDDocument; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.time.LocalDateTime; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * @author cst */ @Service public class SlideServiceImpl implements SlideService { private static final long SLIDE_URL_LIVING_SECONDS = 20 * 60; private final CourseDAO courseDAO; private final SlideDAO slideDAO; private final ChooseDAO chooseDAO; private final FileUtils fileUtils; private final CacheUtils cacheUtils; public SlideServiceImpl(CourseDAO courseDAO, SlideDAO slideDAO, ChooseDAO chooseDAO, FileUtils fileUtils, CacheUtils cacheUtils) { this.courseDAO = courseDAO; this.slideDAO = slideDAO; this.chooseDAO = chooseDAO; this.fileUtils = fileUtils; this.cacheUtils = cacheUtils; } @Transactional(rollbackFor = Exception.class) @SneakyThrows @Override public SlideVO createSlide(LoginUser teacher, SlideDTO slideDTO) { Course course = courseDAO.findCourseById(slideDTO.getCourseId()); AuthUtil.checkDataAuth(teacher.getId(), course.getTeacherId(), "您无权创建该课程的幻灯片"); Slide slide = new Slide() .setCourseId(slideDTO.getCourseId()) .setCourseName(course.getName()) .setTeacherId(teacher.getId()) .setTeacherName(teacher.getName()) .setName(slideDTO.getName()) .setState(slideDTO.getState()); // 解析文件页数 MultipartFile file = slideDTO.getFile(); int pages; switch (Objects.requireNonNull(file.getContentType())) { // pdf case "application/pdf": @Cleanup InputStream fileInputStream = file.getInputStream(); PDDocument doc = PDDocument.load(fileInputStream); pages = doc.getNumberOfPages(); break; default: throw HelperException.of(ExceptionType.FORBIDDEN, "不支持的文件类型"); } slide.setPages(pages); // 上传文件 String objectName = fileUtils.upload(LocalDateTime.now().toString() + "-" + file.getOriginalFilename(), file.getInputStream()); slide.setObjectName(objectName); return new SlideVO(slideDAO.save(slide)); } @Transactional(rollbackFor = Exception.class) @Override public SlideVO modifySlide(LoginUser user, ModifySlideDTO modifySlideDTO) { Slide slide = slideDAO.findSlideById(modifySlideDTO.getId()); AuthUtil.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该幻灯片"); slide.setName(modifySlideDTO.getName()); slide = slideDAO.save(slide); return new SlideVO(slide); } @Transactional(rollbackFor = Exception.class) @Override public void modifySlideState(LoginUser user, SlideStateDTO slideStateDTO) { Slide slide = slideDAO.findSlideById(slideStateDTO.getId()); AuthUtil.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该幻灯片状态"); slide.setState(slideStateDTO.getState()); slideDAO.save(slide); } @Transactional(rollbackFor = Exception.class) @Override public void removeSlide(LoginUser user, Integer slideId) { Slide slide = slideDAO.findSlideById(slideId); AuthUtil.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权删除该幻灯片"); slide.setDeleteAt(System.currentTimeMillis()); slideDAO.save(slide); } @Transactional(readOnly = true) @Override public Page teacherGetSlides(LoginUser teacher, String key, Pageable pageable) { return slideDAO.findByTeacherIdAndNameContains(teacher.getId(), key, pageable).map(SlideVO::new); } @Transactional(readOnly = true) @Override public Page teacherGetSlidesByCourse(LoginUser teacher, Integer courseId, String key, Pageable pageable) { return slideDAO.findByCourseIdAndTeacherIdAndNameContains(courseId, teacher.getId(), key, pageable).map(SlideVO::new); } @Transactional(readOnly = true) @Override public Page studentGetSlidesByCourse(LoginUser student, Integer courseId, String key, Pageable pageable) { if (!chooseDAO.existsByStudentIdAndCourseId(student.getId(), courseId)) { throw HelperException.of(ExceptionType.FORBIDDEN, "你无权查看该课程下的幻灯片"); } return slideDAO.findByCourseIdAndStateNotInAndNameContains(courseId, ImmutableSet.of(SlideState.DRAFT), key, pageable).map(SlideVO::new); } @Transactional(readOnly = true) @Override public String getSlideUrl(LoginUser user, Integer slideId) { Slide slide = slideDAO.findSlideById(slideId); checkDataAccessAuth(user, slide); String url = cacheUtils.get(Consts.SLIDE_URL_CACHE_NAME, slide.getObjectName()); if (url != null) { return url; } url = fileUtils.getUrl(slide.getObjectName(), SLIDE_URL_LIVING_SECONDS); cacheUtils.set(Consts.SLIDE_URL_CACHE_NAME, slide.getObjectName(), url, SLIDE_URL_LIVING_SECONDS, TimeUnit.SECONDS); return url; } @Transactional(readOnly = true) @Override public SlideVO getOneSlide(LoginUser user, Integer slideId) { Slide slide = slideDAO.findSlideById(slideId); checkDataAccessAuth(user, slide); return new SlideVO(slide); } private void checkDataAccessAuth(LoginUser user, Slide slide) { HelperException forbiddenEx = HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该幻灯片"); switch (user.getType()) { case TEACHER: if (!slide.getTeacherId().equals(user.getId())) { throw forbiddenEx; } break; case STUDENT: if (!chooseDAO.existsByStudentIdAndCourseId(user.getId(), slide.getCourseId()) || slide.getState() == SlideState.DRAFT) { throw forbiddenEx; } break; default: throw forbiddenEx; } } }