浏览代码

refactor: 基于逻辑删除的优化重构

ChenSiTong 6 年之前
父节点
当前提交
8bb37affa9
共有 34 个文件被更改,包括 685 次插入556 次删除
  1. 1 1
      src/main/java/nju/seec/helper/config/PageableConfig.java
  2. 2 2
      src/main/java/nju/seec/helper/controller/CommentController.java
  3. 27 0
      src/main/java/nju/seec/helper/dao/CommentDAO.java
  4. 53 4
      src/main/java/nju/seec/helper/dao/CourseDAO.java
  5. 23 0
      src/main/java/nju/seec/helper/dao/NoticeDAO.java
  6. 47 14
      src/main/java/nju/seec/helper/dao/QuizDAO.java
  7. 39 3
      src/main/java/nju/seec/helper/dao/SlideDAO.java
  8. 7 5
      src/main/java/nju/seec/helper/entity/Course.java
  9. 2 1
      src/main/java/nju/seec/helper/entity/Quiz.java
  10. 2 1
      src/main/java/nju/seec/helper/entity/Slide.java
  11. 0 30
      src/main/java/nju/seec/helper/enums/QuizState.java
  12. 1 21
      src/main/java/nju/seec/helper/enums/QuizType.java
  13. 46 0
      src/main/java/nju/seec/helper/service/AuthService.java
  14. 4 2
      src/main/java/nju/seec/helper/service/CommentService.java
  15. 2 1
      src/main/java/nju/seec/helper/service/CourseFileService.java
  16. 2 10
      src/main/java/nju/seec/helper/service/CourseService.java
  17. 2 1
      src/main/java/nju/seec/helper/service/NoticeService.java
  18. 2 79
      src/main/java/nju/seec/helper/service/QuizService.java
  19. 2 1
      src/main/java/nju/seec/helper/service/ReplyService.java
  20. 2 1
      src/main/java/nju/seec/helper/service/SlideService.java
  21. 1 1
      src/main/java/nju/seec/helper/service/impl/AdminServiceImpl.java
  22. 55 37
      src/main/java/nju/seec/helper/service/impl/CommentServiceImpl.java
  23. 43 13
      src/main/java/nju/seec/helper/service/impl/CourseFileServiceImpl.java
  24. 62 77
      src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java
  25. 43 20
      src/main/java/nju/seec/helper/service/impl/NoticeServiceImpl.java
  26. 88 105
      src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java
  27. 1 8
      src/main/java/nju/seec/helper/service/impl/QuizStudentAnswerServiceImpl.java
  28. 33 8
      src/main/java/nju/seec/helper/service/impl/ReplyServiceImpl.java
  29. 76 87
      src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java
  30. 0 2
      src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java
  31. 0 18
      src/main/java/nju/seec/helper/service/util/AuthUtils.java
  32. 13 0
      src/main/java/nju/seec/helper/util/StringUtils.java
  33. 3 0
      src/main/java/nju/seec/helper/vo/CommentVO.java
  34. 1 3
      src/main/java/nju/seec/helper/vo/question/BaseQuestionVO.java

+ 1 - 1
src/main/java/nju/seec/helper/config/PageableConfig.java

@@ -10,7 +10,7 @@ import org.springframework.data.web.config.PageableHandlerMethodArgumentResolver
  * @Author SheentXu
  * @Date 2019/12/24
  * @Version 1.0
- * @Description //TODO
+ * @Description
  **/
 @Configuration
 public class PageableConfig {

+ 2 - 2
src/main/java/nju/seec/helper/controller/CommentController.java

@@ -85,7 +85,7 @@ public class CommentController {
      */
     @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取评论")
     @GetMapping("/{commentId}")
-    public CommentVO getOneComment(@PathVariable Long commentId) {
-        return commentService.getOneComment(commentId);
+    public CommentVO getOneComment(LoginUser user, @PathVariable Long commentId) {
+        return commentService.getOneComment(user, commentId);
     }
 }

+ 27 - 0
src/main/java/nju/seec/helper/dao/CommentDAO.java

@@ -3,10 +3,14 @@ package nju.seec.helper.dao;
 import nju.seec.helper.entity.Comment;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
+
 /**
  * @author cst
  */
@@ -22,4 +26,27 @@ public interface CommentDAO extends JpaRepository<Comment, Long>, JpaSpecificati
         return this.findById(id)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到评论"));
     }
+
+    /**
+     * 根据课件ID、页码、置顶指数统计
+     *
+     * @param slideId
+     * @param pageNumber
+     * @param topNumber
+     * @return
+     */
+    long countBySlideIdAndPageNumberAndTopNumberNot(Long slideId, Integer pageNumber, Integer topNumber);
+
+    List<Comment> findBySlideIdAndPageNumberAndTopNumberGreaterThan(Long slideId, Integer pageNumber, Integer topNumber);
+
+    /**
+     * 根据课件ID、页码、是否展示获取
+     *
+     * @param slideId
+     * @param pageNumber
+     * @param show
+     * @param pageable
+     * @return
+     */
+    Page<Comment> findBySlideIdAndPageNumberAndShow(Long slideId, Integer pageNumber, Boolean show, Pageable pageable);
 }

+ 53 - 4
src/main/java/nju/seec/helper/dao/CourseDAO.java

@@ -1,11 +1,19 @@
 package nju.seec.helper.dao;
 
 import nju.seec.helper.entity.Course;
+import nju.seec.helper.entity.User;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import javax.persistence.criteria.JoinType;
+import java.util.Set;
 
 /**
  * @author cst
@@ -19,17 +27,58 @@ public interface CourseDAO extends JpaRepository<Course, Long>, JpaSpecification
      * @return
      */
     default Course findCourseById(Long id) {
-        return this.findById(id)
-                .filter(course -> course.getDeleteAt() == 0)
-                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到课程"));
+        return this.findById(id).orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到课程"));
     }
 
     /**
-     * 基于课程选课码检索
+     * 课程ID、选课码检查
      *
      * @param courseId
      * @param code
      * @return
      */
     boolean existsByIdAndCode(Long courseId, String code);
+
+    /**
+     * 根据关键字检索
+     *
+     * @param key
+     * @param pageable
+     * @return
+     */
+    default Page<Course> findByKey(String key, Pageable pageable) {
+        String keyPattern = nju.seec.helper.util.StringUtils.keyPattern(key);
+
+        return StringUtils.hasText(key) ?
+                this.findAll((Specification<Course>) (root, query, cb) -> cb.or(cb.like(root.get("name"), keyPattern), cb.like(root.join("teacher", JoinType.LEFT).get("name"), keyPattern)), pageable) :
+                this.findAll(pageable);
+    }
+
+    /**
+     * 根据教师、关键字检索
+     *
+     * @param teacher
+     * @param name
+     * @param pageable
+     * @return
+     */
+    Page<Course> findByTeacherAndNameContains(User teacher, String name, Pageable pageable);
+
+    /**
+     * 根据ID、关键字检索
+     *
+     * @param courseIds
+     * @param key
+     * @param pageable
+     * @return
+     */
+    default Page<Course> findByIdsAndKey(Set<Long> courseIds, String key, Pageable pageable) {
+        final String keyPattern = nju.seec.helper.util.StringUtils.keyPattern(key);
+
+        return this.findAll(
+                (Specification<Course>) (root, query, cb) -> cb.and(
+                        root.get("id").in(courseIds)
+                        , cb.or(cb.like(root.get("name"), keyPattern), cb.like(root.join("teacher", JoinType.LEFT).get("name"), keyPattern))
+                ), pageable);
+    }
 }

+ 23 - 0
src/main/java/nju/seec/helper/dao/NoticeDAO.java

@@ -3,6 +3,10 @@ package nju.seec.helper.dao;
 import nju.seec.helper.entity.Notice;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
+import nju.seec.helper.util.StringUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
@@ -22,4 +26,23 @@ public interface NoticeDAO extends JpaRepository<Notice, Long>, JpaSpecification
         return this.findById(id)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到公告"));
     }
+
+    /**
+     * 感觉课程ID、关键字检索
+     *
+     * @param courseId
+     * @param key
+     * @param pageable
+     * @return
+     */
+    default Page<Notice> findByCourseIdAndKey(Long courseId, String key, Pageable pageable) {
+        String keyPattern = StringUtils.keyPattern(key);
+
+        return this.findAll(
+                (Specification<Notice>) (root, query, cb) -> cb.and(
+                        cb.equal(root.get("courseId"), courseId)
+                        , cb.or(cb.like(root.get("title"), keyPattern), cb.like(root.get("content"), keyPattern))
+                ),
+                pageable);
+    }
 }

+ 47 - 14
src/main/java/nju/seec/helper/dao/QuizDAO.java

@@ -3,12 +3,18 @@ package nju.seec.helper.dao;
 import nju.seec.helper.entity.Quiz;
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.enums.ExceptionType;
+import nju.seec.helper.enums.QuizState;
 import nju.seec.helper.exception.HelperException;
+import nju.seec.helper.util.StringUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
 
 import java.util.List;
+import java.util.Set;
 
 
 /**
@@ -26,27 +32,54 @@ public interface QuizDAO extends JpaRepository<Quiz, Long>, JpaSpecificationExec
      */
     default Quiz findQuizById(Long id) {
         return this.findById(id)
-                .filter(quiz -> quiz.getDeleteAt() == 0L)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到测试"));
     }
 
-//    Page<Quiz> findAllBySlideAndState(Slide slide, QuizState state, Pageable pageable);
-//
-//    Page<Quiz> findAllBySlide(Slide slide, Pageable pageable);
-
     /**
      * 根据课件检索
      *
      * @param slide
      * @return
      */
-    List<Quiz> findAllBySlide(Slide slide);
-
-//    Page<Quiz> findAllByCourseIn(Collection<Course> course, Pageable pageable);
-//
-//    Page<Quiz> findAllByCourseInAndState(Collection<Course> course, QuizState state, Pageable pageable);
-//
-//    Page<Quiz> findByTeacher(User teacher, Pageable pageable);
-//
-//    Page<Quiz> findByTeacherAndState(User teacher, QuizState state, Pageable pageable);
+    List<Quiz> findBySlide(Slide slide);
+
+    /**
+     * 根据课件、测试状态、名称检索
+     *
+     * @param slide
+     * @param quizStates
+     * @param name
+     * @param pageable
+     * @return
+     */
+    Page<Quiz> findBySlideAndStateNotInAndNameContains(Slide slide, Set<QuizState> quizStates, String name, Pageable pageable);
+
+    /**
+     * 根据课程、测试状态、名称检索
+     *
+     * @param courseIds
+     * @param quizState
+     * @param name
+     * @param pageable
+     * @return
+     */
+    default Page<Quiz> findByCourseIdsAndStateAndNameContains(Set<Long> courseIds, QuizState quizState, String name, Pageable pageable) {
+        return this.findAll(
+                (Specification<Quiz>) (root, query, cb) -> cb.and(
+                        root.get("course").get("id").in(courseIds)
+                        , cb.equal(root.get("state"), quizState)
+                        , cb.like(root.get("name"), StringUtils.keyPattern(name))
+                ),
+                pageable);
+    }
+
+    /**
+     * 根据课件、名称检索
+     *
+     * @param slide
+     * @param name
+     * @param pageable
+     * @return
+     */
+    Page<Quiz> findBySlideAndNameContains(Slide slide, String name, Pageable pageable);
 }

+ 39 - 3
src/main/java/nju/seec/helper/dao/SlideDAO.java

@@ -1,17 +1,23 @@
 package nju.seec.helper.dao;
 
+import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Slide;
+import nju.seec.helper.entity.User;
 import nju.seec.helper.enums.ExceptionType;
+import nju.seec.helper.enums.SlideState;
 import nju.seec.helper.exception.HelperException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
 
+import java.util.Set;
+
 /**
  * @author cst
  */
 @Repository
-public interface SlideDAO extends JpaRepository<Slide, Long>, JpaSpecificationExecutor<Slide> {
+public interface SlideDAO extends JpaRepository<Slide, Long> {
     /**
      * 封装findById
      *
@@ -20,7 +26,37 @@ public interface SlideDAO extends JpaRepository<Slide, Long>, JpaSpecificationEx
      */
     default Slide findSlideById(Long id) {
         return this.findById(id)
-                .filter(slide -> slide.getDeleteAt() == 0)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到课件"));
     }
+
+    /**
+     * 基于课程、课件状态、课件名检索
+     *
+     * @param course
+     * @param slideStates
+     * @param name
+     * @param pageable
+     * @return
+     */
+    Page<Slide> findByCourseAndStateNotInAndNameContains(Course course, Set<SlideState> slideStates, String name, Pageable pageable);
+
+    /**
+     * 基于课程、课件名检索
+     *
+     * @param course
+     * @param key
+     * @param pageable
+     * @return
+     */
+    Page<Slide> findByCourseAndNameContains(Course course, String key, Pageable pageable);
+
+    /**
+     * 基于教师、课件名检索
+     *
+     * @param teacher
+     * @param name
+     * @param pageable
+     * @return
+     */
+    Page<Slide> findByTeacherAndNameContains(User teacher, String name, Pageable pageable);
 }

+ 7 - 5
src/main/java/nju/seec/helper/entity/Course.java

@@ -2,11 +2,12 @@ package nju.seec.helper.entity;
 
 import lombok.Data;
 import lombok.experimental.Accessors;
-import org.hibernate.annotations.ColumnDefault;
-import org.hibernate.annotations.CreationTimestamp;
-import org.hibernate.annotations.DynamicInsert;
-import org.hibernate.annotations.DynamicUpdate;
+import org.hibernate.annotations.*;
 
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.ForeignKey;
+import javax.persistence.Table;
 import javax.persistence.*;
 import java.time.LocalDateTime;
 
@@ -17,9 +18,10 @@ import java.time.LocalDateTime;
 @Accessors(chain = true)
 @Entity
 @Table(name = "course",
-        uniqueConstraints = @UniqueConstraint(name = "course_name_unique", columnNames = {"teacher_id", "delete_at", "name"}))
+        uniqueConstraints = @UniqueConstraint(name = "course_name_unique", columnNames = {"delete_at", "teacher_id", "name"}))
 @DynamicUpdate
 @DynamicInsert
+@Where(clause = "delete_at = 0")
 public class Course {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)

+ 2 - 1
src/main/java/nju/seec/helper/entity/Quiz.java

@@ -22,9 +22,10 @@ import java.util.List;
 @Data
 @Accessors(chain = true)
 @Entity
-@Table(name = "quiz", uniqueConstraints = @UniqueConstraint(name = "quiz_name_unique", columnNames = {"slide_id", "delete_at", "name",}))
+@Table(name = "quiz", uniqueConstraints = @UniqueConstraint(name = "quiz_name_unique", columnNames = {"delete_at", "slide_id", "name",}))
 @DynamicInsert
 @DynamicUpdate
+@Where(clause = "delete_at = 0 and slide_id in (select s.id from slide s where s.delete_at = 0) and course_id in (select c.id from course c where c.delete_at = 0)")
 public class Quiz {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)

+ 2 - 1
src/main/java/nju/seec/helper/entity/Slide.java

@@ -19,9 +19,10 @@ import java.time.LocalDateTime;
 @Accessors(chain = true)
 @Entity
 @Table(name = "slide",
-        uniqueConstraints = @UniqueConstraint(name = "slide_name_unique", columnNames = {"course_id", "delete_at", "name",}))
+        uniqueConstraints = @UniqueConstraint(name = "slide_name_unique", columnNames = {"delete_at", "course_id", "name"}))
 @DynamicUpdate
 @DynamicInsert
+@Where(clause = "delete_at = 0 and course_id in (select c.id from course c where c.delete_at = 0)")
 public class Slide {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)

+ 0 - 30
src/main/java/nju/seec/helper/enums/QuizState.java

@@ -1,13 +1,5 @@
 package nju.seec.helper.enums;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import lombok.NonNull;
-import lombok.val;
-
-import java.util.Map;
-import java.util.Set;
-
 /**
  * @author xst
  * <p>
@@ -28,26 +20,4 @@ public enum QuizState {
     public void setName(String name) {
         this.name = name;
     }
-
-    private static final Map<SlideState, Map<QuizState, Set<QuizType>>> MAP = ImmutableMap.of(
-            SlideState.DRAFT, ImmutableMap.of(NOT_STARTED, ImmutableSet.of(QuizType.BEFORE_CLASS, QuizType.IN_CLASS, QuizType.AFTER_CLASS)),
-            SlideState.BEFORE_CLASS, ImmutableMap.of(NOT_STARTED, ImmutableSet.of(QuizType.IN_CLASS, QuizType.AFTER_CLASS),
-                    ONGOING, ImmutableSet.of(QuizType.BEFORE_CLASS)),
-            SlideState.IN_CLASS, ImmutableMap.of(NOT_STARTED, ImmutableSet.of(QuizType.AFTER_CLASS),
-                    ONGOING, ImmutableSet.of(QuizType.IN_CLASS),
-                    CLOSED, ImmutableSet.of(QuizType.BEFORE_CLASS)),
-            SlideState.AFTER_CLASS, ImmutableMap.of(ONGOING, ImmutableSet.of(QuizType.AFTER_CLASS),
-                    CLOSED, ImmutableSet.of(QuizType.BEFORE_CLASS, QuizType.IN_CLASS)),
-            SlideState.FINISH, ImmutableMap.of(CLOSED, ImmutableSet.of(QuizType.BEFORE_CLASS, QuizType.IN_CLASS, QuizType.AFTER_CLASS))
-    );
-
-    public static QuizState getQuizStateBySlideStateAndQuizType(@NonNull SlideState slideState, @NonNull QuizType quizType) {
-        Map<QuizState, Set<QuizType>> map = MAP.get(slideState);
-        for (val entry : map.entrySet()) {
-            if (entry.getValue().contains(quizType)) {
-                return entry.getKey();
-            }
-        }
-        return null;
-    }
 }

+ 1 - 21
src/main/java/nju/seec/helper/enums/QuizType.java

@@ -1,13 +1,5 @@
 package nju.seec.helper.enums;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import lombok.NonNull;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * @author cst
  */
@@ -23,17 +15,5 @@ public enum QuizType {
     /**
      * 课后测试
      */
-    AFTER_CLASS;
-
-    private final static Map<SlideState, Set<QuizType>> SLIDE_QUIZ_MAP = ImmutableMap.of(
-            SlideState.DRAFT, ImmutableSet.of(BEFORE_CLASS, IN_CLASS, AFTER_CLASS),
-            SlideState.BEFORE_CLASS, ImmutableSet.of(BEFORE_CLASS, IN_CLASS, AFTER_CLASS),
-            SlideState.IN_CLASS, ImmutableSet.of(IN_CLASS, AFTER_CLASS),
-            SlideState.AFTER_CLASS, ImmutableSet.of(AFTER_CLASS),
-            SlideState.FINISH, Collections.emptySet()
-    );
-
-    public static Set<QuizType> getQuizTypesBySlideState(@NonNull SlideState slideState) {
-        return SLIDE_QUIZ_MAP.get(slideState);
-    }
+    AFTER_CLASS
 }

+ 46 - 0
src/main/java/nju/seec/helper/service/AuthService.java

@@ -0,0 +1,46 @@
+package nju.seec.helper.service;
+
+import nju.seec.helper.aspect.auth.LoginUser;
+
+/**
+ * 数据权限检查
+ *
+ * @author cst
+ */
+public interface AuthService<T> {
+    /**
+     * 检查新增权限
+     *
+     * @param user
+     * @param t
+     * @return
+     */
+    boolean existsAddAuth(LoginUser user, T t);
+
+    /**
+     * 检查删除权限
+     *
+     * @param user
+     * @param t
+     * @return
+     */
+    boolean existsDelAuth(LoginUser user, T t);
+
+    /**
+     * 检查更新权限
+     *
+     * @param user
+     * @param t
+     * @return
+     */
+    boolean existsUpdateAuth(LoginUser user, T t);
+
+    /**
+     * 检查获取权限
+     *
+     * @param user
+     * @param t
+     * @return
+     */
+    boolean existsGetAuth(LoginUser user, T t);
+}

+ 4 - 2
src/main/java/nju/seec/helper/service/CommentService.java

@@ -2,6 +2,7 @@ package nju.seec.helper.service;
 
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.comment.CommentDTO;
+import nju.seec.helper.entity.Comment;
 import nju.seec.helper.vo.CommentVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -9,7 +10,7 @@ import org.springframework.data.domain.Pageable;
 /**
  * @author cst
  */
-public interface CommentService {
+public interface CommentService extends AuthService<Comment> {
     /**
      * 创建评论
      *
@@ -56,8 +57,9 @@ public interface CommentService {
     /**
      * 获得某条评论
      *
+     * @param user
      * @param commentId
      * @return
      */
-    CommentVO getOneComment(Long commentId);
+    CommentVO getOneComment(LoginUser user, Long commentId);
 }

+ 2 - 1
src/main/java/nju/seec/helper/service/CourseFileService.java

@@ -2,6 +2,7 @@ package nju.seec.helper.service;
 
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.course.CourseFileDTO;
+import nju.seec.helper.entity.CourseFile;
 import nju.seec.helper.vo.CourseFileVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -9,7 +10,7 @@ import org.springframework.data.domain.Pageable;
 /**
  * @author cst
  */
-public interface CourseFileService {
+public interface CourseFileService extends AuthService<CourseFile> {
     /**
      * 上传课程附件
      *

+ 2 - 10
src/main/java/nju/seec/helper/service/CourseService.java

@@ -3,6 +3,7 @@ package nju.seec.helper.service;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.course.ChooseDTO;
 import nju.seec.helper.dto.course.CourseDTO;
+import nju.seec.helper.entity.Course;
 import nju.seec.helper.vo.CourseVO;
 import nju.seec.helper.vo.UserVO;
 import org.springframework.data.domain.Page;
@@ -11,7 +12,7 @@ import org.springframework.data.domain.Pageable;
 /**
  * @author cst
  */
-public interface CourseService {
+public interface CourseService extends AuthService<Course> {
     /**
      * 创建课程
      *
@@ -112,13 +113,4 @@ public interface CourseService {
      * @return
      */
     Page<UserVO> getChooseStudents(LoginUser user, Long courseId, String key, Pageable pageable);
-
-    /**
-     * 检查是否具有访问课程资源权限
-     *
-     * @param user
-     * @param courseId
-     * @return
-     */
-    boolean existsCourseResourcesAuth(LoginUser user, Long courseId);
 }

+ 2 - 1
src/main/java/nju/seec/helper/service/NoticeService.java

@@ -2,6 +2,7 @@ package nju.seec.helper.service;
 
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.notice.NoticeDTO;
+import nju.seec.helper.entity.Notice;
 import nju.seec.helper.vo.NoticeVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -9,7 +10,7 @@ import org.springframework.data.domain.Pageable;
 /**
  * @author cst
  */
-public interface NoticeService {
+public interface NoticeService extends AuthService<Notice> {
     /**
      * 创建公告
      *

+ 2 - 79
src/main/java/nju/seec/helper/service/QuizService.java

@@ -1,10 +1,8 @@
 package nju.seec.helper.service;
 
-//import nju.seec.helper.dto.quiz.QuizDTO;
-//import nju.seec.helper.vo.quiz.QuizVO;
-
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.quiz.QuizDTO;
+import nju.seec.helper.entity.Quiz;
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.enums.QuizState;
 import nju.seec.helper.vo.quiz.QuizVO;
@@ -16,7 +14,7 @@ import org.springframework.data.domain.Pageable;
  * <p>
  * updated by cst
  */
-public interface QuizService {
+public interface QuizService extends AuthService<Quiz> {
 
     /**
      * 创建测试
@@ -94,79 +92,4 @@ public interface QuizService {
      * @param slide
      */
     void modifyQuizState(Slide slide);
-
-//    /**
-//     * @param quizId
-//     * @param uid
-//     * @param answers
-//     * @return
-//     */
-//    Integer upStudentAnswer(Long quizId, Long uid, List<QuestionStudentAnswerDTO> answers);
-
-//    /**
-//     * @param slideId
-//     * @param pageable
-//     * @param state
-//     * @param uid
-//     * @return
-//     */
-//    Page<Quiz> getAllBasicQuizBySlide(Long slideId, Pageable pageable, QuizState state, Long uid);
-
-//    /**
-//     * @param quiz
-//     * @param slide
-//     * @return
-//     */
-//    QuizState checkQuizState(Quiz quiz, Slide slide);
-
-//    /**
-//     * @param quizId
-//     * @param slideId
-//     * @return
-//     */
-//    QuizState checkQuizState(Long quizId, Long slideId);
-
-//    /**
-//     * @param quizId
-//     * @return
-//     */
-//    QuizVO combineQuiz(Long quizId);
-
-//    /**
-//     * @param quizId
-//     * @param uid
-//     * @param type
-//     * @return
-//     */
-//    QuizVO combineQuizWithAnswer(Long quizId, Long uid, UserType type);
-//
-//
-//    /**
-//     * @param quizId
-//     * @param name
-//     * @param questions
-//     * @param quizTime
-//     * @param slideId
-//     * @return
-//     */
-//    Quiz modifyQuiz(Long quizId, String name, List<String> questions, SlideState quizTime, String slideId);
-//
-//    /**
-//     * @param pageable
-//     * @param state
-//     * @param uid
-//     * @return
-//     */
-//    Page<Quiz> getAllBasicQuizByUser(Pageable pageable, QuizState state, Long uid);
-//
-//    /**
-//     * @param aLong
-//     * @return
-//     */
-//    List<QuizResultVO> getQuizResult(Long aLong);
-//
-//    /**
-//     * @param slide
-//     */
-//    void modifySlideState(Slide slide);
 }

+ 2 - 1
src/main/java/nju/seec/helper/service/ReplyService.java

@@ -2,12 +2,13 @@ package nju.seec.helper.service;
 
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.reply.ReplyDTO;
+import nju.seec.helper.entity.Reply;
 import nju.seec.helper.vo.ReplyVO;
 
 /**
  * @author cst
  */
-public interface ReplyService {
+public interface ReplyService extends AuthService<Reply> {
     /**
      * 创建回复
      *

+ 2 - 1
src/main/java/nju/seec/helper/service/SlideService.java

@@ -4,6 +4,7 @@ import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.slide.SlideDTO;
 import nju.seec.helper.dto.slide.SlideFileDTO;
 import nju.seec.helper.dto.slide.SlideStateDTO;
+import nju.seec.helper.entity.Slide;
 import nju.seec.helper.vo.SlideVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -11,7 +12,7 @@ import org.springframework.data.domain.Pageable;
 /**
  * @author cst
  */
-public interface SlideService {
+public interface SlideService extends AuthService<Slide> {
     /**
      * 创建课件
      *

+ 1 - 1
src/main/java/nju/seec/helper/service/impl/AdminServiceImpl.java

@@ -9,7 +9,7 @@ import nju.seec.helper.enums.UserState;
 import nju.seec.helper.enums.UserType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.AdminService;
-import nju.seec.helper.service.util.StringUtils;
+import nju.seec.helper.util.StringUtils;
 import nju.seec.helper.util.EncryptUtils;
 import nju.seec.helper.vo.AdminVO;
 import nju.seec.helper.vo.UserVO;

+ 55 - 37
src/main/java/nju/seec/helper/service/impl/CommentServiceImpl.java

@@ -7,14 +7,14 @@ import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.dto.comment.CommentDTO;
 import nju.seec.helper.entity.Comment;
 import nju.seec.helper.entity.Slide;
+import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.enums.MessageType;
+import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.CommentService;
 import nju.seec.helper.service.MessageService;
-import nju.seec.helper.service.util.AuthUtils;
 import nju.seec.helper.vo.CommentVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -44,12 +44,14 @@ public class CommentServiceImpl implements CommentService {
     public CommentVO createComment(LoginUser user, CommentDTO commentDTO) {
         Slide slide = slideDAO.findSlideById(commentDTO.getSlideId());
 
-        Comment comment = commentDAO.save(new Comment()
-                .setSlideId(slide.getId())
-                .setPageNumber(commentDTO.getPageNumber())
-                .setUser(userDAO.findUserById(user.getId()))
-                .setTitle(commentDTO.getTitle())
-                .setContent(commentDTO.getContent()));
+        Comment comment = commentDAO.save(
+                new Comment()
+                        .setSlideId(slide.getId())
+                        .setPageNumber(commentDTO.getPageNumber())
+                        .setUser(userDAO.findUserById(user.getId()))
+                        .setTitle(commentDTO.getTitle())
+                        .setContent(commentDTO.getContent())
+        );
 
         // 增加通知
         if (!user.getId().equals(slide.getTeacher().getId())) {
@@ -64,7 +66,9 @@ public class CommentServiceImpl implements CommentService {
     public void deleteComment(LoginUser user, Long commentId) {
         Comment comment = commentDAO.findCommentById(commentId);
 
-        AuthUtils.checkDataAuth(user.getId(), comment.getUser().getId(), "您无权删除该评论");
+        if (!existsDelAuth(user, comment)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该评论");
+        }
 
         unTopComment(comment);
         commentDAO.delete(comment);
@@ -75,19 +79,27 @@ public class CommentServiceImpl implements CommentService {
     public void topComment(LoginUser user, Long commentId) {
         Comment comment = commentDAO.findCommentById(commentId);
 
-        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId(), "您无权在该评论区置顶评论");
+        if (!existsUpdateAuth(user, comment)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权在该评论区置顶评论");
+        }
 
         unTopComment(comment);
         comment.setTopNumber((int) countTopComments(comment.getSlideId(), comment.getPageNumber()) + 1);
         commentDAO.save(comment);
     }
 
+    private long countTopComments(Long slideId, Integer pageNumber) {
+        return commentDAO.countBySlideIdAndPageNumberAndTopNumberNot(slideId, pageNumber, 0);
+    }
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void unTopComment(LoginUser user, Long commentId) {
         Comment comment = commentDAO.findCommentById(commentId);
 
-        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId(), "您无权在该评论区取消置顶评论");
+        if (!existsUpdateAuth(user, comment)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权在该评论区取消置顶评论");
+        }
 
         unTopComment(comment);
         commentDAO.save(comment);
@@ -96,46 +108,52 @@ public class CommentServiceImpl implements CommentService {
     @Transactional(readOnly = true)
     @Override
     public Page<CommentVO> getCommentsBySlideAndPageNumber(Long slideId, Integer pageNumber, Pageable pageable) {
-        return findBySlideIdAndPageNumber(slideId, pageNumber, pageable).map(CommentVO::new);
+        return commentDAO.findBySlideIdAndPageNumberAndShow(slideId, pageNumber, true, pageable).map(CommentVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
-    public CommentVO getOneComment(Long commentId) {
-        return new CommentVO(commentDAO.findCommentById(commentId));
+    public CommentVO getOneComment(LoginUser user, Long commentId) {
+        Comment comment = commentDAO.findCommentById(commentId);
+
+        if (!existsGetAuth(user, comment)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权参考该评论");
+        }
+
+        return new CommentVO(comment);
     }
 
     private void unTopComment(Comment comment) {
         Integer topNumber = comment.getTopNumber();
         comment.setTopNumber(0);
-        decTopNumber(comment.getSlideId(), comment.getPageNumber(), topNumber);
+        if (topNumber != 0) {
+            commentDAO.saveAll(
+                    commentDAO.findBySlideIdAndPageNumberAndTopNumberGreaterThan(comment.getSlideId(), comment.getPageNumber(), topNumber)
+                            .parallelStream()
+                            .peek(topComment -> topComment.setTopNumber(comment.getTopNumber() - 1))
+                            .collect(Collectors.toList())
+            );
+        }
+    }
+
+    @Override
+    public boolean existsAddAuth(LoginUser user, Comment comment) {
+        return true;
     }
 
-    long countTopComments(Long slideId, Integer pageNumber) {
-        return commentDAO.count(
-                (Specification<Comment>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("slideId"), slideId),
-                        cb.equal(root.get("pageNumber"), pageNumber),
-                        cb.notEqual(root.get("topNumber"), 0)));
+    @Override
+    public boolean existsDelAuth(LoginUser user, Comment comment) {
+        return comment.getUser().getId().equals(user.getId());
     }
 
-    Page<Comment> findBySlideIdAndPageNumber(Long slideId, Integer pageNumber, Pageable pageable) {
-        return commentDAO.findAll(
-                (Specification<Comment>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("slideId"), slideId),
-                        cb.equal(root.get("pageNumber"), pageNumber)),
-                pageable);
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, Comment comment) {
+        return slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId().equals(user.getId());
     }
 
-    void decTopNumber(Long slideId, Integer pageNumber, Integer topNumber) {
-        commentDAO.saveAll(
-                commentDAO
-                        .findAll((Specification<Comment>) (root, query, cb) -> cb.and(
-                                cb.equal(root.get("slideId"), slideId),
-                                cb.equal(root.get("pageNumber"), pageNumber),
-                                cb.gt(root.get("topNumber"), topNumber)))
-                        .parallelStream()
-                        .peek(comment -> comment.setTopNumber(comment.getTopNumber() - 1))
-                        .collect(Collectors.toSet()));
+    @Override
+    public boolean existsGetAuth(LoginUser user, Comment comment) {
+        return comment.getShow()
+                || comment.getUser().getId().equals(user.getId());
     }
 }

+ 43 - 13
src/main/java/nju/seec/helper/service/impl/CourseFileServiceImpl.java

@@ -11,7 +11,6 @@ import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.CourseFileService;
 import nju.seec.helper.service.CourseService;
-import nju.seec.helper.service.util.AuthUtils;
 import nju.seec.helper.service.util.OssObjectUrlUtils;
 import nju.seec.helper.util.OssUtils;
 import nju.seec.helper.vo.CourseFileVO;
@@ -51,22 +50,26 @@ public class CourseFileServiceImpl implements CourseFileService {
     @Override
     public void uploadCourseFile(LoginUser user, CourseFileDTO courseFileDTO) {
         Course course = courseDAO.findCourseById(courseFileDTO.getCourseId());
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权上传该课程的附件");
 
         MultipartFile file = courseFileDTO.getFile();
         String fileName = file.getOriginalFilename();
-
-        if (courseFileDAO.existsByCourseIdAndFileName(courseFileDTO.getCourseId(), fileName)) {
-            throw HelperException.of(ExceptionType.CONFLICT, "已有同名附件");
-        }
-
         String objectName = getCourseFileObjectName(course.getId(), fileName);
 
-        courseFileDAO.save(new CourseFile()
+        CourseFile courseFile = new CourseFile()
                 .setCourseId(course.getId())
                 .setTeacherId(user.getId())
-                .setFileName(file.getOriginalFilename())
-                .setObjectName(objectName));
+                .setFileName(fileName)
+                .setObjectName(objectName);
+
+        if (!existsAddAuth(user, courseFile)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权上传该附件");
+        }
+
+        if (courseFileDAO.existsByCourseIdAndFileName(courseFileDTO.getCourseId(), fileName)) {
+            throw HelperException.of(ExceptionType.CONFLICT, "该课程已有同名附件");
+        }
+
+        courseFileDAO.save(courseFile);
 
         ossUtils.upload(objectName, file.getInputStream());
     }
@@ -75,7 +78,11 @@ public class CourseFileServiceImpl implements CourseFileService {
     @Override
     public void deleteCourseFile(LoginUser user, Long courseFileId) {
         CourseFile courseFile = courseFileDAO.findCourseFileById(courseFileId);
-        AuthUtils.checkDataAuth(user.getId(), courseFile.getTeacherId(), "您无权删除该附件");
+
+        if (!existsDelAuth(user, courseFile)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该附件");
+        }
+
         courseFileDAO.delete(courseFile);
         ossUtils.delete(courseFile.getObjectName());
     }
@@ -83,9 +90,10 @@ public class CourseFileServiceImpl implements CourseFileService {
     @Transactional(readOnly = true)
     @Override
     public Page<CourseFileVO> getCourseFilesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
-        if (!courseService.existsCourseResourcesAuth(user, courseId)) {
+        if (!courseService.existsGetAuth(user, courseDAO.findCourseById(courseId))) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课程的附件");
         }
+
         return courseFileDAO.findByCourseIdAndFileNameContains(courseId, key, pageable).map(CourseFileVO::new);
     }
 
@@ -94,7 +102,9 @@ public class CourseFileServiceImpl implements CourseFileService {
     public String getCourseFileUrl(LoginUser user, Long courseFileId) {
         CourseFile courseFile = courseFileDAO.findCourseFileById(courseFileId);
 
-        if (!courseService.existsCourseResourcesAuth(user, courseFile.getCourseId())) {
+        System.currentTimeMillis();
+
+        if (!existsGetAuth(user, courseFile)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权获取该附件的链接");
         }
 
@@ -104,4 +114,24 @@ public class CourseFileServiceImpl implements CourseFileService {
     private String getCourseFileObjectName(Long courseId, String fileName) {
         return COURSE_FILE_STORE_DIR + File.separator + courseId + File.separator + fileName;
     }
+
+    @Override
+    public boolean existsAddAuth(LoginUser user, CourseFile courseFile) {
+        return courseDAO.findCourseById(courseFile.getCourseId()).getTeacher().getId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsDelAuth(LoginUser user, CourseFile courseFile) {
+        return courseFile.getTeacherId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, CourseFile courseFile) {
+        return courseFile.getTeacherId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsGetAuth(LoginUser user, CourseFile courseFile) {
+        return courseService.existsGetAuth(user, courseDAO.findCourseById(courseFile.getCourseId()));
+    }
 }

+ 62 - 77
src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java

@@ -9,22 +9,18 @@ import nju.seec.helper.dto.course.CourseDTO;
 import nju.seec.helper.entity.Choose;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.enums.ExceptionType;
+import nju.seec.helper.enums.UserType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.CourseService;
-import nju.seec.helper.service.util.AuthUtils;
-import nju.seec.helper.service.util.StringUtils;
 import nju.seec.helper.util.EncryptUtils;
 import nju.seec.helper.vo.CourseVO;
 import nju.seec.helper.vo.UserVO;
+import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.persistence.criteria.JoinType;
-import java.util.Set;
-
 /**
  * @author cst
  */
@@ -43,14 +39,19 @@ public class CourseServiceImpl implements CourseService {
     @Transactional(rollbackFor = Exception.class)
     @Override
     public CourseVO createCourse(LoginUser teacher, CourseDTO courseDTO) {
-        // 检查
-        checkSameCourseName(0L, teacher.getId(), courseDTO.getName());
-
-        return new CourseVO(courseDAO.save(new Course()
-                .setTeacher(userDAO.findUserById(teacher.getId()))
-                .setName(courseDTO.getName())
-                .setBio(courseDTO.getBio())
-                .setCode(EncryptUtils.encode(courseDTO.getCode()))));
+        try {
+            return new CourseVO(
+                    courseDAO.save(
+                            new Course()
+                                    .setTeacher(userDAO.findUserById(teacher.getId()))
+                                    .setName(courseDTO.getName())
+                                    .setBio(courseDTO.getBio())
+                                    .setCode(EncryptUtils.encode(courseDTO.getCode()))
+                    )
+            );
+        } catch (DataIntegrityViolationException e) {
+            throw HelperException.of(ExceptionType.CONFLICT, "创建失败,请检查课程信息是否正确且不与已有课程冲突");
+        }
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -58,20 +59,31 @@ public class CourseServiceImpl implements CourseService {
     public CourseVO modifyCourse(LoginUser user, Long courseId, CourseDTO courseDTO) {
         Course course = courseDAO.findCourseById(courseId);
 
-        // 检查
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权修改该课程");
-        checkSameCourseName(course.getId(), user.getId(), courseDTO.getName());
+        if (!existsUpdateAuth(user, course)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该课程");
+        }
 
-        return new CourseVO(courseDAO.save(course.setName(courseDTO.getName())
-                .setBio(courseDTO.getBio())
-                .setCode(EncryptUtils.encode(courseDTO.getCode()))));
+        try {
+            return new CourseVO(
+                    courseDAO.save(
+                            course.setName(courseDTO.getName())
+                                    .setBio(courseDTO.getBio())
+                                    .setCode(EncryptUtils.encode(courseDTO.getCode()))
+                    )
+            );
+        } catch (DataIntegrityViolationException e) {
+            throw HelperException.of(ExceptionType.CONFLICT, "修改失败,请检查课程信息是否正确且不与已有课程冲突");
+        }
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void deleteCourse(LoginUser user, Long courseId) {
         Course course = courseDAO.findCourseById(courseId);
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权删除该课程");
+
+        if (!existsUpdateAuth(user, course)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该课程");
+        }
 
         course.setDeleteAt(System.currentTimeMillis());
         courseDAO.save(course);
@@ -81,6 +93,7 @@ public class CourseServiceImpl implements CourseService {
     @Override
     public void chooseCourse(LoginUser student, Long courseId, ChooseDTO chooseDTO) {
         Long studentId = student.getId();
+
         if (chooseDAO.existsByStudentIdAndCourseId(studentId, courseId)) {
             throw HelperException.of(ExceptionType.CONFLICT, "您已选课");
         }
@@ -89,9 +102,11 @@ public class CourseServiceImpl implements CourseService {
             throw HelperException.of(ExceptionType.NOT_FOUND, "选课码错误");
         }
 
-        chooseDAO.save(new Choose()
-                .setCourseId(courseId)
-                .setStudentId(studentId));
+        chooseDAO.save(
+                new Choose()
+                        .setCourseId(courseId)
+                        .setStudentId(studentId)
+        );
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -103,26 +118,29 @@ public class CourseServiceImpl implements CourseService {
     @Transactional(readOnly = true)
     @Override
     public Page<CourseVO> getCourses(String key, Pageable pageable) {
-        return findByKey(key, pageable).map(CourseVO::new);
+        return courseDAO.findByKey(key, pageable).map(CourseVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<CourseVO> getCreatedCourses(LoginUser user, String key, Pageable pageable) {
-        return findByTeacherIdAndKey(user.getId(), key, pageable).map(CourseVO::new);
+        return courseDAO.findByTeacherAndNameContains(userDAO.findUserById(user.getId()), key, pageable).map(CourseVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<CourseVO> getChosenCourses(LoginUser user, String key, Pageable pageable) {
-        return findByIdsAndKey(chooseDAO.findCourseIdsByStudentId(user.getId()), key, pageable).map(CourseVO::new);
+        return courseDAO.findByIdsAndKey(chooseDAO.findCourseIdsByStudentId(user.getId()), key, pageable).map(CourseVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public String getCourseCode(LoginUser user, Long courseId) {
         Course course = courseDAO.findCourseById(courseId);
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权获取该课程选课码");
+
+        if (!existsGetAuth(user, course)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权获取该课程选课码");
+        }
 
         return EncryptUtils.decode(course.getCode());
     }
@@ -137,65 +155,32 @@ public class CourseServiceImpl implements CourseService {
     @Override
     public Page<UserVO> getChooseStudents(LoginUser user, Long courseId, String key, Pageable pageable) {
         Course course = courseDAO.findCourseById(courseId);
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权获取该课程的选课名单");
+
+        if (!existsGetAuth(user, course)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权获取该课程的选课名单");
+        }
 
         return userDAO.findByIdIn(chooseDAO.findStudentIdsByCourseId(courseId), pageable).map(UserVO::new);
     }
 
     @Override
-    public boolean existsCourseResourcesAuth(LoginUser user, Long courseId) {
-        switch (user.getType()) {
-            case TEACHER:
-                return courseDAO.findCourseById(courseId).getTeacher().getId().equals(user.getId());
-            case STUDENT:
-                return chooseDAO.existsByStudentIdAndCourseId(user.getId(), courseId);
-            default:
-                return false;
-        }
-    }
-
-    private void checkSameCourseName(Long courseId, Long teacherId, String name) {
-        long exists = courseDAO.count((Specification<Course>) (root, query, cb) ->
-                cb.and(cb.notEqual(root.get("id"), courseId)
-                        , cb.equal(root.get("teacher").get("id"), teacherId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.equal(root.get("name"), name)
-                ));
-        if (exists > 0) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该课程名已使用");
-        }
+    public boolean existsAddAuth(LoginUser user, Course course) {
+        return user.getType() == UserType.TEACHER;
     }
 
-    private Page<Course> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable) {
-        return courseDAO.findAll(
-                (Specification<Course>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("teacher").get("id"), teacherId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                ),
-                pageable);
+    @Override
+    public boolean existsDelAuth(LoginUser user, Course course) {
+        return course.getTeacher().getId().equals(user.getId());
     }
 
-    private Page<Course> findByKey(String key, Pageable pageable) {
-        final String keyPattern = StringUtils.keyPattern(key);
-
-        return courseDAO.findAll(
-                (Specification<Course>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("deleteAt"), 0L)
-                        , cb.or(cb.like(root.get("name"), keyPattern), cb.like(root.join("teacher", JoinType.LEFT).get("name"), keyPattern))
-                ),
-                pageable);
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, Course course) {
+        return course.getTeacher().getId().equals(user.getId());
     }
 
-    private Page<Course> findByIdsAndKey(Set<Long> courseIds, String key, Pageable pageable) {
-        final String keyPattern = StringUtils.keyPattern(key);
-
-        return courseDAO.findAll(
-                (Specification<Course>) (root, query, cb) -> cb.and(
-                        root.get("id").in(courseIds)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.or(cb.like(root.get("name"), keyPattern), cb.like(root.join("teacher", JoinType.LEFT).get("name"), keyPattern))
-                ),
-                pageable);
+    @Override
+    public boolean existsGetAuth(LoginUser user, Course course) {
+        return course.getTeacher().getId().equals(user.getId())
+                || chooseDAO.existsByStudentIdAndCourseId(user.getId(), course.getId());
     }
 }

+ 43 - 20
src/main/java/nju/seec/helper/service/impl/NoticeServiceImpl.java

@@ -7,15 +7,14 @@ import nju.seec.helper.dao.NoticeDAO;
 import nju.seec.helper.dto.notice.NoticeDTO;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Notice;
+import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.enums.MessageType;
+import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.service.NoticeService;
-import nju.seec.helper.service.util.AuthUtils;
-import nju.seec.helper.service.util.StringUtils;
 import nju.seec.helper.vo.NoticeVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -43,37 +42,52 @@ public class NoticeServiceImpl implements NoticeService {
     @Override
     public NoticeVO createNotice(LoginUser user, NoticeDTO noticeDTO) {
         Course course = courseDAO.findCourseById(noticeDTO.getCourseId());
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权创建该课程的公告");
 
-        Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(noticeDTO.getCourseId());
-        messageService.createMessage(studentIds, MessageType.NOTICE_NEW, String.format("[%s] 发布了新公告", course.getName()), null);
-        return new NoticeVO(noticeDAO.save(new Notice()
+        Notice notice = new Notice()
                 .setCourseId(noticeDTO.getCourseId())
                 .setTeacherId(user.getId())
                 .setTitle(noticeDTO.getTitle())
-                .setContent(noticeDTO.getContent())));
+                .setContent(noticeDTO.getContent());
+
+        if (!existsAddAuth(user, notice)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权创建该公告");
+        }
+
+        Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(noticeDTO.getCourseId());
+        messageService.createMessage(studentIds, MessageType.NOTICE_NEW, String.format("[%s] 发布了新公告", course.getName()), course.getId());
+        return new NoticeVO(noticeDAO.save(notice));
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public NoticeVO modifyNotice(LoginUser user, Long noticeId, NoticeDTO noticeDTO) {
         Notice notice = noticeDAO.findNoticeById(noticeId);
-        AuthUtils.checkDataAuth(user.getId(), notice.getTeacherId(), "您无权修改该公告");
 
-        return new NoticeVO(noticeDAO.save(notice.setTitle(noticeDTO.getTitle()).setContent(noticeDTO.getContent())));
+        if (!existsUpdateAuth(user, notice)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该公告");
+        }
+
+        return new NoticeVO(
+                noticeDAO.save(
+                        notice.setTitle(noticeDTO.getTitle())
+                                .setContent(noticeDTO.getContent())
+                )
+        );
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<NoticeVO> getNoticesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
-        return findByCourseIdAndKey(courseId, key, pageable).map(NoticeVO::new);
+        return noticeDAO.findByCourseIdAndKey(courseId, key, pageable).map(NoticeVO::new);
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void deleteNotice(LoginUser user, Long noticeId) {
         Notice notice = noticeDAO.findNoticeById(noticeId);
-        AuthUtils.checkDataAuth(user.getId(), notice.getTeacherId(), "您无权删除该公告");
+        if (!existsDelAuth(user, notice)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该公告");
+        }
         noticeDAO.delete(notice);
     }
 
@@ -83,14 +97,23 @@ public class NoticeServiceImpl implements NoticeService {
         return new NoticeVO(noticeDAO.findNoticeById(noticeId));
     }
 
-    private Page<Notice> findByCourseIdAndKey(Long courseId, String key, Pageable pageable) {
-        String keyPattern = StringUtils.keyPattern(key);
+    @Override
+    public boolean existsAddAuth(LoginUser user, Notice notice) {
+        return courseDAO.findCourseById(notice.getCourseId()).getTeacher().getId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsDelAuth(LoginUser user, Notice notice) {
+        return notice.getTeacherId().equals(user.getId());
+    }
 
-        return noticeDAO.findAll(
-                (Specification<Notice>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("courseId"), courseId)
-                        , cb.or(cb.like(root.get("title"), keyPattern), cb.like(root.get("content"), keyPattern))
-                ),
-                pageable);
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, Notice notice) {
+        return notice.getTeacherId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsGetAuth(LoginUser user, Notice notice) {
+        return true;
     }
 }

+ 88 - 105
src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java

@@ -1,5 +1,6 @@
 package nju.seec.helper.service.impl;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.ChooseDAO;
@@ -14,17 +15,17 @@ import nju.seec.helper.enums.*;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.service.QuizService;
-import nju.seec.helper.service.util.AuthUtils;
-import nju.seec.helper.service.util.StringUtils;
 import nju.seec.helper.vo.quiz.QuizVO;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 
@@ -51,27 +52,60 @@ public class QuizServiceImpl implements QuizService {
         this.messageService = messageService;
     }
 
+    private static Map<SlideState, Map<QuizType, QuizState>> MAP = ImmutableMap.of(
+            SlideState.DRAFT, ImmutableMap.of(
+                    QuizType.BEFORE_CLASS, QuizState.NOT_STARTED,
+                    QuizType.IN_CLASS, QuizState.NOT_STARTED,
+                    QuizType.AFTER_CLASS, QuizState.NOT_STARTED
+            ),
+            SlideState.BEFORE_CLASS, ImmutableMap.of(
+                    QuizType.BEFORE_CLASS, QuizState.ONGOING,
+                    QuizType.IN_CLASS, QuizState.NOT_STARTED,
+                    QuizType.AFTER_CLASS, QuizState.NOT_STARTED
+            ),
+            SlideState.IN_CLASS, ImmutableMap.of(
+                    QuizType.BEFORE_CLASS, QuizState.CLOSED,
+                    QuizType.IN_CLASS, QuizState.ONGOING,
+                    QuizType.AFTER_CLASS, QuizState.NOT_STARTED
+            ),
+            SlideState.AFTER_CLASS, ImmutableMap.of(
+                    QuizType.BEFORE_CLASS, QuizState.CLOSED,
+                    QuizType.IN_CLASS, QuizState.CLOSED,
+                    QuizType.AFTER_CLASS, QuizState.ONGOING
+            ),
+            SlideState.FINISH, ImmutableMap.of(
+                    QuizType.BEFORE_CLASS, QuizState.CLOSED,
+                    QuizType.IN_CLASS, QuizState.CLOSED,
+                    QuizType.AFTER_CLASS, QuizState.CLOSED
+            )
+    );
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public QuizVO createQuiz(LoginUser user, QuizDTO quizDTO) {
         Slide slide = slideDAO.findSlideById(quizDTO.getSlideId());
 
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权创建该课件的测试");
-        checkSameName(0L, quizDTO.getSlideId(), quizDTO.getName());
-        checkQuizTypeOkToSlideState(slide.getState(), quizDTO.getType());
-
-        QuizState quizState = QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType());
-        Quiz quiz = quizDAO.save(new Quiz()
+        Quiz quiz = new Quiz()
                 .setCourse(slide.getCourse())
                 .setSlide(slide)
                 .setTeacher(slide.getTeacher())
                 .setType(quizDTO.getType())
-                .setState(quizState)
+                .setState(MAP.get(slide.getState()).get(quizDTO.getType()))
                 .setName(quizDTO.getName())
                 .setMaxSubmitNumber(quizDTO.getMaxSubmitTime())
-                .setQuestions(quizDTO.getQuestions()));
-        sendMessages(quiz);
-        return new QuizVO(quiz);
+                .setQuestions(quizDTO.getQuestions());
+
+        if (!existsAddAuth(user, quiz)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权创建该测试");
+        }
+
+        try {
+            quiz = quizDAO.save(quiz);
+            sendMessages(quiz);
+            return new QuizVO(quiz);
+        } catch (DataIntegrityViolationException e) {
+            throw HelperException.of(ExceptionType.CONFLICT, "创建失败,请检查测试信息是否正确且不与已有测试冲突");
+        }
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -79,70 +113,52 @@ public class QuizServiceImpl implements QuizService {
     public QuizVO modifyQuiz(LoginUser user, Long quizId, QuizDTO quizDTO) {
         Quiz quiz = quizDAO.findQuizById(quizId);
 
-        AuthUtils.checkDataAuth(user.getId(), quiz.getTeacher().getId(), "您无权修改该测试");
-        checkSubmit(quiz);
-
-        Slide slide = quiz.getSlide();
-        checkQuizTypeOkToSlideState(slide.getState(), quizDTO.getType());
-
-        checkSameName(quiz.getId(), quizDTO.getSlideId(), quizDTO.getName());
-
-        QuizState quizState = QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType());
-        quiz = quizDAO.save(quiz
-                .setType(quizDTO.getType())
-                .setState(quizState)
-                .setName(quizDTO.getName())
-                .setMaxSubmitNumber(quizDTO.getMaxSubmitTime())
-                .setQuestions(quizDTO.getQuestions()));
-        sendMessages(quiz);
-        return new QuizVO(quiz);
-    }
-
-    private void checkSameName(Long quizId, Long slideId, String name) {
-        long exists = quizDAO.count((Specification<Quiz>) (root, query, cb) ->
-                cb.and(cb.notEqual(root.get("id"), quizId),
-                        cb.equal(root.get("slide").get("id"), slideId),
-                        cb.equal(root.get("name"), name)
-                ));
-        if (exists > 0) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该测试名已使用");
+        if (existsUpdateAuth(user, quiz)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该测试");
         }
-    }
 
-    private void checkQuizTypeOkToSlideState(SlideState slideState, QuizType quizType) {
-        if (!QuizType.getQuizTypesBySlideState(slideState).contains(quizType)) {
-            throw HelperException.of(ExceptionType.FORBIDDEN, "课件暂时不支持创建该类型的测试,请检查课件状态");
-        }
-    }
-
-    private void checkSubmit(Quiz quiz) {
         if (quizStudentAnswerDAO.existsByQuiz(quiz)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "该测试已有提交,无法修改");
         }
+
+        try {
+            quiz = quizDAO.save(quiz
+                    .setType(quizDTO.getType())
+                    .setState(MAP.get(quiz.getSlide().getState()).get(quizDTO.getType()))
+                    .setName(quizDTO.getName())
+                    .setMaxSubmitNumber(quizDTO.getMaxSubmitTime())
+                    .setQuestions(quizDTO.getQuestions()));
+            sendMessages(quiz);
+            return new QuizVO(quiz);
+        } catch (DataIntegrityViolationException e) {
+            throw HelperException.of(ExceptionType.CONFLICT, "修改失败,请检查测试信息是否正确且不与已有测试冲突");
+        }
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void deleteQuiz(LoginUser user, Long quizId) {
         Quiz quiz = quizDAO.findQuizById(quizId);
-        AuthUtils.checkDataAuth(user.getId(), quiz.getTeacher().getId(), "您无权删除该测试");
+        if (!existsDelAuth(user, quiz)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该测试");
+        }
         quiz.setDeleteAt(System.currentTimeMillis());
         quizDAO.save(quiz);
         messageService.deleteMessages(quizId, MessageType.QUIZ_NEW);
     }
 
+    private static final Set<QuizState> STATES_NOT_GET_BY_STUDENT_SET = ImmutableSet.of(QuizState.NOT_STARTED);
+
     @Transactional(readOnly = true)
     @Override
     public Page<QuizVO> teacherGetQuizzesBySlide(LoginUser user, Long slideId, String key, Pageable pageable) {
-        return findBySlideIdAndKey(slideId, key, pageable).map(QuizVO::new);
+        return quizDAO.findBySlideAndNameContains(slideDAO.findSlideById(slideId), key, pageable).map(QuizVO::new);
     }
 
-    public static final Set<QuizState> STATES_NOT_GET_BY_STUDENT_SET = ImmutableSet.of(QuizState.NOT_STARTED);
-
     @Transactional(readOnly = true)
     @Override
     public Page<QuizVO> studentGetQuizzesBySlide(LoginUser user, Long slideId, String key, Pageable pageable) {
-        return findBySlideIdAndStateNotInAndKey(slideId, STATES_NOT_GET_BY_STUDENT_SET, key, pageable).map(QuizVO::new);
+        return quizDAO.findBySlideAndStateNotInAndNameContains(slideDAO.findSlideById(slideId), STATES_NOT_GET_BY_STUDENT_SET, key, pageable).map(QuizVO::new);
     }
 
     @Transactional(readOnly = true)
@@ -152,24 +168,25 @@ public class QuizServiceImpl implements QuizService {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您暂时无法获取该状态的测试");
         }
         Set<Long> courseIds = chooseDAO.findCourseIdsByStudentId(user.getId());
-        return findByCourseIdInAndStateAndKey(courseIds, state, key, pageable).map(QuizVO::new);
+        return quizDAO.findByCourseIdsAndStateAndNameContains(courseIds, state, key, pageable).map(QuizVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public QuizVO getOneQuiz(LoginUser user, Long quizId) {
         Quiz quiz = quizDAO.findQuizById(quizId);
-        checkDataAccessAuth(user, quiz);
+        if (!existsGetAuth(user, quiz)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权查看该测试");
+        }
         return new QuizVO(quiz);
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void modifyQuizState(Slide slide) {
-        List<Quiz> quizzes = quizDAO.findAllBySlide(slide);
+        List<Quiz> quizzes = quizDAO.findBySlide(slide);
         quizzes.forEach(quiz -> {
-            QuizState quizState = QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quiz.getType());
-            quiz.setState(quizState);
+            quiz.setState(MAP.get(slide.getState()).get(quiz.getType()));
             sendMessages(quiz);
         });
     }
@@ -183,58 +200,24 @@ public class QuizServiceImpl implements QuizService {
         }
     }
 
-    private void checkDataAccessAuth(LoginUser user, Quiz quiz) {
-        HelperException forbiddenEx = HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该测试");
-
-        switch (user.getType()) {
-            case TEACHER:
-                break;
-            case STUDENT:
-                if (quiz.getState() == QuizState.NOT_STARTED) {
-                    throw forbiddenEx;
-                }
-                break;
-            default:
-                throw forbiddenEx;
-        }
+    @Override
+    public boolean existsAddAuth(LoginUser user, Quiz quiz) {
+        return quiz.getCourse().getTeacher().getId().equals(user.getId());
     }
 
-    private Page<Quiz> findByCourseIdInAndStateAndKey(Set<Long> courseIds, QuizState state, String key, Pageable pageable) {
-        return quizDAO.findAll(
-                (Specification<Quiz>) (root, query, cb) -> cb.and(
-                        root.get("course").get("id").in(courseIds)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.equal(root.get("state"), state)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                        , cb.equal(root.get("slide").get("deleteAt"), 0L)
-                        , cb.equal(root.get("course").get("deleteAt"), 0L)
-                ),
-                pageable);
+    @Override
+    public boolean existsDelAuth(LoginUser user, Quiz quiz) {
+        return quiz.getTeacher().getId().equals(user.getId());
     }
 
-    private Page<Quiz> findBySlideIdAndKey(Long slideId, String key, Pageable pageable) {
-        return quizDAO.findAll(
-                (Specification<Quiz>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("slide").get("id"), slideId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                        , cb.equal(root.get("slide").get("deleteAt"), 0L)
-                        , cb.equal(root.get("course").get("deleteAt"), 0L)
-                ),
-                pageable);
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, Quiz quiz) {
+        return quiz.getTeacher().getId().equals(user.getId());
     }
 
-
-    private Page<Quiz> findBySlideIdAndStateNotInAndKey(Long slideId, Set<QuizState> exclusiveStates, String key, Pageable pageable) {
-        return quizDAO.findAll(
-                (Specification<Quiz>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("slide").get("id"), slideId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                        , cb.not(root.get("state").in(exclusiveStates))
-                        , cb.equal(root.get("slide").get("deleteAt"), 0L)
-                        , cb.equal(root.get("course").get("deleteAt"), 0L)
-                ),
-                pageable);
+    @Override
+    public boolean existsGetAuth(LoginUser user, Quiz quiz) {
+        return quiz.getState() != QuizState.NOT_STARTED
+                || user.getType() == UserType.TEACHER;
     }
 }

+ 1 - 8
src/main/java/nju/seec/helper/service/impl/QuizStudentAnswerServiceImpl.java

@@ -11,7 +11,6 @@ import nju.seec.helper.entity.QuizStudentAnswer;
 import nju.seec.helper.entity.User;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
-import nju.seec.helper.service.CourseService;
 import nju.seec.helper.service.QuestionService;
 import nju.seec.helper.service.QuizStudentAnswerService;
 import nju.seec.helper.vo.QuizStudentAnswerStatisticVO;
@@ -38,14 +37,12 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
     private final QuizStudentAnswerDAO quizStudentAnswerDAO;
 
     private final QuestionService questionService;
-    private final CourseService courseService;
 
-    public QuizStudentAnswerServiceImpl(QuizDAO quizDAO, UserDAO userDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, QuestionService questionService, CourseService courseService) {
+    public QuizStudentAnswerServiceImpl(QuizDAO quizDAO, UserDAO userDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, QuestionService questionService) {
         this.quizDAO = quizDAO;
         this.userDAO = userDAO;
         this.quizStudentAnswerDAO = quizStudentAnswerDAO;
         this.questionService = questionService;
-        this.courseService = courseService;
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -53,10 +50,6 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
     public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, Long quizId, QuizStudentAnswerDTO quizStudentAnswerDTO) {
         Quiz quiz = quizDAO.findQuizById(quizId);
 
-        if (!courseService.existsCourseResourcesAuth(user, quiz.getCourse().getId())) {
-            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权提交该测试的作答");
-        }
-
         switch (quiz.getState()) {
             case NOT_STARTED:
                 throw HelperException.of(ExceptionType.FORBIDDEN, "测试暂未开始");

+ 33 - 8
src/main/java/nju/seec/helper/service/impl/ReplyServiceImpl.java

@@ -12,10 +12,10 @@ import nju.seec.helper.entity.Reply;
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.enums.MessageType;
+import nju.seec.helper.enums.UserType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.service.ReplyService;
-import nju.seec.helper.service.util.AuthUtils;
 import nju.seec.helper.vo.ReplyVO;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -47,17 +47,19 @@ public class ReplyServiceImpl implements ReplyService {
     @Override
     public ReplyVO createReply(LoginUser user, ReplyDTO replyDTO) {
         Comment comment = commentDAO.findCommentById(replyDTO.getCommentId());
-        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId(), "您无权回复该评论");
 
-        if (comment.getReply() != null) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该评论已回复");
-        }
-        Reply reply = new Reply()
+        Reply reply = (comment.getReply() != null ?
+                comment.getReply() :
+                new Reply())
                 .setComment(comment)
                 .setTeacher(userDAO.findUserById(user.getId()))
                 .setContent(replyDTO.getContent());
+
+        if (!existsAddAuth(user, reply)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权回复该评论");
+        }
+
         comment.setReply(reply);
-        reply.setComment(comment);
         reply = replyDAO.save(reply);
 
         // 增加通知
@@ -73,10 +75,33 @@ public class ReplyServiceImpl implements ReplyService {
     @Override
     public void deleteReply(LoginUser user, Long replyId) {
         Reply reply = replyDAO.findReplyById(replyId);
-        AuthUtils.checkDataAuth(user.getId(), reply.getTeacher().getId(), "您无权删除该评论");
+
+        if (!existsDelAuth(user, reply)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该评论");
+        }
 
         Comment comment = reply.getComment();
         comment.setReply(null);
         replyDAO.delete(reply);
     }
+
+    @Override
+    public boolean existsAddAuth(LoginUser user, Reply reply) {
+        return user.getType() == UserType.TEACHER;
+    }
+
+    @Override
+    public boolean existsDelAuth(LoginUser user, Reply reply) {
+        return reply.getTeacher().getId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, Reply reply) {
+        return reply.getTeacher().getId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsGetAuth(LoginUser user, Reply reply) {
+        return true;
+    }
 }

+ 76 - 87
src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java

@@ -21,16 +21,14 @@ import nju.seec.helper.service.CourseService;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.service.QuizService;
 import nju.seec.helper.service.SlideService;
-import nju.seec.helper.service.util.AuthUtils;
 import nju.seec.helper.service.util.OssObjectUrlUtils;
-import nju.seec.helper.service.util.StringUtils;
 import nju.seec.helper.util.OssUtils;
 import nju.seec.helper.util.file.FileInfo;
 import nju.seec.helper.util.file.FileUtils;
 import nju.seec.helper.vo.SlideVO;
+import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
@@ -75,9 +73,6 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public SlideVO createSlide(LoginUser teacher, SlideDTO slideDTO) {
         Course course = courseDAO.findCourseById(slideDTO.getCourseId());
-        AuthUtils.checkDataAuth(teacher.getId(), course.getTeacher().getId(), "您无权创建该课程的课件");
-
-        checkSameName(0L, slideDTO.getCourseId(), slideDTO.getName());
 
         Slide slide = new Slide()
                 .setCourse(course)
@@ -85,52 +80,53 @@ public class SlideServiceImpl implements SlideService {
                 .setName(slideDTO.getName())
                 .setState(slideDTO.getState());
 
-        MultipartFile file = slideDTO.getFile();
-        FileInfo fileInfo = getFileInfo(file);
-        slide.setPages(fileInfo.getPages());
-        slide.setObjectName("temp");
-        slide = slideDAO.save(slide);
+        if (!existsAddAuth(teacher, slide)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权创建该课件");
+        }
 
-        String objectName = getObjectName(slide.getId(), slide.getName(), fileInfo.getExtension());
-        slide.setObjectName(objectName);
-        slide = slideDAO.save(slide);
+        try {
+            MultipartFile file = slideDTO.getFile();
+            FileInfo fileInfo = getFileInfo(file);
+            slide.setPages(fileInfo.getPages());
+            slide.setObjectName("temp");
+            slide = slideDAO.save(slide);
 
-        ossUtils.upload(objectName, file.getInputStream());
+            String objectName = getObjectName(slide.getId(), slide.getName(), fileInfo.getExtension());
+            slide.setObjectName(objectName);
+            slide = slideDAO.save(slide);
 
-        return new SlideVO(slide);
+            ossUtils.upload(objectName, file.getInputStream());
+
+            return new SlideVO(slide);
+        } catch (DataIntegrityViolationException e) {
+            throw HelperException.of(ExceptionType.CONFLICT, "创建失败,请检查课件信息是否正确且不与已有课件冲突");
+        }
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public SlideVO modifySlide(LoginUser user, Long slideId, SlideDTO slideDTO) {
         Slide slide = slideDAO.findSlideById(slideId);
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权修改该课件");
 
-        checkSameName(slide.getId(), slide.getCourse().getId(), slideDTO.getName());
-
-        slide.setName(slideDTO.getName());
-        String oldObjectName = slide.getObjectName();
-        String newObjectName = getObjectName(slide.getId(), slide.getName(), Files.getFileExtension(oldObjectName));
-        slide.setObjectName(newObjectName);
-        slide = slideDAO.save(slide);
-
-        if (!oldObjectName.equals(newObjectName)) {
-            ossUtils.copy(oldObjectName, newObjectName);
-            ossUtils.delete(oldObjectName);
+        if (!existsUpdateAuth(user, slide)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该课件");
         }
 
-        return new SlideVO(slide);
-    }
-
-    private void checkSameName(Long slideId, Long courseId, String name) {
-        long exists = slideDAO.count((Specification<Slide>) (root, query, cb) -> cb.and(
-                cb.notEqual(root.get("id"), slideId),
-                cb.equal(root.get("course").get("id"), courseId),
-                cb.equal(root.get("deleteAt"), 0L),
-                cb.equal(root.get("name"), name)));
-
-        if (exists > 0) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该课件名已使用");
+        try {
+            slide.setName(slideDTO.getName());
+            String oldObjectName = slide.getObjectName();
+            String newObjectName = getObjectName(slide.getId(), slide.getName(), Files.getFileExtension(oldObjectName));
+            slide.setObjectName(newObjectName);
+            slide = slideDAO.save(slide);
+
+            if (!oldObjectName.equals(newObjectName)) {
+                ossUtils.copy(oldObjectName, newObjectName);
+                ossUtils.delete(oldObjectName);
+            }
+
+            return new SlideVO(slide);
+        } catch (DataIntegrityViolationException e) {
+            throw HelperException.of(ExceptionType.CONFLICT, "修改失败,请检查课件信息是否正确且不与已有课件冲突");
         }
     }
 
@@ -138,7 +134,10 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public void modifySlideState(LoginUser user, Long slideId, SlideStateDTO slideStateDTO) {
         Slide slide = slideDAO.findSlideById(slideId);
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权修改该课件状态");
+
+        if (!existsUpdateAuth(user, slide)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该课件状态");
+        }
 
         slide.setState(slideStateDTO.getState());
         slide = slideDAO.save(slide);
@@ -158,7 +157,10 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public void modifySlideFile(LoginUser user, Long slideId, SlideFileDTO slideFileDTO) {
         Slide slide = slideDAO.findSlideById(slideId);
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权修改该课件文件");
+
+        if (!existsUpdateAuth(user, slide)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该课件文件");
+        }
 
         MultipartFile file = slideFileDTO.getFile();
         FileInfo fileInfo = getFileInfo(file);
@@ -179,7 +181,10 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public void deleteSlide(LoginUser user, Long slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权删除该课件");
+
+        if (!existsDelAuth(user, slide)) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该课件");
+        }
 
         slide.setDeleteAt(System.currentTimeMillis());
         slideDAO.save(slide);
@@ -198,40 +203,36 @@ public class SlideServiceImpl implements SlideService {
     @Transactional(readOnly = true)
     @Override
     public Page<SlideVO> teacherGetSlides(LoginUser teacher, String key, Pageable pageable) {
-        return findByTeacherIdAndKey(teacher.getId(), key, pageable).map(SlideVO::new);
+        return slideDAO.findByTeacherAndNameContains(userDAO.findUserById(teacher.getId()), key, pageable).map(SlideVO::new);
     }
 
+    private static final Set<SlideState> STATES_NOT_GET_BY_STUDENT_SET = ImmutableSet.of(SlideState.DRAFT);
+
     @Transactional(readOnly = true)
     @Override
     public Page<SlideVO> getSlidesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
-        if (!courseService.existsCourseResourcesAuth(user, courseId)) {
+        Course course = courseDAO.findCourseById(courseId);
+
+        if (!courseService.existsGetAuth(user, course)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课程的课件");
         }
+
         switch (user.getType()) {
             case TEACHER:
-                return teacherGetSlidesByCourse(courseId, key, pageable);
+                return slideDAO.findByCourseAndNameContains(course, key, pageable).map(SlideVO::new);
             case STUDENT:
-                return studentGetSlidesByCourse(courseId, key, pageable);
+                return slideDAO.findByCourseAndStateNotInAndNameContains(course, STATES_NOT_GET_BY_STUDENT_SET, key, pageable).map(SlideVO::new);
             default:
                 return Page.empty();
         }
     }
 
-    private Page<SlideVO> teacherGetSlidesByCourse(Long courseId, String key, Pageable pageable) {
-        return findByCourseIdAndKey(courseId, key, pageable).map(SlideVO::new);
-    }
-
-    private static final Set<SlideState> STATES_NOT_GET_BY_STUDENT_SET = ImmutableSet.of(SlideState.DRAFT);
-
-    private Page<SlideVO> studentGetSlidesByCourse(Long courseId, String key, Pageable pageable) {
-        return findByCourseIdAndKeyAndStateNotIn(courseId, STATES_NOT_GET_BY_STUDENT_SET, key, pageable).map(SlideVO::new);
-    }
-
     @Transactional(readOnly = true)
     @Override
     public String getSlideUrl(LoginUser user, Long slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        if (!courseService.existsCourseResourcesAuth(user, slide.getCourse().getId())) {
+
+        if (!existsGetAuth(user, slide)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权获取该课件的链接");
         }
 
@@ -242,43 +243,31 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public SlideVO getOneSlide(LoginUser user, Long slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        if (!courseService.existsCourseResourcesAuth(user, slide.getCourse().getId())) {
+
+        if (!existsGetAuth(user, slide)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课件");
         }
+
         return new SlideVO(slide);
     }
 
-    private Page<Slide> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable) {
-        return slideDAO.findAll(
-                (Specification<Slide>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("teacher").get("id"), teacherId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                        , cb.equal(root.get("course").get("deleteAt"), 0L)
-                ),
-                pageable);
+    @Override
+    public boolean existsAddAuth(LoginUser user, Slide slide) {
+        return slide.getCourse().getTeacher().getId().equals(user.getId());
     }
 
-    private Page<Slide> findByCourseIdAndKey(Long courseId, String key, Pageable pageable) {
-        return slideDAO.findAll(
-                (Specification<Slide>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("course").get("id"), courseId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                        , cb.equal(root.get("course").get("deleteAt"), 0L)
-                ),
-                pageable);
+    @Override
+    public boolean existsDelAuth(LoginUser user, Slide slide) {
+        return slide.getTeacher().getId().equals(user.getId());
     }
 
-    private Page<Slide> findByCourseIdAndKeyAndStateNotIn(Long courseId, Set<SlideState> exclusiveStates, String key, Pageable pageable) {
-        return slideDAO.findAll(
-                (Specification<Slide>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("course").get("id"), courseId)
-                        , cb.equal(root.get("deleteAt"), 0L)
-                        , cb.like(root.get("name"), StringUtils.keyPattern(key))
-                        , cb.not(root.get("state").in(exclusiveStates))
-                        , cb.equal(root.get("course").get("deleteAt"), 0L)
-                ),
-                pageable);
+    @Override
+    public boolean existsUpdateAuth(LoginUser user, Slide slide) {
+        return slide.getTeacher().getId().equals(user.getId());
+    }
+
+    @Override
+    public boolean existsGetAuth(LoginUser user, Slide slide) {
+        return courseService.existsGetAuth(user, courseDAO.findCourseById(slide.getCourse().getId()));
     }
 }

+ 0 - 2
src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java

@@ -10,7 +10,6 @@ import nju.seec.helper.enums.UserState;
 import nju.seec.helper.enums.UserType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.UserService;
-import nju.seec.helper.service.util.AuthUtils;
 import nju.seec.helper.util.Consts;
 import nju.seec.helper.util.EncryptUtils;
 import nju.seec.helper.util.RedisCacheUtils;
@@ -69,7 +68,6 @@ public class UserServiceImpl implements UserService {
     @Override
     public UserVO modifyUser(LoginUser loginUser, UserDTO userDTO) {
         User user = userDAO.findUserById(loginUser.getId());
-        AuthUtils.checkDataAuth(loginUser.getId(), user.getId(), "您无权修改该用户的个人信息");
         user.setName(userDTO.getName());
         user = userDAO.save(user);
         return new UserVO(user);

+ 0 - 18
src/main/java/nju/seec/helper/service/util/AuthUtils.java

@@ -1,18 +0,0 @@
-package nju.seec.helper.service.util;
-
-import lombok.NonNull;
-import lombok.experimental.UtilityClass;
-import nju.seec.helper.enums.ExceptionType;
-import nju.seec.helper.exception.HelperException;
-
-/**
- * @author cst
- */
-@UtilityClass
-public class AuthUtils {
-    public void checkDataAuth(@NonNull Long userId, @NonNull Long dataUserId, String errMsg) {
-        if (!userId.equals(dataUserId)) {
-            throw HelperException.of(ExceptionType.FORBIDDEN, errMsg);
-        }
-    }
-}

+ 13 - 0
src/main/java/nju/seec/helper/util/StringUtils.java

@@ -0,0 +1,13 @@
+package nju.seec.helper.util;
+
+import lombok.experimental.UtilityClass;
+
+/**
+ * @author cst
+ */
+@UtilityClass
+public class StringUtils {
+    public String keyPattern(String key) {
+        return '%' + key + '%';
+    }
+}

+ 3 - 0
src/main/java/nju/seec/helper/vo/CommentVO.java

@@ -34,6 +34,8 @@ public class CommentVO {
 
     private ReplyVO reply;
 
+    private Boolean show;
+
     public CommentVO(@NonNull Comment comment) {
         this.id = comment.getId();
         this.slideId = comment.getSlideId();
@@ -46,5 +48,6 @@ public class CommentVO {
         this.userType = comment.getUser().getType();
         this.createAt = comment.getCreateAt();
         this.reply = comment.getReply() == null ? null : new ReplyVO(comment.getReply());
+        this.show = comment.getShow();
     }
 }

+ 1 - 3
src/main/java/nju/seec/helper/vo/question/BaseQuestionVO.java

@@ -2,9 +2,7 @@ package nju.seec.helper.vo.question;
 
 import lombok.Data;
 import lombok.NonNull;
-import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.enums.QuestionType;
-import nju.seec.helper.exception.HelperException;
 
 import java.util.Date;
 import java.util.Set;
@@ -32,7 +30,7 @@ public abstract class BaseQuestionVO {
             case "true_false":
                 return new TrueOrFalseQuestionVO(bokQuestion, withAnswer);
             default:
-                throw HelperException.of(ExceptionType.ERROR, "无法解析该题目");
+                return null;
         }
     }