ソースを参照

refactor: 基于jpa表连接方式为cross join,使用Specification重构,改为left outer join

ChenSiTong 6 年 前
コミット
97ce3da1ba
28 ファイル変更410 行追加457 行削除
  1. 6 0
      src/main/java/nju/seec/helper/controller/ControllerAdvice.java
  2. 0 1
      src/main/java/nju/seec/helper/controller/CourseController.java
  3. 3 5
      src/main/java/nju/seec/helper/dao/ChooseDAO.java
  4. 2 66
      src/main/java/nju/seec/helper/dao/CommentDAO.java
  5. 2 77
      src/main/java/nju/seec/helper/dao/CourseDAO.java
  6. 1 6
      src/main/java/nju/seec/helper/dao/MessageDAO.java
  7. 2 17
      src/main/java/nju/seec/helper/dao/NoticeDAO.java
  8. 2 97
      src/main/java/nju/seec/helper/dao/SlideDAO.java
  9. 12 16
      src/main/java/nju/seec/helper/entity/Comment.java
  10. 3 8
      src/main/java/nju/seec/helper/entity/Course.java
  11. 35 0
      src/main/java/nju/seec/helper/entity/CourseFile.java
  12. 0 1
      src/main/java/nju/seec/helper/entity/Notice.java
  13. 4 3
      src/main/java/nju/seec/helper/entity/Reply.java
  14. 6 10
      src/main/java/nju/seec/helper/entity/Slide.java
  15. 49 20
      src/main/java/nju/seec/helper/service/impl/CommentServiceImpl.java
  16. 54 26
      src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java
  17. 1 1
      src/main/java/nju/seec/helper/service/impl/MessageServiceImpl.java
  18. 14 2
      src/main/java/nju/seec/helper/service/impl/NoticeServiceImpl.java
  19. 11 7
      src/main/java/nju/seec/helper/service/impl/ReplyServiceImpl.java
  20. 58 19
      src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java
  21. 1 5
      src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java
  22. 0 34
      src/main/java/nju/seec/helper/service/util/RefreshUtils.java
  23. 13 0
      src/main/java/nju/seec/helper/service/util/StringUtils.java
  24. 3 3
      src/main/java/nju/seec/helper/vo/CommentVO.java
  25. 2 2
      src/main/java/nju/seec/helper/vo/CourseVO.java
  26. 4 4
      src/main/java/nju/seec/helper/vo/SlideVO.java
  27. 4 4
      src/main/resources/application-dev.yml
  28. 118 23
      src/main/resources/update.sql

+ 6 - 0
src/main/java/nju/seec/helper/controller/ControllerAdvice.java

@@ -9,6 +9,7 @@ import org.springframework.validation.BindException;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
 
 import javax.validation.ConstraintViolationException;
 import java.util.Objects;
@@ -24,6 +25,11 @@ public class ControllerAdvice {
         return ResponseEntity.badRequest().body(ErrorResponse.of(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()));
     }
 
+    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
+    public ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
+        return ResponseEntity.badRequest().body(ErrorResponse.of("路径参数错误"));
+    }
+
     @ExceptionHandler(ConstraintViolationException.class)
     public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException e) {
         return ResponseEntity.badRequest().body(ErrorResponse.of(e.getLocalizedMessage()));

+ 0 - 1
src/main/java/nju/seec/helper/controller/CourseController.java

@@ -123,7 +123,6 @@ public class CourseController {
     /**
      * 获取某课程
      */
-    @Auth(roles = {UserType.STUDENT, UserType.TEACHER}, message = "获取课程")
     @GetMapping("/{courseId}")
     public CourseVO getOneCourse(@PathVariable Long courseId) {
         return courseService.getOneCourse(courseId);

+ 3 - 5
src/main/java/nju/seec/helper/dao/ChooseDAO.java

@@ -5,7 +5,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 
-import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -23,13 +22,12 @@ public interface ChooseDAO extends JpaRepository<Choose, Long> {
     boolean existsByStudentIdAndCourseId(Long studentId, Long courseId);
 
     /**
-     * 查询选课信息
+     * 删除选课信息
      *
      * @param studentId
      * @param courseId
-     * @return
      */
-    Optional<Choose> findByStudentIdAndCourseId(Long studentId, Long courseId);
+    void deleteByStudentIdAndCourseId(Long studentId, Long courseId);
 
     /**
      * 查询学生选课信息
@@ -37,7 +35,7 @@ public interface ChooseDAO extends JpaRepository<Choose, Long> {
      * @param studentId
      * @return
      */
-    @Query("select distinct choose.courseId from Choose choose where choose.studentId=?1")
+    @Query("select distinct c.courseId from Choose c where c.studentId=?1")
     Set<Long> findCourseIdsByStudentId(Long studentId);
 
     /**

+ 2 - 66
src/main/java/nju/seec/helper/dao/CommentDAO.java

@@ -2,21 +2,16 @@ package nju.seec.helper.dao;
 
 import nju.seec.helper.entity.Comment;
 import nju.seec.helper.util.enums.ExceptionType;
-import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.util.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.Modifying;
-import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
 
 /**
  * @author cst
  */
 @Repository
-public interface CommentDAO extends JpaRepository<Comment, Long> {
+public interface CommentDAO extends JpaRepository<Comment, Long>, JpaSpecificationExecutor<Comment> {
     /**
      * 封装findById
      *
@@ -27,63 +22,4 @@ public interface CommentDAO extends JpaRepository<Comment, Long> {
         return this.findById(id)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到评论"));
     }
-
-    /**
-     * 查询置顶评论数量
-     *
-     * @param slideId
-     * @param pageNumber
-     * @return
-     */
-    @Query("select count(distinct c.topNumber) from Comment c " +
-            "where c.slideId=?1 " +
-            "and c.pageNumber=?2 " +
-            "and c.topNumber is not null")
-    int countTopComments(Long slideId, Integer pageNumber);
-
-    /**
-     * 获取评论
-     *
-     * @param slideId
-     * @param pageNumber
-     * @param pageable
-     * @return
-     */
-    @Query("select c from Comment c " +
-            "where c.slideId=?1 " +
-            "and c.pageNumber=?2 " +
-            "order by nullif(c.topNumber, 0) desc")
-    Page<Comment> findBySlideIdAndPageNumber(Long slideId, Integer pageNumber, Pageable pageable);
-
-    /**
-     * 置顶减一
-     *
-     * @param slideId
-     * @param pageNumber
-     * @param topNumber
-     */
-    @Transactional(rollbackFor = Exception.class)
-    @Modifying
-    @Query("update Comment c " +
-            "set c.topNumber=c.topNumber-1 " +
-            "where c.slideId=?1 " +
-            "and c.pageNumber=?2 " +
-            "and c.topNumber is not null " +
-            "and c.topNumber>?3")
-    void decTopNumber(Long slideId, Integer pageNumber, Integer topNumber);
-
-    /**
-     * 更新所有人信息
-     *
-     * @param userId
-     * @param userName
-     * @param userType
-     */
-    @Transactional(rollbackFor = Exception.class)
-    @Modifying
-    @Query("update Comment c " +
-            "set c.userName=?2, " +
-            "c.userType=?3 " +
-            "where c.userId=?1 ")
-    void updateUser(Long userId, String userName, UserType userType);
 }

+ 2 - 77
src/main/java/nju/seec/helper/dao/CourseDAO.java

@@ -3,21 +3,15 @@ package nju.seec.helper.dao;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.util.enums.ExceptionType;
 import nju.seec.helper.util.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.Modifying;
-import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Set;
 
 /**
  * @author cst
  */
 @Repository
-public interface CourseDAO extends JpaRepository<Course, Long> {
+public interface CourseDAO extends JpaRepository<Course, Long>, JpaSpecificationExecutor<Course> {
     /**
      * 封装findById
      *
@@ -29,73 +23,4 @@ public interface CourseDAO extends JpaRepository<Course, Long> {
                 .filter(course -> course.getDeleteAt() == 0)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到课程"));
     }
-
-    /**
-     * 检查老师是否已创建同名课程
-     *
-     * @param teacherId
-     * @param name
-     * @param courseId
-     * @return
-     */
-    @Query("select case when count(c.id)>0 then true else false end " +
-            "from Course c " +
-            "where c.id<>?1 " +
-            "and c.teacherId=?2 " +
-            "and c.name=?3 " +
-            "and c.deleteAt=0 ")
-    boolean existsByTeacherIdAndName(Long courseId, Long teacherId, String name);
-
-    /**
-     * 查询老师创建的课程
-     *
-     * @param teacherId
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select c from Course c " +
-            "where c.teacherId=?1 " +
-            "and c.name like concat('%',?2,'%') " +
-            "and c.deleteAt=0 ")
-    Page<Course> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable);
-
-    /**
-     * 查询课程
-     *
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select c from Course c " +
-            "where (c.name like concat('%',?1,'%') or c.teacherName like concat('%',?1,'%')) " +
-            "and c.deleteAt=0 ")
-    Page<Course> findByKey(String key, Pageable pageable);
-
-    /**
-     * 按ID列表查询
-     *
-     * @param courseIds
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select c from Course c " +
-            "where c.id in ?1 " +
-            "and (c.name like concat('%',?2,'%') or c.teacherName like concat('%',?2,'%')) " +
-            "and c.deleteAt=0 ")
-    Page<Course> findByIdsAndKey(Set<Long> courseIds, String key, Pageable pageable);
-
-    /**
-     * 更新所有人信息
-     *
-     * @param userId
-     * @param userName
-     */
-    @Transactional(rollbackFor = Exception.class)
-    @Modifying
-    @Query("update Course c " +
-            "set c.teacherName=?2 " +
-            "where c.teacherId=?1 ")
-    void updateUser(Long userId, String userName);
 }

+ 1 - 6
src/main/java/nju/seec/helper/dao/MessageDAO.java

@@ -36,12 +36,7 @@ public interface MessageDAO extends JpaRepository<Message, Long> {
      * @param toUserId
      * @param messageIds
      */
-    @Transactional(rollbackFor = Exception.class)
-    @Modifying
-    @Query("delete from Message m " +
-            "where m.toUserId=?1 " +
-            "and m.id in ?2 ")
-    void delete(Long toUserId, Set<Long> messageIds);
+    void deleteByToUserIdAndIdIn(Long toUserId, Set<Long> messageIds);
 
     /**
      * 获取消息

+ 2 - 17
src/main/java/nju/seec/helper/dao/NoticeDAO.java

@@ -3,17 +3,15 @@ package nju.seec.helper.dao;
 import nju.seec.helper.entity.Notice;
 import nju.seec.helper.util.enums.ExceptionType;
 import nju.seec.helper.util.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.Query;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
 
 /**
  * @author cst
  */
 @Repository
-public interface NoticeDAO extends JpaRepository<Notice, Long> {
+public interface NoticeDAO extends JpaRepository<Notice, Long>, JpaSpecificationExecutor<Notice> {
     /**
      * 封装findById
      *
@@ -24,17 +22,4 @@ public interface NoticeDAO extends JpaRepository<Notice, Long> {
         return this.findById(id)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到公告"));
     }
-
-    /**
-     * 根据课程ID查找
-     *
-     * @param courseId
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select n from Notice n " +
-            "where n.courseId=?1 " +
-            "and (n.title like concat('%',?2,'%') or n.content like concat('%',?2,'%') )")
-    Page<Notice> findByCourseIdAndKey(Long courseId, String key, Pageable pageable);
 }

+ 2 - 97
src/main/java/nju/seec/helper/dao/SlideDAO.java

@@ -2,23 +2,16 @@ package nju.seec.helper.dao;
 
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.util.enums.ExceptionType;
-import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.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.Modifying;
-import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Set;
 
 /**
  * @author cst
  */
 @Repository
-public interface SlideDAO extends JpaRepository<Slide, Long> {
+public interface SlideDAO extends JpaRepository<Slide, Long>, JpaSpecificationExecutor<Slide> {
     /**
      * 封装findById
      *
@@ -30,92 +23,4 @@ public interface SlideDAO extends JpaRepository<Slide, Long> {
                 .filter(slide -> slide.getDeleteAt() == 0)
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到课件"));
     }
-
-    /**
-     * 查询教师创建的课件
-     *
-     * @param teacherId
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select s from Slide s " +
-            "where s.teacherId=?1 " +
-            "and s.name like concat('%',?2,'%') " +
-            "and s.deleteAt=0")
-    Page<Slide> findByTeacherIdAndNameContains(Long teacherId, String key, Pageable pageable);
-
-    /**
-     * 查询教师创建的该课程的课件
-     *
-     * @param courseId
-     * @param teacherId
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select s from Slide s " +
-            "where s.courseId=?1 " +
-            "and s.teacherId=?2 " +
-            "and s.name like concat('%',?3,'%') " +
-            "and s.deleteAt=0")
-    Page<Slide> findByCourseIdAndTeacherIdAndNameContains(Long courseId, Long teacherId, String key, Pageable pageable);
-
-    /**
-     * 根据课程和课件状态查询
-     *
-     * @param courseId
-     * @param exclusiveStates
-     * @param key
-     * @param pageable
-     * @return
-     */
-    @Query("select s from Slide s " +
-            "where s.courseId=?1 " +
-            "and s.state not in ?2 " +
-            "and s.name like concat('%',?3,'%') " +
-            "and s.deleteAt=0")
-    Page<Slide> findByCourseIdAndStateNotInAndNameContains(Long courseId, Set<SlideState> exclusiveStates, String key, Pageable pageable);
-
-    /**
-     * 更新所有人信息
-     *
-     * @param userId
-     * @param userName
-     */
-    @Transactional(rollbackFor = Exception.class)
-    @Modifying
-    @Query("update Slide s " +
-            "set s.teacherName=?2 " +
-            "where s.teacherId=?1 ")
-    void updateUser(Long userId, String userName);
-
-    /**
-     * 更新课程信息
-     *
-     * @param courseId
-     * @param courseName
-     */
-    @Transactional(rollbackFor = Exception.class)
-    @Modifying
-    @Query("update Slide s " +
-            "set s.courseName=?2 " +
-            "where s.courseId=?1 ")
-    void updateCourse(Long courseId, String courseName);
-
-    /**
-     * 检查同名课件
-     *
-     * @param slideId
-     * @param courseId
-     * @param name
-     * @return
-     */
-    @Query("select case when count(s.id)>0 then true else false end " +
-            "from Slide s " +
-            "where s.id<>?1 " +
-            "and s.courseId=?2 " +
-            "and s.name=?3 " +
-            "and s.deleteAt=0 ")
-    boolean existsByCourseIdAndName(Long slideId, Long courseId, String name);
 }

+ 12 - 16
src/main/java/nju/seec/helper/entity/Comment.java

@@ -2,7 +2,6 @@ package nju.seec.helper.entity;
 
 import lombok.Data;
 import lombok.experimental.Accessors;
-import nju.seec.helper.util.enums.UserType;
 import org.hibernate.annotations.CreationTimestamp;
 import org.hibernate.annotations.DynamicInsert;
 import org.hibernate.annotations.DynamicUpdate;
@@ -16,7 +15,10 @@ import java.time.LocalDateTime;
 @Data
 @Accessors(chain = true)
 @Entity
-@Table(name = "comment")
+@Table(name = "comment",
+        indexes = {@Index(name = "slide_page_top_index", columnList = "slide_id"),
+                @Index(name = "slide_page_top_index", columnList = "page_number"),
+                @Index(name = "slide_page_top_index", columnList = "top_number")})
 @DynamicUpdate
 @DynamicInsert
 public class Comment {
@@ -30,32 +32,26 @@ public class Comment {
     @Column(name = "page_number", nullable = false)
     private Integer pageNumber;
 
-    @Column(name = "`show`", nullable = false, columnDefinition = "bit(1) default true")
+    @Column(name = "`show`", nullable = false, columnDefinition = "bit(1) default true not null")
     private Boolean show = true;
 
-    @Column(name = "top_number")
-    private Integer topNumber;
+    @Column(name = "top_number", nullable = false, columnDefinition = "int default 0 not null")
+    private Integer topNumber = 0;
 
     @Column(nullable = false)
     private String title;
 
-    @Column(nullable = false)
+    @Column(nullable = false, columnDefinition = "text")
     private String content;
 
-    @Column(name = "user_id", nullable = false)
-    private Long userId;
-
-    @Column(name = "user_name", nullable = false)
-    private String userName;
-
-    @Enumerated(EnumType.STRING)
-    @Column(name = "user_type", nullable = false)
-    private UserType userType;
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id", nullable = false)
+    private User user;
 
     @Column(name = "create_at", updatable = false, nullable = false)
     @CreationTimestamp
     private LocalDateTime createAt;
 
-    @OneToOne(mappedBy = "comment", cascade = CascadeType.ALL)
+    @OneToOne(mappedBy = "comment", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
     private Reply reply;
 }

+ 3 - 8
src/main/java/nju/seec/helper/entity/Course.java

@@ -37,14 +37,9 @@ public class Course {
     @CreationTimestamp
     private LocalDateTime createAt;
 
-    @Column(name = "teacher_id", nullable = false)
-    private Long teacherId;
-
-    /**
-     * 冗余信息,有利于课程搜索和信息展示
-     */
-    @Column(name = "teacher_name", nullable = false)
-    private String teacherName;
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "teacher_id", nullable = false)
+    private User teacher;
 
     @Column(name = "delete_at", columnDefinition = "bigint default 0 not null")
     private Long deleteAt = 0L;

+ 35 - 0
src/main/java/nju/seec/helper/entity/CourseFile.java

@@ -0,0 +1,35 @@
+package nju.seec.helper.entity;
+
+import lombok.Data;
+import org.hibernate.annotations.CreationTimestamp;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+/**
+ * @author cst
+ */
+@Data
+@Entity
+@Table(name = "course_file", uniqueConstraints = {@UniqueConstraint(columnNames = {"course_id", "file_name"})})
+public class CourseFile {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(name = "course_id", nullable = false)
+    private Long courseId;
+
+    @Column(name = "teacher_id", nullable = false)
+    private Long teacherId;
+
+    @Column(name = "object_name", nullable = false)
+    private String objectName;
+
+    @Column(name = "file_name", nullable = false)
+    private String fileName;
+
+    @Column(name = "create_at", nullable = false, updatable = false)
+    @CreationTimestamp
+    private LocalDateTime creatAt;
+}

+ 0 - 1
src/main/java/nju/seec/helper/entity/Notice.java

@@ -29,7 +29,6 @@ public class Notice {
     @Column(nullable = false)
     private String title;
 
-    @Lob
     @Column(columnDefinition = "text", nullable = false)
     private String content;
 

+ 4 - 3
src/main/java/nju/seec/helper/entity/Reply.java

@@ -23,12 +23,13 @@ public class Reply {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @OneToOne
+    @OneToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "comment_id", nullable = false)
     private Comment comment;
 
-    @Column(name = "teacher_id", nullable = false)
-    private Long teacherId;
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "teacher_id", nullable = false)
+    private User teacher;
 
     @Column(nullable = false)
     private String content;

+ 6 - 10
src/main/java/nju/seec/helper/entity/Slide.java

@@ -26,17 +26,13 @@ public class Slide {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @Column(name = "course_id", nullable = false)
-    private Long courseId;
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "course_id", nullable = false)
+    private Course course;
 
-    @Column(name = "course_name", nullable = false)
-    private String courseName;
-
-    @Column(name = "teacher_id", nullable = false)
-    private Long teacherId;
-
-    @Column(name = "teacher_name", nullable = false)
-    private String teacherName;
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "teacher_id", nullable = false)
+    private User teacher;
 
     @Enumerated(EnumType.STRING)
     @Column(nullable = false)

+ 49 - 20
src/main/java/nju/seec/helper/service/impl/CommentServiceImpl.java

@@ -2,6 +2,7 @@ package nju.seec.helper.service.impl;
 
 import nju.seec.helper.dao.CommentDAO;
 import nju.seec.helper.dao.SlideDAO;
+import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.dto.CommentDTO;
 import nju.seec.helper.dto.LoginUser;
 import nju.seec.helper.entity.Comment;
@@ -13,10 +14,12 @@ import nju.seec.helper.util.enums.MessageType;
 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;
 
 import java.util.Collections;
+import java.util.stream.Collectors;
 
 /**
  * @author cst
@@ -25,12 +28,14 @@ import java.util.Collections;
 public class CommentServiceImpl implements CommentService {
     private final CommentDAO commentDAO;
     private final SlideDAO slideDAO;
+    private final UserDAO userDAO;
 
     private final MessageService messageService;
 
-    public CommentServiceImpl(CommentDAO commentDAO, SlideDAO slideDAO, MessageService messageService) {
+    public CommentServiceImpl(CommentDAO commentDAO, SlideDAO slideDAO, UserDAO userDAO, MessageService messageService) {
         this.commentDAO = commentDAO;
         this.slideDAO = slideDAO;
+        this.userDAO = userDAO;
         this.messageService = messageService;
     }
 
@@ -40,17 +45,15 @@ public class CommentServiceImpl implements CommentService {
         Comment comment = new Comment()
                 .setSlideId(commentDTO.getSlideId())
                 .setPageNumber(commentDTO.getPageNumber())
-                .setUserId(user.getId())
-                .setUserName(user.getName())
-                .setUserType(user.getType())
+                .setUser(userDAO.findUserById(user.getId()))
                 .setTitle(commentDTO.getTitle())
                 .setContent(commentDTO.getContent());
         comment = commentDAO.save(comment);
 
         // 增加通知
         Slide slide = slideDAO.findSlideById(comment.getSlideId());
-        if (!user.getId().equals(slide.getTeacherId())) {
-            messageService.createMessage(Collections.singleton(slide.getTeacherId()), MessageType.COMMENT_NEW, String.format("课程 [%s] 的课件 [%s] 在第%d页有新评论", slide.getCourseName(), slide.getName(), comment.getPageNumber()), slide.getId());
+        if (!user.getId().equals(slide.getTeacher().getId())) {
+            messageService.createMessage(Collections.singleton(slide.getTeacher().getId()), MessageType.COMMENT_NEW, String.format("课程 [%s] 的课件 [%s] 在第%d页有新评论", slide.getCourse().getName(), slide.getName(), comment.getPageNumber()), slide.getId());
         }
 
         return new CommentVO(comment);
@@ -61,10 +64,10 @@ public class CommentServiceImpl implements CommentService {
     public void removeComment(LoginUser user, Long commentId) {
         Comment comment = commentDAO.findCommentById(commentId);
 
-        AuthUtils.checkDataAuth(user.getId(), comment.getUserId(), "您无权删除该评论");
+        AuthUtils.checkDataAuth(user.getId(), comment.getUser().getId(), "您无权删除该评论");
 
         unTopComment(comment);
-        commentDAO.delete(comment);
+        commentDAO.deleteById(commentId);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -72,10 +75,10 @@ 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()).getTeacherId(), "您无权在该评论区置顶评论");
+        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId(), "您无权在该评论区置顶评论");
 
         unTopComment(comment);
-        comment.setTopNumber(commentDAO.countTopComments(comment.getSlideId(), comment.getPageNumber()) + 1);
+        comment.setTopNumber((int) countTopComments(comment.getSlideId(), comment.getPageNumber()) + 1);
         commentDAO.save(comment);
     }
 
@@ -84,24 +87,16 @@ public class CommentServiceImpl implements CommentService {
     public void unTopComment(LoginUser user, Long commentId) {
         Comment comment = commentDAO.findCommentById(commentId);
 
-        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacherId(), "您无权在该评论区取消置顶评论");
+        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId(), "您无权在该评论区取消置顶评论");
 
         unTopComment(comment);
         commentDAO.save(comment);
     }
 
-    private void unTopComment(Comment comment) {
-        Integer topNumber = comment.getTopNumber();
-        if (topNumber != null) {
-            comment.setTopNumber(null);
-            commentDAO.decTopNumber(comment.getSlideId(), comment.getPageNumber(), topNumber);
-        }
-    }
-
     @Transactional(readOnly = true)
     @Override
     public Page<CommentVO> getCommentsBySlideAndPageNumber(Long slideId, Integer pageNumber, Pageable pageable) {
-        return commentDAO.findBySlideIdAndPageNumber(slideId, pageNumber, pageable).map(CommentVO::new);
+        return findBySlideIdAndPageNumber(slideId, pageNumber, pageable).map(CommentVO::new);
     }
 
     @Transactional(readOnly = true)
@@ -109,4 +104,38 @@ public class CommentServiceImpl implements CommentService {
     public CommentVO getOneComment(Long commentId) {
         return new CommentVO(commentDAO.findCommentById(commentId));
     }
+
+    private void unTopComment(Comment comment) {
+        Integer topNumber = comment.getTopNumber();
+        comment.setTopNumber(0);
+        decTopNumber(comment.getSlideId(), comment.getPageNumber(), topNumber);
+    }
+
+    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)));
+    }
+
+    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);
+    }
+
+    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()));
+    }
 }

+ 54 - 26
src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java

@@ -2,6 +2,7 @@ package nju.seec.helper.service.impl;
 
 import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.CourseDAO;
+import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.dto.ChooseDTO;
 import nju.seec.helper.dto.CourseDTO;
 import nju.seec.helper.dto.LoginUser;
@@ -9,30 +10,33 @@ import nju.seec.helper.entity.Choose;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.service.CourseService;
 import nju.seec.helper.service.util.AuthUtils;
-import nju.seec.helper.service.util.RefreshUtils;
+import nju.seec.helper.service.util.StringUtils;
 import nju.seec.helper.util.EncryptUtils;
 import nju.seec.helper.util.enums.ExceptionType;
 import nju.seec.helper.util.exception.HelperException;
 import nju.seec.helper.vo.CourseVO;
 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
  */
 @Service
 public class CourseServiceImpl implements CourseService {
+    private final UserDAO userDAO;
     private final CourseDAO courseDAO;
     private final ChooseDAO chooseDAO;
 
-    private final RefreshUtils refreshUtils;
-
-    public CourseServiceImpl(CourseDAO courseDAO, ChooseDAO chooseDAO, RefreshUtils refreshUtils) {
+    public CourseServiceImpl(UserDAO userDAO, CourseDAO courseDAO, ChooseDAO chooseDAO) {
+        this.userDAO = userDAO;
         this.courseDAO = courseDAO;
         this.chooseDAO = chooseDAO;
-        this.refreshUtils = refreshUtils;
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -42,8 +46,7 @@ public class CourseServiceImpl implements CourseService {
         checkSameCourseName(0L, teacher.getId(), courseDTO.getName());
 
         Course course = new Course()
-                .setTeacherId(teacher.getId())
-                .setTeacherName(teacher.getName())
+                .setTeacher(userDAO.findUserById(teacher.getId()))
                 .setName(courseDTO.getName())
                 .setBio(courseDTO.getBio())
                 .setCode(EncryptUtils.encode(courseDTO.getCode()));
@@ -57,14 +60,13 @@ public class CourseServiceImpl implements CourseService {
         Course course = courseDAO.findCourseById(courseDTO.getId());
 
         // 检查
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacherId(), "您无权修改该课程");
+        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权修改该课程");
         checkSameCourseName(course.getId(), user.getId(), courseDTO.getName());
 
         course.setName(courseDTO.getName())
                 .setBio(courseDTO.getBio())
                 .setCode(EncryptUtils.encode(courseDTO.getCode()));
         course = courseDAO.save(course);
-        refreshUtils.refreshCourse(course);
         return new CourseVO(course);
     }
 
@@ -72,7 +74,7 @@ public class CourseServiceImpl implements CourseService {
     @Override
     public void removeCourse(LoginUser user, Long courseId) {
         Course course = courseDAO.findCourseById(courseId);
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacherId(), "您无权删除该课程");
+        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权删除该课程");
 
         course.setDeleteAt(System.currentTimeMillis());
         courseDAO.save(course);
@@ -101,39 +103,32 @@ public class CourseServiceImpl implements CourseService {
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void quitCourse(LoginUser user, Long courseId) {
-        Choose choose = chooseDAO.findByStudentIdAndCourseId(user.getId(), courseId)
-                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到选课信息"));
-        chooseDAO.deleteById(choose.getId());
+        chooseDAO.deleteByStudentIdAndCourseId(user.getId(), courseId);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<CourseVO> getCourses(String key, Pageable pageable) {
-        return courseDAO.findByKey(key, pageable).map(CourseVO::new);
+        return findByKey(key, pageable).map(CourseVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<CourseVO> teacherGetCourses(LoginUser teacher, String key, Pageable pageable) {
-        return courseDAO.findByTeacherIdAndKey(teacher.getId(), key, pageable).map(CourseVO::new);
+        return findByTeacherIdAndKey(teacher.getId(), key, pageable).map(CourseVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<CourseVO> studentGetCourses(LoginUser student, String key, Pageable pageable) {
-        return courseDAO
-                .findByIdsAndKey(
-                        chooseDAO.findCourseIdsByStudentId(student.getId()),
-                        key,
-                        pageable)
-                .map(CourseVO::new);
+        return findByIdsAndKey(chooseDAO.findCourseIdsByStudentId(student.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.getTeacherId(), "您无权查看该课程选课码");
+        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权查看该课程选课码");
 
         return EncryptUtils.decode(course.getCode());
     }
@@ -144,12 +139,45 @@ public class CourseServiceImpl implements CourseService {
         return new CourseVO(courseDAO.findCourseById(courseId));
     }
 
-    /**
-     * 检查同名课程
-     */
     private void checkSameCourseName(Long courseId, Long teacherId, String name) {
-        if (courseDAO.existsByTeacherIdAndName(courseId, teacherId, 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("name"), name),
+                        cb.equal(root.get("deleteAt"), 0L)
+                ));
+        if (exists > 0) {
             throw HelperException.of(ExceptionType.CONFLICT, "该课程名已使用");
         }
     }
+
+    private Page<Course> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable) {
+        return courseDAO.findAll(
+                (Specification<Course>) (root, query, cb) -> cb.and(
+                        cb.equal(root.get("deleteAt"), 0L),
+                        cb.equal(root.get("teacher").get("id"), teacherId),
+                        cb.like(root.get("name"), StringUtils.keyPattern(key))),
+                pageable);
+    }
+
+    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);
+    }
+
+    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);
+    }
 }

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

@@ -42,7 +42,7 @@ public class MessageServiceImpl implements MessageService {
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void deleteMessages(LoginUser user, MessageDTO messageDTO) {
-        messageDAO.delete(user.getId(), messageDTO.getIds());
+        messageDAO.deleteByToUserIdAndIdIn(user.getId(), messageDTO.getIds());
     }
 
     @Transactional(rollbackFor = Exception.class)

+ 14 - 2
src/main/java/nju/seec/helper/service/impl/NoticeServiceImpl.java

@@ -10,10 +10,12 @@ import nju.seec.helper.entity.Notice;
 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.util.enums.MessageType;
 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 java.util.Set;
@@ -39,7 +41,7 @@ public class NoticeServiceImpl implements NoticeService {
     @Override
     public NoticeVO create(LoginUser user, NoticeDTO noticeDTO) {
         Course course = courseDAO.findCourseById(noticeDTO.getCourseId());
-        AuthUtils.checkDataAuth(user.getId(), course.getTeacherId(), "您无权创建该课程的公告");
+        AuthUtils.checkDataAuth(user.getId(), course.getTeacher().getId(), "您无权创建该课程的公告");
         Notice notice = new Notice()
                 .setCourseId(noticeDTO.getCourseId())
                 .setTeacherId(user.getId())
@@ -62,7 +64,7 @@ public class NoticeServiceImpl implements NoticeService {
 
     @Override
     public Page<NoticeVO> getNoticesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
-        return noticeDAO.findByCourseIdAndKey(courseId, key, pageable).map(NoticeVO::new);
+        return findByCourseIdAndKey(courseId, key, pageable).map(NoticeVO::new);
     }
 
     @Override
@@ -71,4 +73,14 @@ public class NoticeServiceImpl implements NoticeService {
         AuthUtils.checkDataAuth(user.getId(), notice.getTeacherId(), "您无权删除该公告");
         noticeDAO.deleteById(noticeId);
     }
+
+    private Page<Notice> findByCourseIdAndKey(Long courseId, String key, Pageable pageable) {
+        String keyPattern = StringUtils.keyPattern(key);
+
+        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);
+    }
 }

+ 11 - 7
src/main/java/nju/seec/helper/service/impl/ReplyServiceImpl.java

@@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j;
 import nju.seec.helper.dao.CommentDAO;
 import nju.seec.helper.dao.ReplyDAO;
 import nju.seec.helper.dao.SlideDAO;
+import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.dto.LoginUser;
 import nju.seec.helper.dto.ReplyDTO;
 import nju.seec.helper.entity.Comment;
@@ -30,13 +31,15 @@ public class ReplyServiceImpl implements ReplyService {
     private final SlideDAO slideDAO;
     private final CommentDAO commentDAO;
     private final ReplyDAO replyDAO;
+    private final UserDAO userDAO;
 
     private final MessageService messageService;
 
-    public ReplyServiceImpl(SlideDAO slideDAO, CommentDAO commentDAO, ReplyDAO replyDAO, MessageService messageService) {
+    public ReplyServiceImpl(SlideDAO slideDAO, CommentDAO commentDAO, ReplyDAO replyDAO, UserDAO userDAO, MessageService messageService) {
         this.slideDAO = slideDAO;
         this.commentDAO = commentDAO;
         this.replyDAO = replyDAO;
+        this.userDAO = userDAO;
         this.messageService = messageService;
     }
 
@@ -44,22 +47,23 @@ 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()).getTeacherId(), "您无权回复该评论");
+        AuthUtils.checkDataAuth(user.getId(), slideDAO.findSlideById(comment.getSlideId()).getTeacher().getId(), "您无权回复该评论");
 
         if (comment.getReply() != null) {
-            throw HelperException.of(ExceptionType.CONFLICT, "您已回复该评论");
+            throw HelperException.of(ExceptionType.CONFLICT, "该评论已回复");
         }
         Reply reply = new Reply()
                 .setComment(comment)
-                .setTeacherId(user.getId())
+                .setTeacher(userDAO.findUserById(user.getId()))
                 .setContent(replyDTO.getContent());
+        comment.setReply(reply);
         reply.setComment(comment);
         reply = replyDAO.save(reply);
 
         // 增加通知
-        if (!user.getId().equals(comment.getUserId())) {
+        if (!user.getId().equals(comment.getUser().getId())) {
             Slide slide = slideDAO.findSlideById(comment.getSlideId());
-            messageService.createMessage(Collections.singleton(comment.getUserId()), MessageType.COMMENT_REPLY, String.format("您在课件 [%s] 第%d页发表的讨论帖已收到回复", slide.getName(), comment.getPageNumber()), slide.getId());
+            messageService.createMessage(Collections.singleton(comment.getUser().getId()), MessageType.COMMENT_REPLY, String.format("您在课件 [%s] 第%d页发表的讨论帖已收到回复", slide.getName(), comment.getPageNumber()), slide.getId());
         }
 
         return new ReplyVO(reply);
@@ -69,7 +73,7 @@ public class ReplyServiceImpl implements ReplyService {
     @Override
     public void removeReply(LoginUser user, Long replyId) {
         Reply reply = replyDAO.findReplyById(replyId);
-        AuthUtils.checkDataAuth(user.getId(), reply.getTeacherId(), "您无权回复该评论");
+        AuthUtils.checkDataAuth(user.getId(), reply.getTeacher().getId(), "您无权删除该评论");
 
         Comment comment = reply.getComment();
         comment.setReply(null);

+ 58 - 19
src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java

@@ -6,6 +6,7 @@ import lombok.SneakyThrows;
 import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.CourseDAO;
 import nju.seec.helper.dao.SlideDAO;
+import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.dto.LoginUser;
 import nju.seec.helper.dto.SlideDTO;
 import nju.seec.helper.dto.SlideFileDTO;
@@ -15,6 +16,7 @@ import nju.seec.helper.entity.Slide;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.service.SlideService;
 import nju.seec.helper.service.util.AuthUtils;
+import nju.seec.helper.service.util.StringUtils;
 import nju.seec.helper.util.CacheUtils;
 import nju.seec.helper.util.Consts;
 import nju.seec.helper.util.FileUtils;
@@ -26,6 +28,7 @@ import nju.seec.helper.vo.SlideVO;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
@@ -46,16 +49,18 @@ public class SlideServiceImpl implements SlideService {
     private final CourseDAO courseDAO;
     private final SlideDAO slideDAO;
     private final ChooseDAO chooseDAO;
+    private final UserDAO userDAO;
 
     private final FileUtils fileUtils;
     private final CacheUtils cacheUtils;
 
     private final MessageService messageService;
 
-    public SlideServiceImpl(CourseDAO courseDAO, SlideDAO slideDAO, ChooseDAO chooseDAO, FileUtils fileUtils, CacheUtils cacheUtils, MessageService messageService) {
+    public SlideServiceImpl(CourseDAO courseDAO, SlideDAO slideDAO, ChooseDAO chooseDAO, UserDAO userDAO, FileUtils fileUtils, CacheUtils cacheUtils, MessageService messageService) {
         this.courseDAO = courseDAO;
         this.slideDAO = slideDAO;
         this.chooseDAO = chooseDAO;
+        this.userDAO = userDAO;
         this.fileUtils = fileUtils;
         this.cacheUtils = cacheUtils;
         this.messageService = messageService;
@@ -66,15 +71,13 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public SlideVO createSlide(LoginUser teacher, SlideDTO slideDTO) {
         Course course = courseDAO.findCourseById(slideDTO.getCourseId());
-        AuthUtils.checkDataAuth(teacher.getId(), course.getTeacherId(), "您无权创建该课程的课件");
+        AuthUtils.checkDataAuth(teacher.getId(), course.getTeacher().getId(), "您无权创建该课程的课件");
 
         checkSameName(0L, slideDTO.getCourseId(), slideDTO.getName());
 
         Slide slide = new Slide()
-                .setCourseId(slideDTO.getCourseId())
-                .setCourseName(course.getName())
-                .setTeacherId(teacher.getId())
-                .setTeacherName(teacher.getName())
+                .setCourse(course)
+                .setTeacher(userDAO.findUserById(teacher.getId()))
                 .setName(slideDTO.getName())
                 .setState(slideDTO.getState());
 
@@ -97,9 +100,9 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public SlideVO modifySlide(LoginUser user, SlideDTO slideDTO) {
         Slide slide = slideDAO.findSlideById(slideDTO.getId());
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该课件");
+        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权修改该课件");
 
-        checkSameName(slide.getId(), slide.getCourseId(), slideDTO.getName());
+        checkSameName(slide.getId(), slide.getCourse().getId(), slideDTO.getName());
 
         slide.setName(slideDTO.getName());
         String oldObjectName = slide.getObjectName();
@@ -119,15 +122,15 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public void modifySlideState(LoginUser user, SlideStateDTO slideStateDTO) {
         Slide slide = slideDAO.findSlideById(slideStateDTO.getId());
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该课件状态");
+        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权修改该课件状态");
 
         slide.setState(slideStateDTO.getState());
         slide = slideDAO.save(slide);
 
         // 增加通知
         if (slide.getState() == SlideState.IN_CLASS) {
-            Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourseId());
-            messageService.createMessage(studentIds, MessageType.SLIDE_IN_CLASS, String.format("课程 [%s] 的课件 [%s] 已开课", slide.getCourseName(), slide.getName()), slide.getId());
+            Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourse().getId());
+            messageService.createMessage(studentIds, MessageType.SLIDE_IN_CLASS, String.format("课程 [%s] 的课件 [%s] 已开课", slide.getCourse().getName(), slide.getName()), slide.getId());
         }
     }
 
@@ -136,7 +139,7 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public void modifySlideFile(LoginUser user, SlideFileDTO slideFileDTO) {
         Slide slide = slideDAO.findSlideById(slideFileDTO.getId());
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权修改该课件文件");
+        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权修改该课件文件");
 
         MultipartFile file = slideFileDTO.getFile();
         FileInfo fileInfo = getFileInfo(file);
@@ -157,7 +160,7 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public void removeSlide(LoginUser user, Long slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        AuthUtils.checkDataAuth(user.getId(), slide.getTeacherId(), "您无权删除该课件");
+        AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权删除该课件");
 
         slide.setDeleteAt(System.currentTimeMillis());
         slideDAO.save(slide);
@@ -189,13 +192,13 @@ public class SlideServiceImpl implements SlideService {
     @Transactional(readOnly = true)
     @Override
     public Page<SlideVO> teacherGetSlides(LoginUser teacher, String key, Pageable pageable) {
-        return slideDAO.findByTeacherIdAndNameContains(teacher.getId(), key, pageable).map(SlideVO::new);
+        return findByTeacherIdAndKey(teacher.getId(), key, pageable).map(SlideVO::new);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<SlideVO> teacherGetSlidesByCourse(LoginUser teacher, Long courseId, String key, Pageable pageable) {
-        return slideDAO.findByCourseIdAndTeacherIdAndNameContains(courseId, teacher.getId(), key, pageable).map(SlideVO::new);
+        return findByCourseIdAndTeacherIdAndKey(courseId, teacher.getId(), key, pageable).map(SlideVO::new);
     }
 
     @Transactional(readOnly = true)
@@ -205,7 +208,7 @@ public class SlideServiceImpl implements SlideService {
             throw HelperException.of(ExceptionType.FORBIDDEN, "你无权查看该课程下的课件");
         }
 
-        return slideDAO.findByCourseIdAndStateNotInAndNameContains(courseId, ImmutableSet.of(SlideState.DRAFT), key, pageable).map(SlideVO::new);
+        return findByCourseIdAndKeyAndStateNotIn(courseId, ImmutableSet.of(SlideState.DRAFT), key, pageable).map(SlideVO::new);
     }
 
     @Transactional(readOnly = true)
@@ -235,22 +238,58 @@ public class SlideServiceImpl implements SlideService {
      * 检查同名课件
      */
     private void checkSameName(Long slideId, Long courseId, String name) {
-        if (slideDAO.existsByCourseIdAndName(slideId, courseId, name)) {
+        long exists = slideDAO.count((Specification<Slide>) (root, query, cb) -> cb.and(
+                cb.notEqual(root.get("id"), slideId),
+                cb.equal(root.get("deleteAt"), 0L),
+                cb.equal(root.get("courseId"), courseId),
+                cb.equal(root.get("name"), name)));
+
+        if (exists > 0) {
             throw HelperException.of(ExceptionType.CONFLICT, "该课件名已使用");
         }
     }
 
+    private Page<Slide> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable) {
+        return slideDAO.findAll(
+                (Specification<Slide>) (root, query, cb) -> cb.and(
+                        cb.equal(root.get("deleteAt"), 0L),
+                        cb.equal(root.get("teacher").get("id"), teacherId),
+                        cb.like(root.get("name"), StringUtils.keyPattern(key))),
+                pageable);
+    }
+
+    private Page<Slide> findByCourseIdAndTeacherIdAndKey(Long courseId, Long teacherId, String key, Pageable pageable) {
+        return slideDAO.findAll(
+                (Specification<Slide>) (root, query, cb) -> cb.and(
+                        cb.equal(root.get("deleteAt"), 0L),
+                        cb.equal(root.get("courseId"), courseId),
+                        cb.equal(root.get("teacher").get("id"), teacherId),
+                        cb.like(root.get("name"), StringUtils.keyPattern(key))),
+                pageable);
+    }
+
+    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("deleteAt"), 0L),
+                        cb.equal(root.get("courseId"), courseId),
+                        cb.not(root.get("state").in(exclusiveStates)),
+                        cb.like(root.get("name"), StringUtils.keyPattern(key))
+                ),
+                pageable);
+    }
+
     private void checkDataAccessAuth(LoginUser user, Slide slide) {
         HelperException forbiddenEx = HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课件");
 
         switch (user.getType()) {
             case TEACHER:
-                if (!slide.getTeacherId().equals(user.getId())) {
+                if (!slide.getTeacher().getId().equals(user.getId())) {
                     throw forbiddenEx;
                 }
                 break;
             case STUDENT:
-                if (!chooseDAO.existsByStudentIdAndCourseId(user.getId(), slide.getCourseId())
+                if (!chooseDAO.existsByStudentIdAndCourseId(user.getId(), slide.getCourse().getId())
                         || slide.getState() == SlideState.DRAFT) {
                     throw forbiddenEx;
                 }

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

@@ -8,7 +8,6 @@ import nju.seec.helper.dto.UserDTO;
 import nju.seec.helper.entity.User;
 import nju.seec.helper.service.UserService;
 import nju.seec.helper.service.util.AuthUtils;
-import nju.seec.helper.service.util.RefreshUtils;
 import nju.seec.helper.util.CacheUtils;
 import nju.seec.helper.util.Consts;
 import nju.seec.helper.util.EncryptUtils;
@@ -27,7 +26,6 @@ import java.util.Map;
 @Service
 public class UserServiceImpl implements UserService {
     private final CacheUtils cacheUtils;
-    private final RefreshUtils refreshUtils;
     private final UserDAO userDAO;
     private final static Map<String, UserType> EMAIL_SUFFIX_AND_USER_TYPE_MAP =
             ImmutableMap.of(
@@ -35,10 +33,9 @@ public class UserServiceImpl implements UserService {
                     "nju.edu.cn", UserType.TEACHER
             );
 
-    public UserServiceImpl(UserDAO userDAO, CacheUtils cacheUtils, RefreshUtils refreshUtils) {
+    public UserServiceImpl(UserDAO userDAO, CacheUtils cacheUtils) {
         this.userDAO = userDAO;
         this.cacheUtils = cacheUtils;
-        this.refreshUtils = refreshUtils;
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -81,7 +78,6 @@ public class UserServiceImpl implements UserService {
         AuthUtils.checkDataAuth(loginUser.getId(), user.getId(), "您无权修改该用户的个人信息");
         user.setName(userDTO.getName());
         user = userDAO.save(user);
-        refreshUtils.refreshUser(user);
         return new UserVO(user);
     }
 

+ 0 - 34
src/main/java/nju/seec/helper/service/util/RefreshUtils.java

@@ -1,34 +0,0 @@
-package nju.seec.helper.service.util;
-
-import nju.seec.helper.dao.CommentDAO;
-import nju.seec.helper.dao.CourseDAO;
-import nju.seec.helper.dao.SlideDAO;
-import nju.seec.helper.entity.Course;
-import nju.seec.helper.entity.User;
-import org.springframework.stereotype.Component;
-
-/**
- * @author cst
- */
-@Component
-public class RefreshUtils {
-    private final CourseDAO courseDAO;
-    private final SlideDAO slideDAO;
-    private final CommentDAO commentDAO;
-
-    public RefreshUtils(CourseDAO courseDAO, SlideDAO slideDAO, CommentDAO commentDAO) {
-        this.courseDAO = courseDAO;
-        this.slideDAO = slideDAO;
-        this.commentDAO = commentDAO;
-    }
-
-    public void refreshUser(User user) {
-        courseDAO.updateUser(user.getId(), user.getName());
-        slideDAO.updateUser(user.getId(), user.getName());
-        commentDAO.updateUser(user.getId(), user.getName(), user.getType());
-    }
-
-    public void refreshCourse(Course course) {
-        slideDAO.updateCourse(course.getId(), course.getName());
-    }
-}

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

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

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

@@ -41,9 +41,9 @@ public class CommentVO {
         this.topNumber = comment.getTopNumber();
         this.title = comment.getTitle();
         this.content = comment.getContent();
-        this.userId = comment.getUserId();
-        this.userName = comment.getUserName();
-        this.userType = comment.getUserType();
+        this.userId = comment.getUser().getId();
+        this.userName = comment.getUser().getName();
+        this.userType = comment.getUser().getType();
         this.createAt = comment.getCreateAt();
         this.reply = comment.getReply() == null ? null : new ReplyVO(comment.getReply());
     }

+ 2 - 2
src/main/java/nju/seec/helper/vo/CourseVO.java

@@ -25,7 +25,7 @@ public class CourseVO {
         this.bio = course.getBio();
         this.createAt = course.getCreateAt();
 
-        this.teacherId = course.getTeacherId();
-        this.teacherName = course.getTeacherName();
+        this.teacherId = course.getTeacher().getId();
+        this.teacherName = course.getTeacher().getName();
     }
 }

+ 4 - 4
src/main/java/nju/seec/helper/vo/SlideVO.java

@@ -25,10 +25,10 @@ public class SlideVO {
 
     public SlideVO(@NonNull Slide slide) {
         this.id = slide.getId();
-        this.courseId = slide.getCourseId();
-        this.courseName = slide.getCourseName();
-        this.teacherId = slide.getTeacherId();
-        this.teacherName = slide.getTeacherName();
+        this.courseId = slide.getCourse().getId();
+        this.courseName = slide.getCourse().getName();
+        this.teacherId = slide.getTeacher().getId();
+        this.teacherName = slide.getTeacher().getName();
         this.state = slide.getState();
         this.name = slide.getName();
         this.createAt = slide.getCreateAt();

+ 4 - 4
src/main/resources/application-dev.yml

@@ -4,10 +4,10 @@ spring:
     driver-class-name: com.mysql.cj.jdbc.Driver
     username: root
     password: 123456
-    schema: classpath:update.sql
-    schema-username: root
-    schema-password: 123456
-    initialization-mode: always
+#    schema: classpath:update.sql
+#    schema-username: root
+#    schema-password: 123456
+#    initialization-mode: always
   jpa:
     open-in-view: true
     hibernate:

+ 118 - 23
src/main/resources/update.sql

@@ -1,27 +1,122 @@
-alter table user modify id bigint auto_increment;
+# alter table user
+#     modify id bigint auto_increment;
+#
+# alter table course
+#     modify id bigint auto_increment;
+# alter table course
+#     modify teacher_id bigint not null;
+#
+# alter table choose
+#     modify id bigint auto_increment;
+# alter table choose
+#     modify course_id bigint not null;
+# alter table choose
+#     modify student_id bigint not null;
+#
+# alter table slide
+#     modify id bigint auto_increment;
+# alter table slide
+#     modify course_id bigint not null;
+# alter table slide
+#     modify teacher_id bigint not null;
+# alter table slide
+#     drop teacher_name;
+# alter table slide
+#     drop course_name;
 
-alter table course modify id bigint auto_increment;
-alter table course modify teacher_id bigint not null;
+# alter table message
+#     modify id bigint auto_increment;
+# alter table message
+#     modify slide_id bigint;
+# alter table message
+#     modify to_user_id bigint not null;
 
-alter table choose modify id bigint auto_increment;
-alter table choose modify course_id bigint not null;
-alter table choose modify student_id bigint not null;
+# alter table reply
+#     modify id bigint auto_increment;
+# alter table reply
+#     modify teacher_id bigint not null;
+# alter table reply
+#     drop foreign key FKrd48dnp0ucmn1uyk9bdhtjm1c;
+# drop index FKrd48dnp0ucmn1uyk9bdhtjm1c on reply;
+# alter table reply
+#     modify comment_id bigint not null;
 
-alter table slide modify id bigint auto_increment;
-alter table slide modify course_id bigint not null;
-alter table slide modify teacher_id bigint not null;
+update comment
+set top_number=0
+where top_number is null;
+# alter table comment
+#     modify top_number int not null;
+# alter table comment
+#     modify user_id bigint not null;
+# alter table comment
+#     modify slide_id bigint not null;
+alter table comment
+    modify content text not null;
 
-alter table message modify id bigint auto_increment;
-alter table message modify slide_id bigint;
-alter table message modify to_user_id bigint not null;
-
-alter table reply modify id bigint auto_increment;
-alter table reply modify teacher_id bigint not null;
-alter table reply drop foreign key FK6w0ns67lrq1jdiwi5xvtj1vxx;
-drop index FK6w0ns67lrq1jdiwi5xvtj1vxx on reply;
-alter table reply modify comment_id bigint not null;
-
-alter table comment modify id bigint auto_increment;
-alter table comment modify user_id bigint not null;
-alter table comment modify slide_id bigint not null;
-alter table comment modify content text not null;
+# alter table comment
+#     drop user_name;
+# alter table comment
+#     drop user_type;
+delimiter //
+create procedure clear_other()
+begin
+    if exists(
+            select *
+            from information_schema.COLUMNS
+            where TABLE_NAME = 'course'
+              and COLUMN_NAME = 'teacher_name'
+              and TABLE_SCHEMA = 'helper'
+        )
+    then
+        alter table course
+            drop column teacher_name;
+    end if;
+    if exists(
+            select *
+            from information_schema.COLUMNS
+            where TABLE_NAME = 'slide'
+              and COLUMN_NAME = 'teacher_name'
+              and TABLE_SCHEMA = 'helper'
+        )
+    then
+        alter table slide
+            drop column teacher_name;
+    end if;
+    if exists(
+            select *
+            from information_schema.COLUMNS
+            where TABLE_NAME = 'slide'
+              and COLUMN_NAME = 'course_name'
+              and TABLE_SCHEMA = 'helper'
+        )
+    then
+        alter table slide
+            drop column course_name;
+    end if;
+    if exists(
+            select *
+            from information_schema.COLUMNS
+            where TABLE_NAME = 'comment'
+              and COLUMN_NAME = 'user_name'
+              and TABLE_SCHEMA = 'helper'
+        )
+    then
+        alter table comment
+            drop column user_name;
+    end if;
+    if exists(
+            select *
+            from information_schema.COLUMNS
+            where TABLE_NAME = 'comment'
+              and COLUMN_NAME = 'user_type'
+              and TABLE_SCHEMA = 'helper'
+        )
+    then
+        alter table comment
+            drop column user_type;
+    end if;
+end;
+//
+delimiter ;
+call clear_other();
+drop procedure clear_other;