package nju.seec.helper.service.impl; import com.google.common.collect.ImmutableSet; 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.SlideDTO; import nju.seec.helper.entity.Course; import nju.seec.helper.entity.Page; import nju.seec.helper.entity.Slide; 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.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.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * @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()); if (!course.getTeacherId().equals(teacher.getId())) { throw HelperException.of(HelperException.ExceptionType.FORBIDDEN, "您无权创建该课程的幻灯片"); } Slide slide = new Slide(); slide.setCourseId(slideDTO.getCourseId()) .setTeacherId(teacher.getId()) .setName(slideDTO.getName()) .setState(SlideState.DRAFT); // 解析文件页数 MultipartFile file = slideDTO.getFile(); int pageNum; switch (Objects.requireNonNull(file.getContentType())) { // pdf case "application/pdf": PDDocument doc = PDDocument.load(file.getInputStream()); pageNum = doc.getNumberOfPages(); break; default: throw HelperException.of(HelperException.ExceptionType.FORBIDDEN, "不支持的文件类型"); } slide.setPages(IntStream.rangeClosed(1, pageNum) .mapToObj(number -> { Page page = new Page(); page.setTeacherId(teacher.getId()); page.setNumber(number); return page; }) .collect(Collectors.toList()) ); // 上传文件 String objectName = fileUtils.upload(LocalDateTime.now().toString() + "-" + file.getOriginalFilename(), file.getInputStream()); slide.setObjectName(objectName); return new SlideVO(slideDAO.save(slide)); } @Transactional(readOnly = true) @Override public List teacherGetSlides(LoginUser teacher, String key, Pageable pageable) { return slideDAO .findByTeacherIdAndNameContains(teacher.getId(), key, pageable) .stream() .map(SlideVO::new) .collect(Collectors.toList()); } @Transactional(readOnly = true) @Override public List studentGetSlides(LoginUser student, Integer courseId, String key, Pageable pageable) { if (!chooseDAO.existsByStudentIdAndCourseId(student.getId(), courseId)) { throw HelperException.of(HelperException.ExceptionType.FORBIDDEN, "你无权查看该课程下的幻灯片"); } return slideDAO .findByCourseIdAndStateNotInAndNameContains(courseId, ImmutableSet.of(SlideState.DRAFT), key, pageable) .stream() .map(SlideVO::new) .collect(Collectors.toList()); } @Transactional(readOnly = true) @Override public String getSlideUrl(LoginUser user, Integer slideId) { checkDataAccessAuth(user, slideId); Slide slide = slideDAO.findSlideById(slideId); 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; } private void checkDataAccessAuth(LoginUser user, Integer slideId) { HelperException forbiddenEx = HelperException.of(HelperException.ExceptionType.FORBIDDEN, "您无权访问该幻灯片"); Slide slide = slideDAO.findSlideById(slideId); switch (user.getType()) { case TEACHER: if (!slide.getTeacherId().equals(user.getId())) { throw forbiddenEx; } break; case STUDENT: if (!chooseDAO.existsByStudentIdAndCourseId(user.getId(), slide.getCourseId())) { throw forbiddenEx; } break; default: throw forbiddenEx; } } }