SlideServiceImpl.java 9.9 KB

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