SlideServiceImpl.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package nju.seec.helper.service.impl;
  2. import com.google.common.collect.ImmutableSet;
  3. import lombok.AllArgsConstructor;
  4. import lombok.SneakyThrows;
  5. import nju.seec.helper.dao.ChooseDAO;
  6. import nju.seec.helper.dao.CourseDAO;
  7. import nju.seec.helper.dao.SlideDAO;
  8. import nju.seec.helper.dto.LoginUser;
  9. import nju.seec.helper.dto.SlideDTO;
  10. import nju.seec.helper.dto.SlideFileDTO;
  11. import nju.seec.helper.dto.SlideStateDTO;
  12. import nju.seec.helper.entity.Course;
  13. import nju.seec.helper.entity.Slide;
  14. import nju.seec.helper.service.MessageService;
  15. import nju.seec.helper.service.QuizService;
  16. import nju.seec.helper.service.SlideService;
  17. import nju.seec.helper.service.util.AuthUtils;
  18. import nju.seec.helper.util.RedisCacheUtils;
  19. import nju.seec.helper.util.Consts;
  20. import nju.seec.helper.util.FileUtils;
  21. import nju.seec.helper.util.enums.ExceptionType;
  22. import nju.seec.helper.util.enums.MessageType;
  23. import nju.seec.helper.util.enums.SlideState;
  24. import nju.seec.helper.util.exception.HelperException;
  25. import nju.seec.helper.vo.SlideVO;
  26. import org.apache.pdfbox.pdmodel.PDDocument;
  27. import org.springframework.beans.factory.annotation.Autowired;
  28. import org.springframework.data.domain.Page;
  29. import org.springframework.data.domain.Pageable;
  30. import org.springframework.stereotype.Service;
  31. import org.springframework.transaction.annotation.Transactional;
  32. import org.springframework.web.multipart.MultipartFile;
  33. import java.io.File;
  34. import java.util.Objects;
  35. import java.util.Set;
  36. import java.util.concurrent.TimeUnit;
  37. /**
  38. * @author cst
  39. */
  40. @Service
  41. public class SlideServiceImpl implements SlideService {
  42. private static final String SLIDE_STORE_DIR = "slide";
  43. private static final long SLIDE_URL_LIVING_SECONDS = 20 * 60;
  44. private final CourseDAO courseDAO;
  45. private final SlideDAO slideDAO;
  46. private final ChooseDAO chooseDAO;
  47. private final QuizService quizService;
  48. private final FileUtils fileUtils;
  49. private final RedisCacheUtils cacheUtils;
  50. private final MessageService messageService;
  51. @Autowired
  52. public SlideServiceImpl(CourseDAO courseDAO, SlideDAO slideDAO, ChooseDAO chooseDAO, FileUtils fileUtils, RedisCacheUtils cacheUtils, MessageService messageService, QuizService quizService) {
  53. this.courseDAO = courseDAO;
  54. this.slideDAO = slideDAO;
  55. this.chooseDAO = chooseDAO;
  56. this.fileUtils = fileUtils;
  57. this.cacheUtils = cacheUtils;
  58. this.messageService = messageService;
  59. this.quizService = quizService;
  60. }
  61. @Transactional(rollbackFor = Exception.class)
  62. @SneakyThrows
  63. @Override
  64. public SlideVO createSlide(LoginUser teacher, SlideDTO slideDTO) {
  65. Course course = courseDAO.findCourseById(slideDTO.getCourseId());
  66. AuthUtils.checkDataAuth(teacher.getId(), course.getTeacherId(), "您无权创建该课程的课件");
  67. checkSameName(0L, slideDTO.getCourseId(), slideDTO.getName());
  68. Slide slide = new Slide()
  69. .setCourseId(slideDTO.getCourseId())
  70. .setCourseName(course.getName())
  71. .setTeacherId(teacher.getId())
  72. .setTeacherName(teacher.getName())
  73. .setName(slideDTO.getName())
  74. .setState(slideDTO.getState());
  75. MultipartFile file = slideDTO.getFile();
  76. FileInfo fileInfo = getFileInfo(file);
  77. slide.setPages(fileInfo.pages);
  78. slide.setObjectName("temp");
  79. slide = slideDAO.save(slide);
  80. String objectName = getObjectName(slide.getId(), slide.getName(), fileInfo.suffix);
  81. slide.setObjectName(objectName);
  82. slide = slideDAO.save(slide);
  83. fileUtils.upload(objectName, file.getInputStream());
  84. return new SlideVO(slide);
  85. }
  86. @Transactional(rollbackFor = Exception.class)
  87. @Override
  88. public SlideVO modifySlide(LoginUser user, SlideDTO slideDTO) {
  89. Slide slide = slideDAO.findSlideById(slideDTO.getId());
  90. AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该课件");
  91. checkSameName(slide.getId(), slide.getCourseId(), slideDTO.getName());
  92. slide.setName(slideDTO.getName());
  93. String oldObjectName = slide.getObjectName();
  94. String newObjectName = getObjectName(slide.getId(), slide.getName(), oldObjectName.substring(oldObjectName.lastIndexOf('.') + 1));
  95. slide.setObjectName(newObjectName);
  96. slide = slideDAO.save(slide);
  97. if (!oldObjectName.equals(newObjectName)) {
  98. fileUtils.copy(oldObjectName, newObjectName);
  99. fileUtils.delete(oldObjectName);
  100. }
  101. return new SlideVO(slide);
  102. }
  103. @Transactional(rollbackFor = Exception.class)
  104. @Override
  105. public void modifySlideState(LoginUser user, SlideStateDTO slideStateDTO) {
  106. Slide slide = slideDAO.findSlideById(slideStateDTO.getId());
  107. AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该课件状态");
  108. slide.setState(slideStateDTO.getState());
  109. slide = slideDAO.save(slide);
  110. // 修改Slide状态同时推进quiz状态
  111. quizService.modifySlideState(slide);
  112. // 增加通知
  113. if (slide.getState() == SlideState.IN_CLASS) {
  114. Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourseId());
  115. messageService.createMessage(studentIds, MessageType.SLIDE_IN_CLASS, String.format("课程 [%s] 的课件 [%s] 已开课", slide.getCourseName(), slide.getName()), slide.getId());
  116. }
  117. }
  118. @Transactional(rollbackFor = Exception.class)
  119. @SneakyThrows
  120. @Override
  121. public void modifySlideFile(LoginUser user, SlideFileDTO slideFileDTO) {
  122. Slide slide = slideDAO.findSlideById(slideFileDTO.getId());
  123. AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该课件文件");
  124. MultipartFile file = slideFileDTO.getFile();
  125. FileInfo fileInfo = getFileInfo(file);
  126. slide.setPages(fileInfo.pages);
  127. String oldObjectName = slide.getObjectName();
  128. String newObjectName = getObjectName(slide.getId(), slide.getName(), fileInfo.suffix);
  129. slide.setObjectName(newObjectName);
  130. slideDAO.save(slide);
  131. fileUtils.upload(newObjectName, file.getInputStream());
  132. if (!oldObjectName.equals(newObjectName)) {
  133. fileUtils.delete(oldObjectName);
  134. }
  135. }
  136. @Transactional(rollbackFor = Exception.class)
  137. @Override
  138. public void removeSlide(LoginUser user, Long slideId) {
  139. Slide slide = slideDAO.findSlideById(slideId);
  140. AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权删除该课件");
  141. slide.setDeleteAt(System.currentTimeMillis());
  142. slideDAO.save(slide);
  143. }
  144. @SuppressWarnings("all")
  145. @SneakyThrows
  146. private FileInfo getFileInfo(MultipartFile file) {
  147. switch (Objects.requireNonNull(file.getContentType())) {
  148. case "application/pdf":
  149. // 解析页数
  150. PDDocument doc = PDDocument.load(file.getInputStream());
  151. return FileInfo.of(doc.getNumberOfPages(), "pdf");
  152. default:
  153. throw HelperException.of(ExceptionType.PARAM_ERROR, "不支持的文件类型");
  154. }
  155. }
  156. @AllArgsConstructor(staticName = "of")
  157. private static class FileInfo {
  158. private int pages;
  159. private String suffix;
  160. }
  161. private String getObjectName(Long slideId, String slideName, String suffix) {
  162. return SLIDE_STORE_DIR + File.separator + slideId + File.separator + slideName + "." + suffix;
  163. }
  164. @Transactional(readOnly = true)
  165. @Override
  166. public Page<SlideVO> teacherGetSlides(LoginUser teacher, String key, Pageable pageable) {
  167. return slideDAO.findByTeacherIdAndNameContains(teacher.getId(), key, pageable).map(SlideVO::new);
  168. }
  169. @Transactional(readOnly = true)
  170. @Override
  171. public Page<SlideVO> teacherGetSlidesByCourse(LoginUser teacher, Long courseId, String key, Pageable pageable) {
  172. return slideDAO.findByCourseIdAndTeacherIdAndNameContains(courseId, teacher.getId(), key, pageable).map(SlideVO::new);
  173. }
  174. @Transactional(readOnly = true)
  175. @Override
  176. public Page<SlideVO> studentGetSlidesByCourse(LoginUser student, Long courseId, String key, Pageable pageable) {
  177. if (!chooseDAO.existsByStudentIdAndCourseId(student.getId(), courseId)) {
  178. throw HelperException.of(ExceptionType.FORBIDDEN, "你无权查看该课程下的课件");
  179. }
  180. return slideDAO.findByCourseIdAndStateNotInAndNameContains(courseId, ImmutableSet.of(SlideState.DRAFT), key, pageable).map(SlideVO::new);
  181. }
  182. @Transactional(readOnly = true)
  183. @Override
  184. public String getSlideUrl(LoginUser user, Long slideId) {
  185. Slide slide = slideDAO.findSlideById(slideId);
  186. checkDataAccessAuth(user, slide);
  187. String url = cacheUtils.get(Consts.SLIDE_URL_CACHE_NAME, slide.getObjectName());
  188. if (url != null) {
  189. return url;
  190. }
  191. url = fileUtils.getUrl(slide.getObjectName(), SLIDE_URL_LIVING_SECONDS);
  192. cacheUtils.set(Consts.SLIDE_URL_CACHE_NAME, slide.getObjectName(), url, SLIDE_URL_LIVING_SECONDS, TimeUnit.SECONDS);
  193. return url;
  194. }
  195. @Transactional(readOnly = true)
  196. @Override
  197. public SlideVO getOneSlide(LoginUser user, Long slideId) {
  198. Slide slide = slideDAO.findSlideById(slideId);
  199. checkDataAccessAuth(user, slide);
  200. return new SlideVO(slide);
  201. }
  202. /**
  203. * 检查同名课件
  204. */
  205. private void checkSameName(Long slideId, Long courseId, String name) {
  206. if (slideDAO.existsByCourseIdAndName(slideId, courseId, name)) {
  207. throw HelperException.of(ExceptionType.CONFLICT, "该课件名已使用");
  208. }
  209. }
  210. private void checkDataAccessAuth(LoginUser user, Slide slide) {
  211. HelperException forbiddenEx = HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课件");
  212. switch (user.getType()) {
  213. case TEACHER:
  214. if (!slide.getTeacherId().equals(user.getId())) {
  215. throw forbiddenEx;
  216. }
  217. break;
  218. case STUDENT:
  219. if (!chooseDAO.existsByStudentIdAndCourseId(user.getId(), slide.getCourseId())
  220. || slide.getState() == SlideState.DRAFT) {
  221. throw forbiddenEx;
  222. }
  223. break;
  224. default:
  225. throw forbiddenEx;
  226. }
  227. }
  228. }