Jelajahi Sumber

Merge branch 'feature-theme-course' into 'Develop'

Feature theme course



See merge request !1209

menduo 5 tahun lalu
induk
melakukan
b9af76c233
42 mengubah file dengan 2248 tambahan dan 428 penghapusan
  1. 0 20
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/enums/CourseAuthorisedType.java
  2. 25 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/enums/CourseType.java
  3. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/CourseResourceDao.java
  4. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/OperationCourseDao.java
  5. 23 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/OperationResourceDao.java
  6. 1 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThemeDetailDao.java
  7. 7 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThemeEntityRelationsDao.java
  8. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/User2ThemeDao.java
  9. 67 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/OperationResource.java
  10. 6 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeDetail.java
  11. 40 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeEntityRelations.java
  12. 5 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/User2Theme.java
  13. 4 2
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/CourseResourceService.java
  14. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/OperationRecourseService.java
  15. 13 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeEntityRelationsSchedulerService.java
  16. 12 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeEntityRelationsStatusService.java
  17. 3 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeSchedulerService.java
  18. 9 2
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeService.java
  19. 4 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/User2RoleService.java
  20. 8 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/User2ThemeService.java
  21. 8 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/CourseResourceServiceImpl.java
  22. 30 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/OperationRecourseServiceImpl.java
  23. 130 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeEntityRelationsSchedulerServiceImpl.java
  24. 45 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeEntityRelationsStatusServiceImpl.java
  25. 2 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeSchedulerServiceImpl.java
  26. 20 4
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeServiceImpl.java
  27. 0 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeStatusServiceImpl.java
  28. 10 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/User2RoleServiceImpl.java
  29. 25 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/User2ThemeServiceImpl.java
  30. 273 116
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThemeController.java
  31. 2 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseResourceVO.java
  32. 2 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseVO.java
  33. 2 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/EntityVO.java
  34. 22 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/StudentCourseResultVO.java
  35. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeEntityRelationsVO.java
  36. 13 2
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ThemeEntityRelationsVOWrapper.java
  37. 18 9
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ThemeVOWrapper.java
  38. 33 6
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ThemeLogic.java
  39. 2 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ProductProcessLogicImpl.java
  40. 549 216
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ThemeLogicImpl.java
  41. 288 0
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/ctrl/ThemeControllerTest.java
  42. 511 29
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/logic/impl/ThemeLogicImplTest.java

+ 0 - 20
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/enums/CourseAuthorisedType.java

@@ -1,20 +0,0 @@
-package cn.iselab.mooctest.site.common.enums;
-
-
-import lombok.Getter;
-
-/**
- * @author guochao
- * @date 2020-02-15 17:29
- */
-public enum CourseAuthorisedType {
-
-    UNAUTHORISED(0),
-    AUTHORISED(1);
-
-    @Getter
-    int type;
-    CourseAuthorisedType(int type){
-        this.type = type;
-    }
-}

+ 25 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/enums/CourseType.java

@@ -0,0 +1,25 @@
+package cn.iselab.mooctest.site.common.enums;
+
+
+import lombok.Getter;
+
+/**
+ * @author guochao
+ * @date 2020-03-07 12:15
+ */
+public enum CourseType {
+
+    WEB(1),
+    DEV(2),
+    APP(3),
+    EMB(4),
+    AUTO(5),
+    CUSTOM(6),
+    OTHER(7);
+    //1-Web安全测试 2-开发者测试 3-移动应用测试 4-嵌入式测试 5-自主可控测试 6-自定义课程 7-其他
+
+    @Getter
+    private int type;
+
+    CourseType(int type){this.type = type;}
+}

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/CourseResourceDao.java

@@ -19,6 +19,8 @@ import java.util.List;
 public interface CourseResourceDao extends PagingAndSortingRepository<CourseResource,Long>, JpaSpecificationExecutor<CourseResource>, UpdateAdapter<CourseResource,Long> {
     List<CourseResource> findAllByIdIn(List<Long> ids);
 
+    List<CourseResource> findAllByOwnerIdAndDeleted(Long userId, int isDeleted);
+
 //    Page<CourseResource> findAllAndDeleted(Pageable pageable, int deletedStatus);
 
 //    @Override

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/OperationCourseDao.java

@@ -16,4 +16,6 @@ public interface OperationCourseDao extends PagingAndSortingRepository<Operation
     List<OperationCourse> findAllByUserId(Long userId);
 
     List<OperationCourse> findAllByUserIdAndOperationNotAndIsDeleted(Long userId, String operation, int isDeleted);
+
+    List<OperationCourse> findAllByUserIdAndOperationAndIsDeleted(Long userId, String operation, int isDeleted);
 }

+ 23 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/OperationResourceDao.java

@@ -0,0 +1,23 @@
+package cn.iselab.mooctest.site.dao;
+
+import cn.iselab.mooctest.site.dao.adapter.UpdateAdapter;
+import cn.iselab.mooctest.site.models.OperationResource;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-03-14 16:09
+ */
+@Transactional
+public interface OperationResourceDao extends PagingAndSortingRepository<OperationResource,Long>, CrudRepository<OperationResource,Long>,JpaSpecificationExecutor<OperationResource>, UpdateAdapter<OperationResource,Long> {
+
+
+    List<OperationResource> findAllByUserIdAndOperationNotAndResourceIdAndResDeletedAndResPublicStatusAndPublicStatusAndTypeAndIsDeleted(Long userId, String operation, Long resourceId, int resDeleted, int resIsPublic, int courseIsPublic, int type, int courseDeleted);
+
+    List<OperationResource> findAllByResourceId(Long resourceId);
+}

+ 1 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThemeDetailDao.java

@@ -1,7 +1,6 @@
 package cn.iselab.mooctest.site.dao;
 
 import cn.iselab.mooctest.site.dao.adapter.UpdateAdapter;
-import cn.iselab.mooctest.site.models.CourseResource;
 import cn.iselab.mooctest.site.models.ThemeDetail;
 
 import org.springframework.data.domain.Page;
@@ -12,7 +11,6 @@ import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.data.repository.PagingAndSortingRepository;
 import org.springframework.data.repository.query.Param;
-import org.springframework.stereotype.Component;
 
 import javax.transaction.Transactional;
 import java.util.List;
@@ -38,7 +36,7 @@ public interface ThemeDetailDao extends PagingAndSortingRepository<ThemeDetail,L
     @Query("update ThemeDetail t set t.status = :status where t.id= :id")
     void updateStatusById(@Param("id") Long id, @Param("status") int status);
 
-
+    List<ThemeDetail> findAllByTitleLikeAndTypeAndPublicStatusAndVisibilityNotAndReleasedAndIsDeleted(String courseName,int type, int publicStatus, int visibility,int released, int isDeleted);
 
     List<ThemeDetail> findAllByVisibilityIsNot(int visibility);
 }

+ 7 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThemeEntityRelationsDao.java

@@ -7,8 +7,11 @@ import cn.iselab.mooctest.site.models.ThemeEntityRelations;
 import java.util.List;
 
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Component;
 
 import javax.transaction.Transactional;
@@ -26,4 +29,8 @@ public interface ThemeEntityRelationsDao extends PagingAndSortingRepository<Them
     ThemeEntityRelations findByThemeIdAndEntityIdAndEntityType(long themeId, long entityId, EntityTypeEnum entityTypeEnum);
     List<ThemeEntityRelations> findAllByEntityIdAndEntityType(long entityId, EntityTypeEnum entityTypeEnum);
     void deleteByThemeId(Long themeId);
+
+    @Modifying
+    @Query("update ThemeEntityRelations t set t.releaseStatus = :status where t.id= :id")
+    void updateStatusById(@Param("id") Long id, @Param("status") int status);
 }

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/User2ThemeDao.java

@@ -1,8 +1,12 @@
 package cn.iselab.mooctest.site.dao;
 
 import cn.iselab.mooctest.site.models.User2Theme;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
 
+import javax.transaction.Transactional;
 import java.util.List;
 
 /**
@@ -11,8 +15,18 @@ import java.util.List;
  * @author: menduo
  * @create: 2019-06-03 15:04
  **/
+@Transactional
 public interface User2ThemeDao extends CrudRepository<User2Theme,Long> {
     User2Theme findByUserIdAndThemeId(long userId,long themeId);
+    List<User2Theme> findAllByUserIdAndThemeId(long userId, long themeId);
     List<User2Theme> findByUserId(long userId);
+
+    @Modifying
+    @Query("update User2Theme u2t set u2t.operation = :operation where u2t.id= :id")
+    void updateOperationById(@Param("id") long id, @Param("operation") String operation);
+
+    User2Theme findByUserIdAndThemeIdAndOperation(long userId, long themeId, String operation);
+
+    List<User2Theme> findByUserIdAndThemeIdAndOperationNot(long userId, long themeId, String operation);
 }
 

+ 67 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/OperationResource.java

@@ -0,0 +1,67 @@
+package cn.iselab.mooctest.site.models;
+
+import lombok.Data;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.sql.Timestamp;
+
+/**
+ * guochao
+ **/
+@Entity
+@Table(name = "course_resource_operation")
+@Data
+public class OperationResource {
+
+    @Id
+    private Long id;
+
+    @Column(name = "operation")
+    private String operation;
+
+    @Column(name = "user_id")
+    private Long userId;
+
+    @Column(name = "title")
+    private String title;
+
+    @Column(name = "owner_id")
+    private Long ownerId;
+
+    @Column(name = "status")
+    private Integer status;
+
+    @Column(name = "public")
+    private int publicStatus;
+
+    @Column(name = "type")
+    private int type;
+
+    @Column(name = "is_deleted")
+    private int isDeleted;
+
+    @Column(name = "released")
+    private int released;
+
+    @Column(name = "visibility")
+    private int visibility;
+
+    @Column(name = "resource_id")
+    private Long resourceId;
+
+    @Column(name = "res_title")
+    private String resTitle;
+
+    @Column(name = "res_owner_id")
+    private Long resOwnerId;
+
+    @Column(name = "res_deleted")
+    private int resDeleted;
+
+    @Column(name = "res_public_status")
+    private int resPublicStatus;
+
+}

+ 6 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeDetail.java

@@ -30,9 +30,12 @@ public class ThemeDetail {
     public static final Integer IS_PUBLIC = 0;
     public static final Integer IS_PRIVATE = 1;
 
-//    public static final Byte TYPE_Web = 0;
-//    public static final Byte TYPE_mobile = 1;
-//    public static final Byte TYPE_python = 2;
+    // deletedStatus
+    public static final Integer IS_DELETED = 1;
+    public static final Integer NOT_DELETED = 0;
+
+    // theme type
+    public static final Integer COURSE = 0;
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)

+ 40 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeEntityRelations.java

@@ -5,6 +5,7 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.*;
+import java.sql.Timestamp;
 
 /**
  * @program: mooctest-site
@@ -17,6 +18,10 @@ import javax.persistence.*;
 @Data
 @NoArgsConstructor
 public class ThemeEntityRelations {
+
+    public static final Integer UNRELEASED = 0;
+    public static final Integer RELEASED = 1;
+
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private long id;
@@ -37,6 +42,22 @@ public class ThemeEntityRelations {
     @Column(name = "is_previewable")
     private boolean previewable;
 
+    @Column(name = "release_time")
+    private Timestamp releaseTime;
+
+    @Column(name = "release_status")
+    private int releaseStatus;
+
+    public ThemeEntityRelations(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releaseTime, int releaseStatus) {
+        this.themeId = themeId;
+        this.entityId = entityId;
+        this.entityType = entityType;
+        this.sortId = sortId;
+        this.previewable = previewable;
+        this.releaseTime = releaseTime;
+        this.releaseStatus = releaseStatus;
+    }
+
     public ThemeEntityRelations(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable) {
         this.themeId = themeId;
         this.entityId = entityId;
@@ -45,6 +66,25 @@ public class ThemeEntityRelations {
         this.previewable = previewable;
     }
 
+    public ThemeEntityRelations(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releaseTime) {
+        this.themeId = themeId;
+        this.entityId = entityId;
+        this.entityType = entityType;
+        this.sortId = sortId;
+        this.previewable = previewable;
+        this.releaseTime = releaseTime;
+    }
+
+    public ThemeEntityRelations(long id ,long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releaseTime) {
+        this.id = id;
+        this.themeId = themeId;
+        this.entityId = entityId;
+        this.entityType = entityType;
+        this.sortId = sortId;
+        this.previewable = previewable;
+        this.releaseTime = releaseTime;
+    }
+
     public ThemeEntityRelations(long id ,long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable) {
         this.id = id;
         this.themeId = themeId;

+ 5 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/User2Theme.java

@@ -18,6 +18,11 @@ import javax.persistence.*;
 @NoArgsConstructor
 @AllArgsConstructor
 public class User2Theme {
+
+    public static final String AUTH_VIEW = "view";
+    public static final String AUTH_USE = "use";
+    public static final String AUTH_ALL = "*";
+
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private long id;

+ 4 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/CourseResourceService.java

@@ -16,7 +16,9 @@ public interface CourseResourceService {
 
     CourseResource getCourseResourceByResourceId(long courseResourceId);
 
-    CourseResource createCourseRecourse(CourseResource courseResource);
+    CourseResource createCourseResource(CourseResource courseResource);
 
-    CourseResource updateCourseRecourse(CourseResource courseResource);
+    CourseResource updateCourseResource(CourseResource courseResource);
+
+    List<CourseResource> getCourseResourceByUserId(Long userId, int isDeleted);
 }

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/OperationRecourseService.java

@@ -0,0 +1,16 @@
+package cn.iselab.mooctest.site.service;
+
+import cn.iselab.mooctest.site.models.OperationResource;
+
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-03-14 16:15
+ */
+public interface OperationRecourseService {
+
+    List<OperationResource> getOperationResourceList(Long userId, String operation, Long resourceId, int resDeleted, int resIsPublic, int courseIsPublic, int type, int courseDeleted);
+
+    List<OperationResource> getOperationResourceListByResourceId(Long resourceId);
+}

+ 13 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeEntityRelationsSchedulerService.java

@@ -0,0 +1,13 @@
+package cn.iselab.mooctest.site.service;
+
+import cn.iselab.mooctest.site.models.ThemeEntityRelations;
+
+/**
+ * @author guochao
+ * @date 2020-02-17 10:54
+ */
+public interface ThemeEntityRelationsSchedulerService {
+    boolean createEntityRelationScheduler(ThemeEntityRelations themeEntityRelations);
+    boolean cancelEntityRelationScheduler(ThemeEntityRelations themeEntityRelations);
+    void clearAllSchedulers();
+}

+ 12 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeEntityRelationsStatusService.java

@@ -0,0 +1,12 @@
+package cn.iselab.mooctest.site.service;
+
+import cn.iselab.mooctest.site.models.ThemeEntityRelations;
+
+/**
+ * @author guochao
+ * @date 2020-02-17 10:57
+ */
+public interface ThemeEntityRelationsStatusService {
+    Integer updateStatus(ThemeEntityRelations themeEntityRelations);
+    Integer updateStatusByTime(Long releasedTime);
+}

+ 3 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeSchedulerService.java

@@ -7,7 +7,7 @@ import cn.iselab.mooctest.site.models.ThemeDetail;
  * @date 2020-02-17 10:54
  */
 public interface ThemeSchedulerService {
-    public boolean createNewThemeScheduler(ThemeDetail themeDetail);
-    public boolean cancelThemeScheduler(ThemeDetail themeDetail);
-    public void clearAllSchedulers();
+    boolean createNewThemeScheduler(ThemeDetail themeDetail);
+    boolean cancelThemeScheduler(ThemeDetail themeDetail);
+    void clearAllSchedulers();
 }

+ 9 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeService.java

@@ -7,6 +7,7 @@ import cn.iselab.mooctest.site.web.data.ThemeDetailVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
+import java.sql.Timestamp;
 import java.util.List;
 import java.util.Map;
 
@@ -40,9 +41,11 @@ public interface ThemeService {
     ThemeEntityRelations addEntity(ThemeEntityRelations themeEntityRelations);
     void rmEntity(long id);
 
-    ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable);
+    ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releasedTime, int updateStatus);
 
-    ThemeEntityRelations updateEntityRelation(long id, long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable);
+    ThemeEntityRelations updateEntityRelation(long id, long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releasedTime);
+
+    ThemeEntityRelations updateEntityRelation(ThemeEntityRelations themeEntityRelations);
 
     void createStudyViewRecord(Long themeId, Long courseId, Long userId, EntityTypeEnum entityType);
 
@@ -66,4 +69,8 @@ public interface ThemeService {
     Page<ThemeDetail> getPlatformThemeDetails(Map<String, String> extraCondition, Map<String, String> excludeCondition, Pageable pageable, String keyword);
 
     ThemeEntityRelations getThemeEntityRelationsByThemeIdAndEntityIdAndEntityType(Long themeId, Long entityId, EntityTypeEnum courseResource);
+
+    List<ThemeDetail> getThemeDetailByCourseName(String courseName);
+
+    List<OperationCourse> getTeacherAuthorizedCourseListByUse(Long teacherId, String operation);
 }

+ 4 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/User2RoleService.java

@@ -21,4 +21,8 @@ public interface User2RoleService {
     List<User2Role> findByRoleId(Long roleId);
 
     User2Role addRole2User(long userId, long roleId);
+
+    List<User2Role> getRole2User(long userId, long roleId);
+
+    void deleteRole2User(List<User2Role> user2RoleList);
 }

+ 8 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/User2ThemeService.java

@@ -1,6 +1,8 @@
 package cn.iselab.mooctest.site.service;
 
 import cn.iselab.mooctest.site.models.User2Theme;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
 
 import java.util.List;
 
@@ -14,5 +16,11 @@ public interface User2ThemeService {
     void createRelation(long userId, long themeId);
     void createOperationRelation(long userId, long themeId, String operation);
     User2Theme findByUserIdAndThemeId(long userId, long themeId);
+    List<User2Theme> findAllByUserIdAndThemeId(long userId, long themeId);
+    User2Theme findByUserIdAndThemeIdAndOperation(long userId, long themeId, String operation);
+    List<User2Theme> findByUserIdAndThemeIdAndOperationNot(long userId, long themeId, String operation);
+    void updateOperationById(long id, String operation);
     List<User2Theme> findByUserId(long userId);
+
+    void delete(User2Theme user2Theme);
 }

+ 8 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/CourseResourceServiceImpl.java

@@ -29,12 +29,17 @@ public class CourseResourceServiceImpl implements CourseResourceService {
     }
 
     @Override
-    public CourseResource createCourseRecourse(CourseResource courseResource) {
+    public CourseResource createCourseResource(CourseResource courseResource) {
         return courseResourceDao.save(courseResource);
     }
 
     @Override
-    public CourseResource updateCourseRecourse(CourseResource courseResource) {
-        return createCourseRecourse(courseResource);
+    public CourseResource updateCourseResource(CourseResource courseResource) {
+        return createCourseResource(courseResource);
+    }
+
+    @Override
+    public List<CourseResource> getCourseResourceByUserId(Long userId, int isDeleted) {
+        return courseResourceDao.findAllByOwnerIdAndDeleted(userId, isDeleted);
     }
 }

+ 30 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/OperationRecourseServiceImpl.java

@@ -0,0 +1,30 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.dao.OperationResourceDao;
+import cn.iselab.mooctest.site.models.OperationResource;
+import cn.iselab.mooctest.site.service.OperationRecourseService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-03-14 16:16
+ */
+@Service
+public class OperationRecourseServiceImpl implements OperationRecourseService {
+
+    @Autowired
+    private OperationResourceDao operationResourceDao;
+
+    @Override
+    public List<OperationResource> getOperationResourceList(Long userId, String operation, Long resourceId, int resDeleted, int resIsPublic, int courseIsPublic, int type, int courseDeleted) {
+        return operationResourceDao.findAllByUserIdAndOperationNotAndResourceIdAndResDeletedAndResPublicStatusAndPublicStatusAndTypeAndIsDeleted(userId, operation, resourceId, resDeleted, resIsPublic, courseIsPublic, type, courseDeleted);
+    }
+
+    @Override
+    public List<OperationResource> getOperationResourceListByResourceId(Long resourceId) {
+        return operationResourceDao.findAllByResourceId(resourceId);
+    }
+}

+ 130 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeEntityRelationsSchedulerServiceImpl.java

@@ -0,0 +1,130 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.models.ThemeEntityRelations;
+import cn.iselab.mooctest.site.service.ThemeEntityRelationsSchedulerService;
+import cn.iselab.mooctest.site.service.ThemeEntityRelationsStatusService;
+import cn.iselab.mooctest.site.service.ThemeEntityRelationsSchedulerService;
+import cn.iselab.mooctest.site.service.ThemeStatusService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.scheduling.support.CronTrigger;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+
+/**
+ * Created by guochao on 2020/2/17.
+ */
+@Service
+public class ThemeEntityRelationsSchedulerServiceImpl implements ThemeEntityRelationsSchedulerService {
+
+    @Autowired
+    private ThemeEntityRelationsStatusService themeEntityRelationsStatusService;
+
+    private ConcurrentHashMap<ThemeEntityRelations, ThemeEntityRelationsScheduler> map = new ConcurrentHashMap<>();
+
+    @Override
+    public void clearAllSchedulers() {
+        this.map = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public boolean createEntityRelationScheduler(ThemeEntityRelations themeEntityRelations) {
+        if(! this.map.containsKey(themeEntityRelations)) {
+            String cronExp = this.generateNextCronExp(themeEntityRelations);
+            if(cronExp==null){
+                return false;
+            }
+            ThemeEntityRelationsScheduler themeEntityRelationsScheduler = new ThemeEntityRelationsScheduler(themeEntityRelations, cronExp);
+            themeEntityRelationsScheduler.startCron();
+            map.put(themeEntityRelations, themeEntityRelationsScheduler);
+            System.out.println("map.size: " + map.size());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean cancelEntityRelationScheduler(ThemeEntityRelations themeEntityRelations) {
+        if(this.map.containsKey(themeEntityRelations)) {
+            ThemeEntityRelationsScheduler themeEntityRelationsScheduler = map.get(themeEntityRelations);
+            themeEntityRelationsScheduler.stopCron();
+            map.remove(themeEntityRelations);
+            return true;
+        }
+        return false;
+    }
+
+    public String generateNextCronExp(ThemeEntityRelations themeEntityRelations) {
+        String cronExp = null;
+        String dateFormat="ss mm HH dd MM EE";
+        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat, Locale.US);
+        Date now = new Date();
+        if(now.before(themeEntityRelations.getReleaseTime())) {
+            cronExp = sdf.format(themeEntityRelations.getReleaseTime());
+        }
+//        else if(now.before(themeEntityRelations.getEndTime())){
+//            cronExp = sdf.format(themeEntityRelations.getEndTime());
+//        }
+        return cronExp;
+    }
+
+    private class ThemeEntityRelationsScheduler {
+        private ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
+        private ScheduledFuture<?> future;
+        private ThemeEntityRelations themeEntityRelations = null;
+        private String cronExp = null;
+
+        ThemeEntityRelationsScheduler() {}
+
+        ThemeEntityRelationsScheduler (ThemeEntityRelations themeEntityRelations, String cronExp) {
+            this.themeEntityRelations = themeEntityRelations;
+            this.cronExp =  cronExp;
+            this.threadPoolTaskScheduler =  new ThreadPoolTaskScheduler();
+            this.threadPoolTaskScheduler.initialize();
+        }
+
+//        public ThreadPoolTaskScheduler threadPoolTaskScheduler () {
+//            return new ThreadPoolTaskScheduler();
+//        }
+
+        public String startCron() {
+            future = this.threadPoolTaskScheduler.schedule(new MyRunnable(this.themeEntityRelations), new CronTrigger(this.cronExp));
+            System.out.println("ThemeEntityRelationsScheduler " + this.themeEntityRelations.getId() + ".startCron()");
+            return "startCron";
+        }
+
+        public String stopCron() {
+            if (future != null) {
+                future.cancel(true);
+            }
+            System.out.println("ThemeEntityRelationsScheduler" + this.themeEntityRelations.getId() + ".stopCron()");
+            return "stopCron";
+        }
+
+        private class MyRunnable implements Runnable {
+
+            private ThemeEntityRelations themeEntityRelations;
+
+            public MyRunnable() {}
+
+            public MyRunnable (ThemeEntityRelations themeEntityRelations) {
+                this.themeEntityRelations = themeEntityRelations;
+            }
+
+            @Override
+            public void run() {
+                System.out.println("ThemeEntityRelationsScheduler " + this.themeEntityRelations.getId() + " updateStatus," + new Date());
+                Integer result = themeEntityRelationsStatusService.updateStatus(this.themeEntityRelations);
+                map.remove(themeEntityRelations);
+                if(result.equals(ThemeEntityRelations.UNRELEASED)) {
+                    createEntityRelationScheduler(themeEntityRelations);
+                }
+            }
+        }
+    }
+}

+ 45 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeEntityRelationsStatusServiceImpl.java

@@ -0,0 +1,45 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.dao.ThemeEntityRelationsDao;
+import cn.iselab.mooctest.site.dao.ThemeEntityRelationsDao;
+import cn.iselab.mooctest.site.models.ThemeEntityRelations;
+import cn.iselab.mooctest.site.service.ThemeEntityRelationsStatusService;
+import cn.iselab.mooctest.site.service.ThemeStatusService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author guochao
+ * @date 2020-02-17 10:57
+ */
+@Service
+public class ThemeEntityRelationsStatusServiceImpl implements ThemeEntityRelationsStatusService {
+    @Autowired
+    private ThemeEntityRelationsDao themeEntityRelationsDao;
+
+    @Override
+    public Integer updateStatus(ThemeEntityRelations themeEntityRelations) {
+        int status = ThemeEntityRelations.UNRELEASED;
+
+        long curr = System.currentTimeMillis();
+        if (themeEntityRelations.getReleaseTime().getTime() > curr) {
+            status = ThemeEntityRelations.UNRELEASED;
+        } else {
+            status = ThemeEntityRelations.RELEASED;
+        }
+        themeEntityRelationsDao.updateStatusById(themeEntityRelations.getId(),status);
+        return status;
+    }
+
+    @Override
+    public Integer updateStatusByTime(Long releasedTime) {
+        int status = ThemeEntityRelations.UNRELEASED;
+        long curr = System.currentTimeMillis();
+        if (releasedTime > curr) {
+            status = ThemeEntityRelations.UNRELEASED;
+        } else {
+            status = ThemeEntityRelations.RELEASED;
+        }
+        return status;
+    }
+}

+ 2 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeSchedulerServiceImpl.java

@@ -40,7 +40,6 @@ public class ThemeSchedulerServiceImpl implements ThemeSchedulerService {
             ThemeScheduler themeDetailScheduler = new ThemeScheduler(themeDetail, cronExp);
             themeDetailScheduler.startCron();
             map.put(themeDetail, themeDetailScheduler);
-            System.out.println("map.size: " + map.size());
             return true;
         }
         return false;
@@ -91,7 +90,7 @@ public class ThemeSchedulerServiceImpl implements ThemeSchedulerService {
 
         public String startCron() {
             future = this.threadPoolTaskScheduler.schedule(new MyRunnable(this.themeDetail), new CronTrigger(this.cronExp));
-            System.out.println("ThemeScheduler " + this.themeDetail.getTitle() + ".startCron()");
+//            System.out.println("ThemeScheduler " + this.themeDetail.getTitle() + ".startCron()");
             return "startCron";
         }
 
@@ -99,7 +98,7 @@ public class ThemeSchedulerServiceImpl implements ThemeSchedulerService {
             if (future != null) {
                 future.cancel(true);
             }
-            System.out.println("ThemeScheduler" + this.themeDetail.getTitle() + ".stopCron()");
+//            System.out.println("ThemeScheduler" + this.themeDetail.getTitle() + ".stopCron()");
             return "stopCron";
         }
 

+ 20 - 4
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeServiceImpl.java

@@ -24,6 +24,7 @@ import org.springframework.data.jpa.domain.Specifications;
 import org.springframework.stereotype.Service;
 
 import javax.persistence.criteria.*;
+import java.sql.Timestamp;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -204,21 +205,26 @@ public class ThemeServiceImpl implements ThemeService {
     }
 
     @Override
-    public ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable) {
+    public ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releasedTime, int updateStatus) {
         ThemeEntityRelations themeEntityRelations = checkRelationBetweenThemeAndEntity(themeId, entityId, entityType);
         if (themeEntityRelations != null){
 //            throw new HttpNotFoundException("课程中已包含此章节,请勿重复添加!");
             log.info("课程中已包含此章节,请勿重复添加!");
             return null;
         }else{
-            themeEntityRelations = new ThemeEntityRelations(themeId, entityId, entityType, sortId, previewable);
+            themeEntityRelations = new ThemeEntityRelations(themeId, entityId, entityType, sortId, previewable, releasedTime, updateStatus);
             return themeEntityRelationsDao.save(themeEntityRelations);
         }
     }
 
     @Override
-    public ThemeEntityRelations updateEntityRelation(long id, long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable){
-        ThemeEntityRelations themeEntityRelations = new ThemeEntityRelations(id, themeId, entityId, entityType, sortId, previewable);
+    public ThemeEntityRelations updateEntityRelation(long id, long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releasedTime){
+        ThemeEntityRelations themeEntityRelations = new ThemeEntityRelations(id, themeId, entityId, entityType, sortId, previewable, releasedTime);
+        return themeEntityRelationsDao.save(themeEntityRelations);
+    }
+
+    @Override
+    public ThemeEntityRelations updateEntityRelation(ThemeEntityRelations themeEntityRelations){
         return themeEntityRelationsDao.save(themeEntityRelations);
     }
 
@@ -502,4 +508,14 @@ public class ThemeServiceImpl implements ThemeService {
             return predicate;
         };
     }
+
+    @Override
+    public List<ThemeDetail> getThemeDetailByCourseName(String courseName){
+        return themeDetailDao.findAllByTitleLikeAndTypeAndPublicStatusAndVisibilityNotAndReleasedAndIsDeleted("%"+courseName+"%", ThemeDetail.COURSE, ThemeDetail.IS_PUBLIC, CourseVisibility.NONE_OPEN.getVisibility(), ThemeDetail.RELEASED, 0);
+    }
+
+    @Override
+    public List<OperationCourse> getTeacherAuthorizedCourseListByUse(Long teacherId, String operation){
+        return operationCourseDao.findAllByUserIdAndOperationAndIsDeleted(teacherId, operation, 0);
+    }
 }

+ 0 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeStatusServiceImpl.java

@@ -29,7 +29,6 @@ public class ThemeStatusServiceImpl implements ThemeStatusService {
         } else if (themeDetail.getEndTime().getTime() < curr){
             status = ThemeDetail.STATUS_FINISHED;
         }
-        System.out.println("课程状态 " + status);
         themeDetailDao.updateStatusById(themeDetail.getId(),status);
         return status;
     }

+ 10 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/User2RoleServiceImpl.java

@@ -84,5 +84,15 @@ public class User2RoleServiceImpl extends BaseService implements User2RoleServic
             return user2RoleDao.save(user2Role);
         }
     }
+
+    @Override
+    public List<User2Role> getRole2User(long userId, long roleId){
+        return user2RoleDao.findByUserIdAndRoleId(userId, roleId);
+    }
+
+    @Override
+    public void deleteRole2User(List<User2Role> user2RoleList){
+        user2RoleDao.deleteAll(user2RoleList);
+    }
 }
 

+ 25 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/User2ThemeServiceImpl.java

@@ -34,7 +34,32 @@ public class User2ThemeServiceImpl implements User2ThemeService {
     }
 
     @Override
+    public List<User2Theme> findAllByUserIdAndThemeId(long userId, long themeId){
+        return user2ThemeDao.findAllByUserIdAndThemeId(userId,themeId);
+    }
+
+    @Override
+    public User2Theme findByUserIdAndThemeIdAndOperation(long userId, long themeId, String operation) {
+        return user2ThemeDao.findByUserIdAndThemeIdAndOperation(userId,themeId,operation);
+    }
+
+    @Override
+    public List<User2Theme> findByUserIdAndThemeIdAndOperationNot(long userId, long themeId, String operation) {
+        return user2ThemeDao.findByUserIdAndThemeIdAndOperationNot(userId,themeId,operation);
+    }
+
+    @Override
+    public void updateOperationById(long id, String operation) {
+        user2ThemeDao.updateOperationById(id, operation);
+    }
+
+    @Override
     public List<User2Theme> findByUserId(long userId) {
         return user2ThemeDao.findByUserId(userId);
     }
+
+    @Override
+    public void delete(User2Theme user2Theme) {
+        user2ThemeDao.delete(user2Theme);
+    }
 }

+ 273 - 116
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThemeController.java

@@ -4,7 +4,6 @@ import cn.iselab.mooctest.site.common.constant.Constants;
 import cn.iselab.mooctest.site.common.constant.UrlConstants;
 import cn.iselab.mooctest.site.common.enums.RoleType;
 import cn.iselab.mooctest.site.models.ThemeDetail;
-import cn.iselab.mooctest.site.models.ThemeEntityRelations;
 import cn.iselab.mooctest.site.web.data.*;
 import cn.iselab.mooctest.site.web.data.response.ResponseVO;
 import cn.iselab.mooctest.site.web.data.response.ServerCode;
@@ -14,17 +13,16 @@ import cn.iselab.mooctest.site.web.logic.ExamLogic;
 import cn.iselab.mooctest.site.web.logic.GroupLogic;
 import cn.iselab.mooctest.site.web.logic.ThemeLogic;
 import com.google.gson.Gson;
+import io.lettuce.core.dynamic.annotation.Param;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.apache.shiro.subject.Subject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.*;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import javax.websocket.server.PathParam;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -82,7 +80,7 @@ public class ThemeController extends BaseSearchController {
     }
 
     /**
-     * fork课程时,教师对课程fork指定班级
+     * fork课程时,教师对课程fork指定班级,并校验是否具有未授权课程的章节
      * @param themeId
      * @return
      */
@@ -96,10 +94,10 @@ public class ThemeController extends BaseSearchController {
      * fork课程时,新建fork课程
      * @return
      */
-    @RequestMapping(value = UrlConstants.API + "theme/fork/{themeId}/{groupId}", method = RequestMethod.GET)
+    @RequestMapping(value = UrlConstants.API + "theme/fork", method = RequestMethod.POST)
     @RequiresPermissions(value = "theme:create")
-    public ResponseVO<CourseVO> getForkGroups(@PathVariable(value = "themeId") Long themeId, @PathVariable(value = "groupId") Long groupId) {
-        CourseVO courseResultVO = themeLogic.forkThemeCourse(themeId, groupId);
+    public ResponseVO<CourseVO> getForkGroups(@RequestBody ThemeDetailVO themeDetailVO) {
+        CourseVO courseResultVO = themeLogic.forkThemeCourse(themeDetailVO);
         return new ResponseVO<>(ServerCode.SUCCESS,courseResultVO);
     }
 
@@ -115,6 +113,18 @@ public class ThemeController extends BaseSearchController {
     }
 
     /**
+     * 教师和管理员对于课程章节的发布时间进行变更
+     * @return
+     */
+    @RequestMapping(value = UrlConstants.API + "theme/resource/released/{themeId}/{entityId}/{releasedTime}", method = RequestMethod.GET)
+    @RequiresPermissions(value = "theme:create")
+    public ResponseVO<ThemeEntityRelationsVO> changeReleasedTime(@PathVariable(value = "themeId") Long themeId,
+                                                                 @PathVariable(value = "entityId") Long entityId, @PathVariable("releasedTime") Long releasedTime) {
+        ThemeEntityRelationsVO themeEntityRelationsVO = themeLogic.updateCourseResourceReleasedTime(themeId, entityId, releasedTime);
+        return new ResponseVO<>(ServerCode.SUCCESS,themeEntityRelationsVO);
+    }
+
+    /**
      * 教师新建私有主题课程
      * @param courseVO
      * @return
@@ -136,8 +146,6 @@ public class ThemeController extends BaseSearchController {
     @RequiresPermissions(value = "theme:create")
     public ResponseVO<CourseVO> updateThemeCourse(@RequestBody CourseVO courseVO) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-//        themeLogic.updateThemeCourse(courseVO, userId);
-//        CourseVO courseResultVO = themeLogic.getCourse(courseVO.getThemeDetailVO().getId(), userId);
         return new ResponseVO<>(ServerCode.SUCCESS,themeLogic.updateThemeCourse(courseVO, userId));
     }
 
@@ -149,7 +157,6 @@ public class ThemeController extends BaseSearchController {
     @RequestMapping(value = UrlConstants.API + "theme/release/{themeId}", method = RequestMethod.PUT)
     @RequiresPermissions(value = "theme:create")
     public ResponseVO<ThemeDetail> releaseThemeCourse(@PathVariable("themeId") Long themeId) {
-        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
         return new ResponseVO<>(ServerCode.SUCCESS,themeLogic.releaseThemeCourse(themeId));
     }
 
@@ -161,7 +168,7 @@ public class ThemeController extends BaseSearchController {
     @RequestMapping(value = UrlConstants.API + "resource/create", method = RequestMethod.POST)
     @RequiresPermissions(value = "theme:create")
     public ResponseVO<CourseResourceVO> createCourseResource(@RequestBody CourseResourceVO courseResourceVO) {
-        CourseResourceVO courseResourceResultVO  = themeLogic.createCourseRecourse(courseResourceVO);
+        CourseResourceVO courseResourceResultVO  = themeLogic.createCourseResource(courseResourceVO);
         return new ResponseVO<>(ServerCode.SUCCESS,courseResourceResultVO);
     }
 
@@ -178,14 +185,26 @@ public class ThemeController extends BaseSearchController {
     }
 
     /**
-     * 教师导入课程章节、练习、考试
+     * 教师导入课程章节
      * @param entityVOList
      * @return
      */
     @RequestMapping(value = UrlConstants.API + "resource/add", method = RequestMethod.POST)
     @RequiresPermissions(value = "theme:create")
-    public ResponseVO<List<ThemeEntityRelations>> importCourseResources(@RequestBody List<EntityVO> entityVOList) {
-        List<ThemeEntityRelations> entityRelations = themeLogic.addRecourseList(entityVOList);
+    public ResponseVO<List<CourseResourceVO>> importCourseResources(@RequestBody List<EntityVO> entityVOList) {
+        List<CourseResourceVO> courseResourceVOS = themeLogic.addRecourseList(entityVOList);
+        return new ResponseVO<>(ServerCode.SUCCESS,courseResourceVOS);
+    }
+
+    /**
+     * 教师导入课程练习、考试
+     * @param entityVOList
+     * @return
+     */
+    @RequestMapping(value = UrlConstants.API + "resource/exam/add", method = RequestMethod.POST)
+    @RequiresPermissions(value = "theme:create")
+    public ResponseVO<List<ThemeEntityRelationsVO>> importExamResources(@RequestBody List<EntityVO> entityVOList) {
+        List<ThemeEntityRelationsVO> entityRelations = themeLogic.addExamRecourseList(entityVOList);
         return new ResponseVO<>(ServerCode.SUCCESS,entityRelations);
     }
 
@@ -222,9 +241,10 @@ public class ThemeController extends BaseSearchController {
      * @param searchCondition
      * @return
      */
-    @GetMapping(value = UrlConstants.API + "search/theme/type/{type}")
+    @GetMapping(value = UrlConstants.API + "search/theme/{themeId}/type/{type}")
     @RequiresPermissions(value = "theme:create")
-    public ResponseVO<Page<CourseVO>> searchThemeByCourseResource(@PathVariable("type") int type,@RequestParam(name = "searchCondition") String searchCondition) {
+    public ResponseVO<Page<CourseVO>> searchThemeByCourseResource(@PathVariable("themeId") Long themeId,@PathVariable("type") int type,
+                                                                  @RequestParam(name = "searchCondition") String searchCondition) {
         Gson gson = new Gson();
         SearchConditionVO searchConditionVO = gson.fromJson(searchCondition, SearchConditionVO.class);
         Pageable pageable = this.getResourcePageable(searchConditionVO);
@@ -233,10 +253,9 @@ public class ThemeController extends BaseSearchController {
         log.info("userId: {}获取课程列表,参数:{}",userId,searchConditionVO.toString());
 
         // 通过关键词获取相关的课程和课程章节
-        Long[] themeIdListByCourse = themeLogic.getAuthorisedResourceList(keyword);
+        Long[] themeIdListByCourse = themeLogic.getAuthorisedResourceList(themeId, keyword);
         // 获取所有themeId的课程章节信息
         List<CourseVO>  result = themeLogic.getCoursesByThemeIds(themeIdListByCourse, type);// type 0 代表所有课程
-
         int start = (int) pageable.getOffset();
         int end = (start + pageable.getPageSize()) > result.size() ? result.size() : (start + pageable.getPageSize());
         if (start > end) {
@@ -255,47 +274,66 @@ public class ThemeController extends BaseSearchController {
         return new PageRequest(activePage-1, Constants.DEFAULT_RECOURSE_ROWS_ON_PAGE, sort);
     }
 
-//    /**
-//     * 教师添加章节时  单个添加已授权的课程章节
-//     * @param entityVO
-//     * @return
-//     */
-//    @RequestMapping(value = UrlConstants.API + "add/resource", method = RequestMethod.POST)
-//    @RequiresPermissions(value = "theme:create")
-//    public ResponseVO<ThemeEntityRelations> addCourseResource(@RequestBody EntityVO entityVO) {
-//        ThemeEntityRelations entityRelations = themeLogic.addCourseRecourse(entityVO);
-//        return new ResponseVO<>(ServerCode.SUCCESS,entityRelations);
-//    }
-
+    /**
+     * 拖拽排序改变课程实体顺序
+     * @param themeId
+     * @return
+     * @throws Exception
+     */
+    @RequiresPermissions("theme:create")
+    @RequestMapping(value = UrlConstants.API + "theme/entity/order/{themeId}", method = RequestMethod.POST)
+    public ResponseVO<CourseVO> orderThemeDetailEntity(@PathVariable(name = "themeId") Long themeId, @RequestBody List<EntityVO> entityVOList) {
+        return new ResponseVO<>(ServerCode.SUCCESS,themeLogic.orderThemeDetailEntity(themeId, entityVOList));
+    }
 
     /**
-     * 获取课程教学信息 分数
+     * 教学管理:获取课程教学进度信息
      * @param themeId
      * @return
      * @throws Exception
      */
     @RequiresPermissions("theme:create")
     @RequestMapping(value = UrlConstants.API + "theme/result/{themeId}", method = RequestMethod.GET)
-    public ThemeInfoVO getScoreListByExamId(@PathVariable(name = "themeId") Long themeId) throws Exception {
-        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        String examPermissionStr = userId.toString() + ":task:view:" + themeId.toString();
-//        if (!SecurityUtils.getSubject().isPermitted(new ExamPermission(examPermissionStr))) {
-//            throw new UnauthenticatedException("forbidden");
-//        }
+    public ThemeInfoVO getScoreListByExamId(@PathVariable(name = "themeId") Long themeId) {
         return themeLogic.getThemeInfoByThemeId(themeId);
     }
 
     /**
+     * 教学管理:获取课程教学每个学生的进度信息
+     * @param themeId
+     * @return
+     * @throws Exception
+     */
+    @RequiresPermissions("theme:create")
+    @RequestMapping(value = UrlConstants.API + "theme/student/result/{themeId}", method = RequestMethod.GET)
+    public ResponseVO<Page<StudentCourseResultVO>> getStudentResult(@PathVariable(name = "themeId") Long themeId,
+                                                                    @RequestParam(name = "searchCondition") String searchCondition) {
+        Gson gson = new Gson();
+        SearchConditionVO searchConditionVO = gson.fromJson(searchCondition, SearchConditionVO.class);
+        Pageable pageable = this.getPageable(searchConditionVO);
+        String keyword = searchConditionVO.getKeyword();
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("userId: {}获取学生进度列表,参数:{}",userId,searchConditionVO.toString());
+
+        List<StudentCourseResultVO>  result = themeLogic.getStudentThemeInfoByThemeId(themeId,keyword);
+        int start = (int) pageable.getOffset();
+        int end = (start + pageable.getPageSize()) > result.size() ? result.size() : (start + pageable.getPageSize());
+        if (start > end) {
+            Page<StudentCourseResultVO> resultPage = new PageImpl<>(result.subList(0,
+                    result.size() < pageable.getOffset() ? result.size() : (int) pageable.getOffset()), pageable, result.size());
+            return new ResponseVO<>(20000, "分页信息超过最大值,已返回第一页的数据", resultPage);
+        }
+        Page<StudentCourseResultVO> resultPage = new PageImpl<>(result.subList(start, end), pageable, result.size());
+        return new ResponseVO<>(ServerCode.SUCCESS, resultPage);
+    }
+
+    /**
      * 学生点击视频记录
      */
     @RequiresPermissions("theme:view")
     @RequestMapping(value = UrlConstants.API + "theme/{themeId}/view/course/{courseId}", method = RequestMethod.GET)
     public void createStudyRecord(@PathVariable(name = "themeId") Long themeId,@PathVariable(name = "courseId") Long courseId) throws Exception {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        String examPermissionStr = userId.toString() + ":task:view:" + themeId.toString();
-//        if (!SecurityUtils.getSubject().isPermitted(new ExamPermission(examPermissionStr))) {
-//            throw new UnauthenticatedException("forbidden");
-//        }
         themeLogic.createStudyRecord(themeId, courseId, userId);
     }
 
@@ -328,12 +366,12 @@ public class ThemeController extends BaseSearchController {
     @RequestMapping(value = UrlConstants.API + "theme/{themeId}", method = RequestMethod.DELETE)
     public ResponseVO<ThemeDetailVO> deleteExamById(@PathVariable("themeId") Long themeId){
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        ThemeDetailVO themeDetailVO = themeLogic.deleteThemeDetailById(themeId);
+        return new ResponseVO<>(ServerCode.SUCCESS, themeDetailVO);
 //        String permissionStr = userId + ":theme:delete:" + themeId;
 //        boolean havePermissionToDelete = SecurityUtils.getSubject().isPermitted(new ExamPermission(permissionStr));
 //        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
         // 判断删除权限
-        ThemeDetailVO themeDetailVO = themeLogic.deleteThemeDetailById(themeId);
-        return new ResponseVO<>(ServerCode.SUCCESS, themeDetailVO);
 //        if (havePermissionToDelete || isAdmin) {
 //            return examLogic.deleteExamById(examId);
 //        }else {
@@ -347,8 +385,6 @@ public class ThemeController extends BaseSearchController {
         Gson gson = new Gson();
         SearchConditionVO searchConditionVO = gson.fromJson(searchCondition, SearchConditionVO.class);
         Pageable pageable = this.getPageable(searchConditionVO);
-//        String keyword = searchConditionVO.getKeyword();
-//        Map<String, String> extraCondition = super.getExtraCondition(searchConditionVO);
         Page<ThemeDetailVO> themeDetailVOS = themeLogic.getThemeDetailList(pageable);
         return new ResponseVO<>(ServerCode.SUCCESS, themeDetailVOS);
     }
@@ -375,74 +411,6 @@ public class ThemeController extends BaseSearchController {
         return new ResponseVO<>(ServerCode.SUCCESS,themeDetailVO);
     }
 
-
-    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/case/{caseId}", method = RequestMethod.PUT)
-    @RequiresPermissions(value = "theme:create")
-    public ResponseVO<CaseExtendsVO> addCases2theme(@PathVariable("themeId")long themeId, @PathVariable("caseId") long caseId) {
-        CaseExtendsVO caseExtendsVO = themeLogic.createRelationBetweenThemeAndCase(themeId,caseId);
-        return new ResponseVO<>(ServerCode.SUCCESS, caseExtendsVO);
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/paper/{paperId}", method = RequestMethod.PUT)
-    @RequiresPermissions(value = "theme:create")
-    public ResponseVO addPapers2theme(@PathVariable("themeId")long themeId, @PathVariable("paperId") long paperId) {
-        PaperVO paperVO = themeLogic.createRelationBetweenThemeAndPaper(themeId, paperId);
-        return new ResponseVO<>(ServerCode.SUCCESS, paperVO);
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/exam/{examId}", method = RequestMethod.PUT)
-    @RequiresPermissions(value = "theme:create")
-    public ResponseVO addExams2theme(@PathVariable("themeId")long themeId, @PathVariable("examId") long examId) {
-        ExamVO examVO = themeLogic.createRelationBetweenThemeAndExam(themeId, examId);
-        return new ResponseVO<>(ServerCode.SUCCESS, examVO);
-    }
-    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/case/{caseId}", method = RequestMethod.DELETE)
-    @RequiresPermissions(value = "theme:delete")
-    public ResponseVO<Boolean> removeCaseFromtheme(@PathVariable("themeId")long themeId, @PathVariable("caseId") long caseId) {
-        boolean result = themeLogic.deleteRelationBetweenThemeAndCase(themeId,caseId);
-        return new ResponseVO<>(ServerCode.SUCCESS, result);
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/paper/{paperId}", method = RequestMethod.DELETE)
-    @RequiresPermissions(value = "theme:delete")
-    public ResponseVO<Boolean> removePaperFromtheme(@PathVariable("themeId")long themeId, @PathVariable("paperId") long paperId) {
-        boolean result = themeLogic.deleteRelationBetweenThemeAndPaper(themeId,paperId);
-        return new ResponseVO<>(ServerCode.SUCCESS, result);
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/exam/{examId}", method = RequestMethod.DELETE)
-    @RequiresPermissions(value = "theme:delete")
-    public ResponseVO<Boolean> removeExamFromtheme(@PathVariable("themeId")long themeId, @PathVariable("examId") long examId) {
-        boolean result = themeLogic.deleteRelationBetweenThemeAndExam(themeId, examId);
-        return new ResponseVO<>(ServerCode.SUCCESS, result);
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/case/{caseId}", method = RequestMethod.GET)
-    public ResponseVO<List<ThemeDetailVO>> getThemesHasTheCase(@PathVariable("caseId")long caseId) {
-        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getThemeDetailListByCase(caseId));
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/paper/{paperId}", method = RequestMethod.GET)
-    public ResponseVO<List<ThemeDetailVO>> getThemesHasThePaper(@PathVariable("paperId")long paperId) {
-        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getThemeDetailListByCase(paperId));
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/exam/{examId}", method = RequestMethod.GET)
-    public ResponseVO<List<ThemeDetailVO>> getThemesHasTheExam(@PathVariable("examId")long examId) {
-        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getThemeDetailListByCase(examId));
-    }
-
-    @RequestMapping(value = UrlConstants.API + "theme/courses", method = RequestMethod.GET)
-    public ResponseVO<List<CourseVO>> getCourses() {
-        List<CourseVO> courseVOList = themeLogic.getCourses();
-        return new ResponseVO<>(ServerCode.SUCCESS,courseVOList);
-    }
-
-    @Override
-    public Page<?> search(String searchCondition) {
-        return null;
-    }
-
     @RequestMapping(value = UrlConstants.API + "theme/buy/{themeId}", method = RequestMethod.GET)
     @RequiresPermissions(value = "theme:buy")
     public ResponseVO buyTheme(@PathVariable("themeId")long themeId) {
@@ -530,10 +498,14 @@ public class ThemeController extends BaseSearchController {
         return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getParticipantCourses(userId, extraCondition, keyword, pageable));
     }
 
+    /**
+     * 教师查看课程
+     * @param courseId
+     * @return
+     */
     @GetMapping(value = UrlConstants.API + "course/{courseId}")
     public ResponseVO<CourseVO> course(@PathVariable("courseId")Long courseId) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-//        CourseVO courseVO = themeLogic.getCourse(courseId, userId);
         CourseVO courseVO = themeLogic.getCourse(courseId, userId);
         if(courseVO.getThemeDetailVO().getType() != 0) {
             log.info("这个不是一个课程:{}, 访问用户为:{}",courseId ,userId);
@@ -542,6 +514,22 @@ public class ThemeController extends BaseSearchController {
         return new ResponseVO<>(ServerCode.SUCCESS, courseVO);
     }
 
+    /**
+     * 学生查看课程
+     * @param courseId
+     * @return
+     */
+    @GetMapping(value = UrlConstants.API + "student/course/{courseId}")
+    public ResponseVO<CourseVO> getCourseByStudent(@PathVariable("courseId")Long courseId) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        CourseVO courseVO = themeLogic.getCourseByStudent(courseId, userId);
+        if(courseVO.getThemeDetailVO().getType() != 0) {
+            log.info("这个不是一个课程:{}, 访问用户为:{}",courseId ,userId);
+            return new ResponseVO<>(ServerCode.ENTITY_NOT_A_COURSE,null);
+        }
+        return new ResponseVO<>(ServerCode.SUCCESS, courseVO);
+    }
+
     @GetMapping(value = UrlConstants.API + "course/{courseId}/resource/{resourceId}")
     public ResponseVO<CourseResourceVO> resource(@PathVariable("courseId")Long courseId,@PathVariable("resourceId")Long resourceId) {
         CourseResourceVO courseResourceVO = themeLogic.getCourseResource(courseId, resourceId);
@@ -576,5 +564,174 @@ public class ThemeController extends BaseSearchController {
         return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.joinCourse(courseId,userId));
     }
 
+    /**
+     * 管理员为教师授权课程-查看教师是否具有自定义课程的权限
+     * @param teacherId
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "manage/role/{teacherId}")
+    public ResponseVO<Boolean> judgeCourseTeacherRole( @PathVariable("teacherId") Long teacherId) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("赋予用户自定义课程教师权限,被授权用户ID为:{}, 授权用户ID为:{}",teacherId ,userId);
+        Boolean flag = themeLogic.checkCourseTeacherRole(teacherId,userId);
+        return new ResponseVO<>(ServerCode.SUCCESS, flag);
+    }
+
+    /**
+     * 管理员为教师授权课程-赋予自定义课程教师的角色
+     * @param teacherId
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "manage/authorised/role/{teacherId}")
+    public ResponseVO<Boolean> authorisedCourseTeacherRole( @PathVariable("teacherId") Long teacherId) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("赋予用户自定义课程教师权限,被授权用户ID为:{}, 授权用户ID为:{}",teacherId ,userId);
+        themeLogic.authorisedCourseTeacherRole(teacherId,userId);
+        return new ResponseVO<>(ServerCode.SUCCESS, true);
+    }
+
+    /**
+     * 管理员为教师授权课程-查找课程(公开的、已发布的、未删除的、公开课程)
+     * @param courseName
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "manage/course/search")
+    public ResponseVO<List<ThemeDetailVO>> searchCourse( @RequestParam(name = "courseName", required = false, defaultValue = "") String courseName ) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("查找课程名称:{},用户ID为:{}",courseName ,userId);
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.searchCourse(courseName,userId));
+    }
 
+    /**
+     * 管理员为教师授权课程-获取教师已授权的课程(包含自定义未发布的课程)
+     * @param teacherId
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "authorised/courses/{teacherId}")
+    public ResponseVO<List<ThemeDetailVO>> getTeacherAuthorisedCourses(@PathVariable(name = "teacherId") Long teacherId) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("userId: {}获取教师已授权课程,参数:{}",userId,teacherId);
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getTeacherAuthorisedCourses(teacherId));
+    }
+
+    /**
+     * 管理员为教师授权课程-授权课程
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @PostMapping(value = UrlConstants.API + "authorised/courses/{teacherId}")
+    public ResponseVO<Boolean> authorisedTeacherCourses(@PathVariable(name = "teacherId") Long teacherId, @RequestBody List<Long> courseIdList) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("userId: {}授权课程,参数:{}",userId,teacherId);
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.authorisedCoursesList(teacherId,courseIdList));
+    }
+
+    /**
+     * 管理员为教师授权课程-删除授权课程
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "delete/authorised/courses/{teacherId}/{courseId}")
+    public ResponseVO<Boolean> authorisedTeacherCourses(@PathVariable(name = "teacherId") Long teacherId, @PathVariable Long courseId) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("userId: {}删除授权课程,参数:{}",userId,teacherId);
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.deleteAuthorisedCourse(teacherId,courseId));
+    }
+
+    /**
+     * 管理员初始化章节的发布时间和发布状态
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "init/resource/release/all")
+    public ResponseVO<Boolean> initEntityRelationReleaseStatus() {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("userId: {}始化章节的发布时间和发布状态,参数:{}",userId);
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.initEntityRelationReleaseStatus(userId));
+    }
+
+    /**
+     * 管理员初始化章节的发布时间和发布状态
+     * @return
+     */
+    @RequiresPermissions("manager:update")
+    @GetMapping(value = UrlConstants.API + "init/resource/release/list")
+    public ResponseVO<Boolean> initEntityRelationReleaseStatus(@RequestParam(name = "themeIdList") String themeIdList) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("userId: {}始化章节的发布时间和发布状态,参数:{}",userId);
+        String[] ids = themeIdList.split("-");
+        List<Long> themeIdLists = Arrays.stream(ids).map(id -> Long.parseLong(id)).collect(Collectors.toList());
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.initEntityRelationListReleaseStatus(userId, themeIdLists));
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/case/{caseId}", method = RequestMethod.PUT)
+    @RequiresPermissions(value = "theme:create")
+    public ResponseVO<CaseExtendsVO> addCases2theme(@PathVariable("themeId")long themeId, @PathVariable("caseId") long caseId) {
+        CaseExtendsVO caseExtendsVO = themeLogic.createRelationBetweenThemeAndCase(themeId,caseId);
+        return new ResponseVO<>(ServerCode.SUCCESS, caseExtendsVO);
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/paper/{paperId}", method = RequestMethod.PUT)
+    @RequiresPermissions(value = "theme:create")
+    public ResponseVO addPapers2theme(@PathVariable("themeId")long themeId, @PathVariable("paperId") long paperId) {
+        PaperVO paperVO = themeLogic.createRelationBetweenThemeAndPaper(themeId, paperId);
+        return new ResponseVO<>(ServerCode.SUCCESS, paperVO);
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/exam/{examId}", method = RequestMethod.PUT)
+    @RequiresPermissions(value = "theme:create")
+    public ResponseVO addExams2theme(@PathVariable("themeId")long themeId, @PathVariable("examId") long examId) {
+        ExamVO examVO = themeLogic.createRelationBetweenThemeAndExam(themeId, examId);
+        return new ResponseVO<>(ServerCode.SUCCESS, examVO);
+    }
+    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/case/{caseId}", method = RequestMethod.DELETE)
+    @RequiresPermissions(value = "theme:delete")
+    public ResponseVO<Boolean> removeCaseFromtheme(@PathVariable("themeId")long themeId, @PathVariable("caseId") long caseId) {
+        boolean result = themeLogic.deleteRelationBetweenThemeAndCase(themeId,caseId);
+        return new ResponseVO<>(ServerCode.SUCCESS, result);
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/paper/{paperId}", method = RequestMethod.DELETE)
+    @RequiresPermissions(value = "theme:delete")
+    public ResponseVO<Boolean> removePaperFromtheme(@PathVariable("themeId")long themeId, @PathVariable("paperId") long paperId) {
+        boolean result = themeLogic.deleteRelationBetweenThemeAndPaper(themeId,paperId);
+        return new ResponseVO<>(ServerCode.SUCCESS, result);
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/{themeId}/exam/{examId}", method = RequestMethod.DELETE)
+    @RequiresPermissions(value = "theme:delete")
+    public ResponseVO<Boolean> removeExamFromtheme(@PathVariable("themeId")long themeId, @PathVariable("examId") long examId) {
+        boolean result = themeLogic.deleteRelationBetweenThemeAndExam(themeId, examId);
+        return new ResponseVO<>(ServerCode.SUCCESS, result);
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/case/{caseId}", method = RequestMethod.GET)
+    public ResponseVO<List<ThemeDetailVO>> getThemesHasTheCase(@PathVariable("caseId")long caseId) {
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getThemeDetailListByCase(caseId));
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/paper/{paperId}", method = RequestMethod.GET)
+    public ResponseVO<List<ThemeDetailVO>> getThemesHasThePaper(@PathVariable("paperId")long paperId) {
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getThemeDetailListByCase(paperId));
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/exam/{examId}", method = RequestMethod.GET)
+    public ResponseVO<List<ThemeDetailVO>> getThemesHasTheExam(@PathVariable("examId")long examId) {
+        return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getThemeDetailListByCase(examId));
+    }
+
+    @RequestMapping(value = UrlConstants.API + "theme/courses", method = RequestMethod.GET)
+    public ResponseVO<List<CourseVO>> getCourses() {
+        List<CourseVO> courseVOList = themeLogic.getCourses();
+        return new ResponseVO<>(ServerCode.SUCCESS,courseVOList);
+    }
+
+    @Override
+    public Page<?> search(String searchCondition) {
+        return null;
+    }
 }

+ 2 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseResourceVO.java

@@ -25,7 +25,8 @@ public class CourseResourceVO extends EntityVO {
     private int sliderImagesCount;
     private String sliderCover;
     private String description;
-    private Boolean previewable;
+    private boolean previewable;
     private int publicStatus;
     private int released;
+    private boolean canEdit;
 }

+ 2 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseVO.java

@@ -13,9 +13,8 @@ import lombok.Data;
 public class CourseVO {
     ThemeDetailVO themeDetailVO;
     List<EntityVO> entityVOList;
-    List<Long> completeCourseRecourseId;
-    List<Long> totalCourseRecourseId;
-    List<Long> totalCourseRecourseIdOne;
+    List<Long> completeCourseResourceId;
+    List<Long> totalCourseResourceId;
     List<Long> completeExamAndExerciseId;
     List<Long> totalExamAndExerciseId;
 }

+ 2 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/EntityVO.java

@@ -15,5 +15,6 @@ public class EntityVO {
     private long entityId;
     private EntityTypeEnum entityType;
     private int sortedId;
-    private boolean previewable;
+    private Long releasedTime;
+    private int releasedStatus;
 }

+ 22 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/StudentCourseResultVO.java

@@ -0,0 +1,22 @@
+package cn.iselab.mooctest.site.web.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-03-16 20:19
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class StudentCourseResultVO {
+    private UserVO userVO;
+    private List<Long> completeCourseResourceId;
+    private int completeCourseResourceCount;
+    private List<Long> totalCourseResourceId;
+    private int totalCourseResourceCount;
+}

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeEntityRelationsVO.java

@@ -16,5 +16,7 @@ public class ThemeEntityRelationsVO {
     private long entityId;
     private EntityTypeEnum entityType;
     private int sortId;
+    private Long releasedTime;
+    private int releasedStatus;
     private boolean previewable;
 }

+ 13 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ThemeEntityRelationsVOWrapper.java

@@ -7,6 +7,8 @@ import org.apache.poi.ss.formula.functions.T;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 
+import java.sql.Timestamp;
+
 /**
  * @author guochao
  * @date 2020-02-08 18:14
@@ -15,19 +17,28 @@ import org.springframework.stereotype.Service;
 public class ThemeEntityRelationsVOWrapper {
     public ThemeEntityRelations unWrapperThemeEntityRelations(ThemeEntityRelationsVO themeEntityRelationsVO){
         ThemeEntityRelations themeEntityRelations = new ThemeEntityRelations();
+        if(themeEntityRelationsVO.getReleasedTime() != null){
+            themeEntityRelations.setReleaseTime(new Timestamp(themeEntityRelationsVO.getReleasedTime()));
+        }
         BeanUtils.copyProperties(themeEntityRelationsVO, themeEntityRelations);
         return themeEntityRelations;
     }
 
     public ThemeEntityRelationsVO wrapperThemeEntityRelations(ThemeEntityRelations themeEntityRelations){
         ThemeEntityRelationsVO themeEntityRelationsVO = new ThemeEntityRelationsVO();
+        if(themeEntityRelations.getReleaseTime() != null){
+            themeEntityRelationsVO.setReleasedTime(themeEntityRelations.getReleaseTime().getTime());
+        }
         BeanUtils.copyProperties(themeEntityRelations, themeEntityRelationsVO);
         return themeEntityRelationsVO;
     }
 
-    public EntityVO wrapperEntityVO(ThemeEntityRelationsVO themeEntityRelationsVO){
+    public EntityVO wrapperEntityVO(ThemeEntityRelations themeEntityRelations){
         EntityVO entityVO = new EntityVO();
-        BeanUtils.copyProperties(themeEntityRelationsVO, entityVO);
+        if(themeEntityRelations.getReleaseTime() != null){
+            entityVO.setReleasedTime(themeEntityRelations.getReleaseTime().getTime());
+        }
+        BeanUtils.copyProperties(themeEntityRelations, entityVO);
         return entityVO;
     }
 }

+ 18 - 9
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ThemeVOWrapper.java

@@ -91,7 +91,7 @@ public class ThemeVOWrapper {
 
     public boolean varifyIfPurchase(long themeDetailId) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        return user2ThemeService.findByUserIdAndThemeId(userId, themeDetailId) != null || themeService.isUserJoinCourse(themeDetailId,userId);
+        return user2ThemeService.findAllByUserIdAndThemeId(userId, themeDetailId).size() > 0 || themeService.isUserJoinCourse(themeDetailId,userId);
     }
 
     public ThemeVO wrapper(Theme theme) {
@@ -133,6 +133,7 @@ public class ThemeVOWrapper {
                     .map(examVOWrapper::wrap2CourseExam)
                     .peek(courseExamVO -> {
                         ThemeEntityRelations themeEntityRelations = examRelationMap.get(courseExamVO.getId());
+                        courseExamVO.setThemeId(themeEntityRelations.getThemeId());
                         courseExamVO.setSortedId(themeEntityRelations.getSortId());
                         courseExamVO.setEntityId(themeEntityRelations.getEntityId());
                         courseExamVO.setEntityType(themeEntityRelations.getEntityType());
@@ -142,19 +143,27 @@ public class ThemeVOWrapper {
         if (theme.getCourseResourceList() != null) {
             List<CourseResourceVO> courseResourceVOs = theme.getCourseResourceList()
                     .stream().map(courseResourceVOWrapper::wrap)
-                    .peek(courseExamVO -> {
-                        ThemeEntityRelations themeEntityRelations = courseRelationMap.get(courseExamVO.getId());
-                        courseExamVO.setSortedId(themeEntityRelations.getSortId());
-                        courseExamVO.setEntityId(themeEntityRelations.getEntityId());
-                        courseExamVO.setEntityType(themeEntityRelations.getEntityType());
-                        courseExamVO.setPreviewable(themeEntityRelations.isPreviewable());
+                    .peek(courseResourceVO -> {
+                        ThemeEntityRelations themeEntityRelations = courseRelationMap.get(courseResourceVO.getId());
+                        courseResourceVO.setThemeId(themeEntityRelations.getThemeId());
+                        courseResourceVO.setSortedId(themeEntityRelations.getSortId());
+                        courseResourceVO.setEntityId(themeEntityRelations.getEntityId());
+                        courseResourceVO.setEntityType(themeEntityRelations.getEntityType());
+                        courseResourceVO.setPreviewable(themeEntityRelations.isPreviewable());
+                        courseResourceVO.setReleasedTime(themeEntityRelations.getReleaseTime().getTime());
+                        courseResourceVO.setReleasedStatus(themeEntityRelations.getReleaseStatus());
+                        if(courseResourceVO.getOwnerId().equals(userId)){
+                            courseResourceVO.setCanEdit(true);
+                        }else{
+                            courseResourceVO.setCanEdit(false);
+                        }
                     }).collect(Collectors.toList());
             entityVOS.addAll(courseResourceVOs);
         }
         entityVOS.sort(Comparator.comparingLong(EntityVO::getSortedId));
         courseVO.setEntityVOList(entityVOS);
-        courseVO.setTotalCourseRecourseId(entityRelations.stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
-        courseVO.setCompleteCourseRecourseId(themeService.getStudyCompletenessByThemeId(theme.getThemeDetail().getId(), userId, EntityTypeEnum.COURSE_RESOURCE)
+        courseVO.setTotalCourseResourceId(entityRelations.stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
+        courseVO.setCompleteCourseResourceId(themeService.getStudyCompletenessByThemeId(theme.getThemeDetail().getId(), userId, EntityTypeEnum.COURSE_RESOURCE)
                 .stream().map(StudyRecord::getEntityId).collect(Collectors.toList()));
         return courseVO;
     }

+ 33 - 6
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ThemeLogic.java

@@ -6,6 +6,7 @@ import cn.iselab.mooctest.site.models.ThemeDetail;
 import cn.iselab.mooctest.site.models.ThemeEntityRelations;
 import cn.iselab.mooctest.site.web.data.*;
 
+import java.sql.Timestamp;
 import java.util.List;
 import java.util.Map;
 
@@ -29,7 +30,7 @@ public interface ThemeLogic{
     //new a themeCourse
     CourseVO newThemeCourse(CourseVO courseVO, Long userId);
 
-    CourseVO forkThemeCourse(Long themeId, Long groupId);
+    CourseVO forkThemeCourse(ThemeDetailVO themeDetailVO);
 
     Page<ThemeDetailVO> getThemeDetailList(Pageable pageable);
 
@@ -41,7 +42,7 @@ public interface ThemeLogic{
 
     Page<ThemeDetailVO> getAuthorisedCourses(Pageable pageable, Map<String, String> extraCondition, String keyword);
 
-    Long[] getAuthorisedResourceList(String keyword);
+    Long[] getAuthorisedResourceList(Long themeId, String keyword);
 
     Page<ThemeDetailVO> getThemeList(Map<String, String> extraCondition, Map<String, String> excludeCondition, Pageable pageable, String keyword);
 
@@ -64,13 +65,15 @@ public interface ThemeLogic{
 
     CourseVO getCourse(Long id, Long userId);
 
+    CourseVO getCourseByStudent(Long id, Long userId);
+
     List<CourseVO>  getCoursesByThemeIds(Long[] themeIdListByCourse, int type);
 
     List<CourseVO> getCoursesByThemeIdList(List<Long> themeIdList, int type);
 
     List<CourseVO> getCourses();
 
-    ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable);
+//    ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable, Timestamp releasedTime);
 
     CaseExtendsVO createRelationBetweenThemeAndCase(long themeId, long caseId);
 
@@ -92,6 +95,8 @@ public interface ThemeLogic{
 
     ThemeInfoVO getThemeInfoByThemeId(Long themeId);
 
+    List<StudentCourseResultVO> getStudentThemeInfoByThemeId(Long themeId, String keyword);
+
     void createStudyRecord(Long themeId, Long courseId, Long userId);
 
     List<Theme2Group> getThemeListByGroup(Long groupId);
@@ -106,15 +111,37 @@ public interface ThemeLogic{
 
     CourseVO joinCourse(Long courseId, long userId);
 
-    CourseResourceVO createCourseRecourse(CourseResourceVO courseResourceVO);
+    CourseResourceVO createCourseResource(CourseResourceVO courseResourceVO);
 
     CourseResourceVO updateCourseResource(CourseResourceVO courseResourceVO);
 
     boolean deleteResource(Long themeId, Long entityId, String entityType);
 
-    ThemeEntityRelations addCourseRecourse(EntityVO entityVO);
+//    ThemeEntityRelations addCourseResource(EntityVO entityVO);
 
-    List<ThemeEntityRelations> addRecourseList(List<EntityVO> entityVOList);
+    List<CourseResourceVO> addRecourseList(List<EntityVO> entityVOList);
+
+    List<ThemeEntityRelationsVO> addExamRecourseList(List<EntityVO> entityVOList);
 
     ThemeEntityRelationsVO updateCourseResourcePreview(Long themeId, Long entityId);
+
+    ThemeEntityRelationsVO updateCourseResourceReleasedTime(Long themeId, Long entityId, Long releasedTime);
+
+    List<ThemeDetailVO> searchCourse(String courseName, Long userId);
+
+    List<ThemeDetailVO>  getTeacherAuthorisedCourses(Long teacherId);
+
+    Boolean authorisedCoursesList(Long teacherId, List<Long> courseIdList);
+
+    void authorisedCourseTeacherRole(Long teacherId, Long userId);
+
+    Boolean checkCourseTeacherRole(Long teacherId, Long userId);
+
+    Boolean deleteAuthorisedCourse(Long teacherId, Long courseId);
+
+    CourseVO orderThemeDetailEntity(Long themeId, List<EntityVO> entityVOList);
+
+    boolean initEntityRelationReleaseStatus(Long userId);
+
+    boolean initEntityRelationListReleaseStatus(Long userId, List<Long> themeIdList);
 }

+ 2 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ProductProcessLogicImpl.java

@@ -44,7 +44,8 @@ public class ProductProcessLogicImpl implements ProductProcessLogic {
                 JSONObject entity = entities.getJSONObject(i);
                 switch (entity.getString("entityType")){//根据不同Type进行不同处理
                     case "THEME":
-                        if (user2ThemeService.findByUserIdAndThemeId(userId, entity.getLong("entityId"))==null) {
+//                        if (user2ThemeService.findByUserIdAndThemeId(userId, entity.getLong("entityId"))==null) {
+                        if (user2ThemeService.findAllByUserIdAndThemeId(userId, entity.getLong("entityId")).size()==0) {
                             user2ThemeService.createOperationRelation(userId, entity.getLong("entityId"),"view");//关联不存在则建立关联
                             Theme2Group theme2Group = theme2GroupService.findByThemeId(entity.getLong("entityId"));
                             if (theme2Group != null && !groupService.isUserInGroup(userId, theme2Group.getGroupId()))

+ 549 - 216
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ThemeLogicImpl.java

@@ -1,7 +1,6 @@
 package cn.iselab.mooctest.site.web.logic.impl;
 
-import cn.iselab.mooctest.site.common.enums.CourseAuthorisedType;
-import cn.iselab.mooctest.site.common.enums.EntityType;
+import cn.iselab.mooctest.site.common.enums.CourseType;
 import cn.iselab.mooctest.site.common.enums.CourseVisibility;
 import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
 import cn.iselab.mooctest.site.models.*;
@@ -9,23 +8,18 @@ import cn.iselab.mooctest.site.service.*;
 import cn.iselab.mooctest.site.service.common.PdfService;
 import cn.iselab.mooctest.site.util.data.Converter;
 import cn.iselab.mooctest.site.web.data.*;
-import cn.iselab.mooctest.site.web.data.response.ResponseVO;
-import cn.iselab.mooctest.site.web.data.response.ServerCode;
 import cn.iselab.mooctest.site.web.data.wrapper.*;
 import cn.iselab.mooctest.site.web.exception.HttpForbiddenException;
 import cn.iselab.mooctest.site.web.exception.HttpNotFoundException;
 import cn.iselab.mooctest.site.web.exception.HttpBadRequestException;
-import cn.iselab.mooctest.site.web.exception.HttpNotFoundException;
 import cn.iselab.mooctest.site.web.logic.OSSLogic;
 import cn.iselab.mooctest.site.web.logic.ThemeLogic;
 import com.google.common.collect.Lists;
+import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.crypto.hash.Hash;
-import org.drools.core.time.SchedulerService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -34,10 +28,10 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.sql.Array;
 import java.sql.Timestamp;
 import java.util.*;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * @program: mooctest-site
@@ -69,6 +63,10 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Autowired
     private ThemeSchedulerService themeSchedulerService;
     @Autowired
+    private ThemeEntityRelationsSchedulerService themeEntityRelationsSchedulerService;
+    @Autowired
+    private ThemeEntityRelationsStatusService themeEntityRelationsStatusService;
+    @Autowired
     private ThemeStatusService themeStatusService;
     @Autowired
     private PdfService pdfService;
@@ -81,6 +79,12 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Autowired
     SubmitRecordService submitRecordService;
     @Autowired
+    private User2RoleService user2RoleService;
+    @Autowired
+    private OperationRecourseService operationRecourseService;
+    @Autowired
+    private RoleService roleService;
+    @Autowired
     private OSSLogic ossLogic;
     @Autowired
     private CourseResourceService courseResourceService;
@@ -91,6 +95,8 @@ public class ThemeLogicImpl implements ThemeLogic {
     private ThemeEntityRelationsVOWrapper themeEntityRelationsVOWrapper;
     @Autowired
     private GroupVOWrapper groupVOWrapper;
+    @Autowired
+    private UserVOWrapper userVOWrapper;
 
 
     private static final String BASE_DIR = "theme";
@@ -107,8 +113,42 @@ public class ThemeLogicImpl implements ThemeLogic {
 
     @Override
     public List<GroupVO> getForkCourseGroups(long themeId) {
-        Theme2Group theme2Group = theme2GroupService.findByThemeId(themeId);
+        // 判断对被fork课程的使用权限
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        List<User2Theme> user2ThemeList = user2ThemeService.findAllByUserIdAndThemeId(userId, themeId);
+        // 使用权限要为* 或者 use
+        List<User2Theme> user2Themes = user2ThemeList.stream().filter(user2Theme -> user2Theme.getOperation() != User2Theme.AUTH_VIEW).collect(Collectors.toList());
+        if(user2Themes.size() <= 0){
+            throw new HttpBadRequestException("fork失败,当前用户并无对此课程的使用权限!");
+        }
+        // 判断对被fork课程的章节是否是 已授权的非自定义课程中的章节
+        // 获取被fork课程的所有章节 -> 所有公开的章节
+        List<Long> courseResourceIdList = themeService.getThemeEntityRelations(themeId, EntityTypeEnum.COURSE_RESOURCE).stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList());
+        boolean canFork = true;
+        String message = "";
+        // 如果存在章节则需要判断
+        if(courseResourceIdList.size() > 0){
+            List<Long> publicResourceIdList = courseResourceService.getCourseResourceById(courseResourceIdList).stream()
+                    .filter(resource -> resource.getPublicStatus() == CourseResource.IS_PUBLIC)
+                    .map(CourseResource::getId).collect(Collectors.toList());
+            // 如果存在公开的章节则需要判断
+            if(publicResourceIdList.size() > 0){
+                // 根据这些公开章节获取所有已使用当前章节的 公开课程
+                for(Long resourceId : publicResourceIdList){
+                    List<OperationResource> operationRecourseList = operationRecourseService.getOperationResourceList(userId, User2Theme.AUTH_VIEW, resourceId, CourseResource.NOT_Deleted, CourseResource.IS_PUBLIC, ThemeDetail.IS_PUBLIC, ThemeDetail.COURSE, ThemeDetail.NOT_DELETED);
+                    if(operationRecourseList.size() <= 0){
+                        canFork = false;
+                        message += " \"" + courseResourceService.getCourseResourceByResourceId(resourceId).getTitle() + "\"";
+                    }
+                }
+            }
+        }
+        if(!canFork){
+            throw new HttpBadRequestException("复制课程失败,被复制的课程中含有未授权的课程中的章节:" + message + "!");
+        }
+
+        // 允许被fork
+        Theme2Group theme2Group = theme2GroupService.findByThemeId(themeId);
         List<Group> groups = groupService.getGroupsByOwnerId(userId);
         List<GroupVO> groupVOs = groupVOWrapper.wrap(groups);
         return groupVOs.stream().filter(groupVO -> !groupVO.getId().equals(theme2Group.getGroupId())).collect(Collectors.toList());
@@ -132,80 +172,33 @@ public class ThemeLogicImpl implements ThemeLogic {
         if(!admin && themeDetailVO.getVisibility() != CourseVisibility.NONE_OPEN.getVisibility()){
             throw new HttpBadRequestException("当前用户只能新建自定义课程");
         }
+        // 设置是否公开
+        if(admin && themeDetailVO.getVisibility() != CourseVisibility.NONE_OPEN.getVisibility()){
+            themeDetailVO.setPublicStatus(ThemeDetail.IS_PUBLIC);
+        }else{
+            themeDetailVO.setPublicStatus(ThemeDetail.IS_PRIVATE);
+        }
         // 检验课程信息
         checkThemeInfo(themeDetailVO);
         if (themeDetailVO.getGroupId() != null && groupService.getGroupsByOwnerId(themeDetailVO.getOwnerId()).stream()
                 .map(Group::getId).collect(Collectors.toList()).contains(themeDetailVO.getGroupId())) {
-            // 课程中的实体信息(课程信息、考试信息、练习信息) 可否为空
-            List<EntityVO> entityVOList = courseVO.getEntityVOList();
-            if(entityVOList.size() != 0){
-//                if(entityVOList.size() > 40) throw new HttpBadRequestException("课程内容(章节、练习、考试)总共不能超过40个");
-                checkThemeCourseGroupAndExam(themeDetailVO, entityVOList);
-                // 条件符合,可以创建
-                ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
-                themeDetail.setReleased(ThemeDetail.UNRELEASED);
-                themeDetail = themeService.createThemeDetail(themeDetail);
-
-                themeSchedulerService.createNewThemeScheduler(themeDetail);
-
-                themeDetailVO.setId(themeDetail.getId());
-                theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
-                user2ThemeService.createOperationRelation(userId,themeDetail.getId(),"*");
-                int index = 0;
-                for(EntityVO entityVO: entityVOList){
-                    index++;
-                    entityVO.setThemeId(themeDetailVO.getId());
-                    ThemeEntityRelationsVO entityRelation = createEntityRelation(themeDetailVO.getId(), entityVO.getEntityId(), entityVO.getEntityType(), index, Boolean.TRUE);
-                    entityVO.setSortedId(entityRelation.getSortId());
-                }
-                courseVO.setThemeDetailVO(themeVOWrapper.wrapperThemeDetail(themeDetail));
-
-            }else{
-                // 条件符合,可以创建
-                ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
-                themeDetail.setReleased(ThemeDetail.UNRELEASED);
-                themeDetail = themeService.createThemeDetail(themeDetail);
-                themeSchedulerService.createNewThemeScheduler(themeDetail);
-                themeDetailVO.setId(themeDetail.getId());
-                theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
-                user2ThemeService.createOperationRelation(userId,themeDetail.getId(),"*");
-                courseVO.setThemeDetailVO(themeVOWrapper.wrapperThemeDetail(themeDetail));
-            }
+            // 条件符合,可以创建
+            ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
+            themeDetail.setReleased(ThemeDetail.UNRELEASED);
+            themeDetail = themeService.createThemeDetail(themeDetail);
+            themeSchedulerService.createNewThemeScheduler(themeDetail);
+            themeDetailVO.setId(themeDetail.getId());
+            theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
+            user2ThemeService.createOperationRelation(userId,themeDetail.getId(),User2Theme.AUTH_ALL);
+            ThemeDetailVO themeDetailVOResult = themeVOWrapper.wrapperThemeDetail(themeDetail);
+            themeDetailVOResult.setCanEdit(true);
+            courseVO.setThemeDetailVO(themeDetailVOResult);
         } else {
             throw new HttpBadRequestException("No right for some group");
         }
         return courseVO;
     }
 
-    @Override
-    public CourseVO forkThemeCourse(Long themeId, Long groupId) {
-        CourseVO courseVO = new CourseVO();
-        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        // fork course info
-        ThemeDetailVO themeDetailVO = getThemeDetailByThemeId(themeId);
-        themeDetailVO.setOwnerId(userId);
-        ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
-        themeDetail.setId(null);
-        themeDetail.setReleased(ThemeDetail.UNRELEASED);
-        themeDetail = themeService.createThemeDetail(themeDetail);
-        themeSchedulerService.createNewThemeScheduler(themeDetail);
-        theme2GroupService.create(groupId, themeDetail.getId());
-        user2ThemeService.createOperationRelation(userId, themeDetail.getId(),"*");
-        courseVO.setThemeDetailVO(themeVOWrapper.wrapperThemeDetail(themeDetail));
-        // fork course resource
-        List<EntityVO> entityVOS = new ArrayList<>();
-        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(themeId);
-        List<ThemeEntityRelations> resourceEntityRelations = themeEntityRelations.stream().filter(entityRelation -> entityRelation.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).collect(Collectors.toList());
-        if(resourceEntityRelations.size() != 0){
-            for(ThemeEntityRelations resourceEntity: resourceEntityRelations){
-                ThemeEntityRelationsVO entityRelation = createEntityRelation(themeDetail.getId(), resourceEntity.getEntityId(), resourceEntity.getEntityType(), resourceEntity.getSortId(), resourceEntity.isPreviewable());
-                entityVOS.add(themeEntityRelationsVOWrapper.wrapperEntityVO(entityRelation));
-            }
-        }
-        courseVO.setEntityVOList(entityVOS);
-        return courseVO;
-    }
-
     private void checkThemeInfo(ThemeDetailVO themeDetailVO) {
         // 检查主题信息
 //        long currTime = System.currentTimeMillis();
@@ -231,6 +224,10 @@ public class ThemeLogicImpl implements ThemeLogic {
     public CourseVO updateThemeCourse(CourseVO courseVO, Long userId) {
         // 主题信息
         ThemeDetailVO themeDetailVO = courseVO.getThemeDetailVO();
+        // 判断是否有查看当前课程的权限
+        checkCourseViewAuth(userId, themeDetailVO);
+        // 判断是否具有编辑权限
+        checkEditCourseAuth(themeDetailVO.getId());
         if (!themeDetailVO.getOwnerId().equals(userId)) throw new HttpBadRequestException("当前用户无修改此课程的权限");
         if (themeDetailVO == null) throw new HttpBadRequestException("主题内容不可为空");
 //        long currTime = System.currentTimeMillis();
@@ -238,14 +235,19 @@ public class ThemeLogicImpl implements ThemeLogic {
 //        if (isThemeTimeInvalid) {
 //            throw new HttpBadRequestException("主题课程时间无效");
 //        }
+        boolean admin = SecurityUtils.getSubject().hasRole("admin");
+        // 设置是否公开
+        if(admin && themeDetailVO.getVisibility() != CourseVisibility.NONE_OPEN.getVisibility()){
+            themeDetailVO.setPublicStatus(ThemeDetail.IS_PUBLIC);
+        }else{
+            themeDetailVO.setPublicStatus(ThemeDetail.IS_PRIVATE);
+        }
 
         // 课程中的实体信息(课程信息、考试信息、练习信息)
         List<EntityVO> entityVOList = courseVO.getEntityVOList();
 
         if(entityVOList.size() != 0){
 //            if(entityVOList.size() > 40) throw new HttpBadRequestException("课程内容(章节、练习、考试)总共不能超过40个");
-            // 删除原有的实体
-            deleteEntityRelationByThemeId(themeDetailVO.getId());
             checkThemeCourseGroupAndExam(themeDetailVO, entityVOList);
             ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
             Integer updateStatus = themeStatusService.updateStatus(themeDetail);
@@ -258,11 +260,12 @@ public class ThemeLogicImpl implements ThemeLogic {
             int index = 0;
             for(EntityVO entityVO: entityVOList){
                 index++;
-                entityVO.setThemeId(themeDetailVO.getId());
-                ThemeEntityRelationsVO entityRelation = createEntityRelation(themeDetailVO.getId(), entityVO.getEntityId(), entityVO.getEntityType(), index, Boolean.TRUE);
-                entityVO.setSortedId(entityRelation.getSortId());
+                ThemeEntityRelations entityRelations = themeService.getThemeEntityRelationsByThemeIdAndEntityIdAndEntityType(themeDetail.getId(), entityVO.getEntityId(), entityVO.getEntityType());
+                entityRelations.setSortId(index);
+                themeService.updateEntityRelation(entityRelations);
             }
-            themeDetail.setReleased(getThemeDetailByThemeId(themeDetailVO.getId()).getReleased());
+
+            themeDetail.setReleased(getThemeDetailByThemeId(themeDetail.getId()).getReleased());
             courseVO.setThemeDetailVO(themeVOWrapper.wrapperThemeDetail(themeDetail));
         }else{
             ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
@@ -277,6 +280,68 @@ public class ThemeLogicImpl implements ThemeLogic {
         return courseVO;
     }
 
+    private void checkCourseViewAuth(Long userId, ThemeDetailVO themeDetailVO) {
+        List<User2Theme> user2ThemeList = user2ThemeService.findAllByUserIdAndThemeId(userId, themeDetailVO.getId());
+        if(user2ThemeList.size() == 0){
+            throw new HttpForbiddenException("当前用户没有查看此课程的权限");
+        }
+        themeDetailVO.setCanEdit(false);
+        for (User2Theme user2Theme : user2ThemeList){
+            if(user2Theme.getOperation().equals(User2Theme.AUTH_ALL)){
+                themeDetailVO.setCanEdit(true);
+                break;
+            }
+        }
+    }
+
+    @Override
+    public CourseVO forkThemeCourse(ThemeDetailVO forkThemeDetailVO) {
+        // 检验课程信息
+        checkThemeInfo(forkThemeDetailVO);
+        CourseVO courseVO = new CourseVO();
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        // fork course info
+        ThemeDetailVO themeDetailVO = getThemeDetailByThemeId(forkThemeDetailVO.getId());
+        ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
+        if(SecurityUtils.getSubject().hasRole("admin")){
+            themeDetail.setVisibility(forkThemeDetailVO.getVisibility());
+        }else{
+            themeDetail.setVisibility(CourseVisibility.NONE_OPEN.getVisibility());
+        }
+        if(themeDetail.getVisibility() == CourseVisibility.NONE_OPEN.getVisibility()){
+            themeDetail.setPublicStatus(ThemeDetail.IS_PRIVATE);
+        }
+        themeDetail.setId(null);
+        themeDetail.setOwnerId(userId);
+        themeDetail.setTitle(forkThemeDetailVO.getTitle());
+        themeDetail.setBeginTime(new Timestamp(forkThemeDetailVO.getBeginTime()));
+        themeDetail.setEndTime(new Timestamp(forkThemeDetailVO.getEndTime()));
+        themeDetail.setCourseType(CourseType.CUSTOM.getType());
+        themeDetail.setReleased(ThemeDetail.UNRELEASED);
+        Integer updateStatusCourse = themeStatusService.updateStatus(themeDetail);
+        themeDetail.setStatus(updateStatusCourse);
+        themeDetail = themeService.createThemeDetail(themeDetail);
+        themeSchedulerService.createNewThemeScheduler(themeDetail);
+        theme2GroupService.create(forkThemeDetailVO.getGroupId(), themeDetail.getId());
+        user2ThemeService.createOperationRelation(userId, themeDetail.getId(),User2Theme.AUTH_ALL);
+        courseVO.setThemeDetailVO(themeVOWrapper.wrapperThemeDetail(themeDetail));
+        // fork course resource
+        List<EntityVO> entityVOS = new ArrayList<>();
+        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(forkThemeDetailVO.getId());
+        List<ThemeEntityRelations> resourceEntityRelations = themeEntityRelations.stream().filter(entityRelation -> entityRelation.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).collect(Collectors.toList());
+        if(resourceEntityRelations.size() != 0){
+            Integer updateStatus = themeEntityRelationsStatusService.updateStatusByTime(themeDetail.getBeginTime().getTime());
+            for(ThemeEntityRelations relation: resourceEntityRelations){
+                ThemeEntityRelations entityRelation = null;
+                entityRelation = themeService.createEntityRelation(themeDetail.getId(),relation.getEntityId(),relation.getEntityType(),relation.getSortId(),relation.isPreviewable(),themeDetail.getBeginTime(),updateStatus);
+                themeEntityRelationsSchedulerService.createEntityRelationScheduler(entityRelation);
+                entityVOS.add(themeEntityRelationsVOWrapper.wrapperEntityVO(entityRelation));
+            }
+        }
+        courseVO.setEntityVOList(entityVOS);
+        return courseVO;
+    }
+
     @Override
     public ThemeDetail releaseThemeCourse(Long themeId) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
@@ -341,7 +406,7 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Override
     public Page<ThemeDetailVO> getAuthorisedCourses(Pageable pageable, Map<String, String> extraCondition, String keyword) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        Page<OperationCourse> operationCourseList = themeService.getAuthorizedCourseList(pageable, userId, null, "view", extraCondition, keyword);
+        Page<OperationCourse> operationCourseList = themeService.getAuthorizedCourseList(pageable, userId, null, User2Theme.AUTH_VIEW, extraCondition, keyword);
         return operationCourseList.map(operationCourse -> {
             ThemeDetail convert = Converter.convert(ThemeDetail.class, operationCourse);
             return themeVOWrapper.wrapperThemeDetail(convert);
@@ -350,42 +415,40 @@ public class ThemeLogicImpl implements ThemeLogic {
 
     // 获取当前教师用户已授权的与keyword有关的资源列表(通过课程展示)
     @Override
-    public Long[] getAuthorisedResourceList(String keyword) {
+    public Long[] getAuthorisedResourceList(Long themeId, String keyword) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        // 获取所有已授权的课程
-        List<OperationCourse> operationCourseList = themeService.getOperationCourseList(userId, "view");
+        ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+
+        // 获取所有已授权的课程(除了当前课程)
+        List<OperationCourse> operationCourseList = themeService.getOperationCourseList(userId, User2Theme.AUTH_VIEW).stream()
+                .filter(operationCourse -> !operationCourse.getId().equals(themeDetail.getId())).collect(Collectors.toList());
+
+        if(themeDetail.getPublicStatus() == ThemeDetail.IS_PUBLIC){
+            operationCourseList = operationCourseList.stream().filter(operationCourse -> operationCourse.getPublicStatus() == ThemeDetail.IS_PUBLIC).collect(Collectors.toList());
+        }
         // 获取包含关键字的课程章节对应的已授权课程
         List<List<ThemeEntityRelations>> relationsLists = operationCourseList.stream().map(operationCourse -> themeService.getThemeEntityRelations(operationCourse.getId())).collect(Collectors.toList());
         Set<Long> themeIdSet = new HashSet<>();
         for(List<ThemeEntityRelations> relations : relationsLists){
+            relations = relations.stream().filter(relationType -> relationType.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).collect(Collectors.toList());
             if(relations.size() > 0){
                 for(ThemeEntityRelations relation : relations) {
-//                    System.out.println(relation.getId());
                     if(relation != null && relation.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE) && courseResourceService.getCourseResourceByResourceId(relation.getEntityId()).getTitle().contains(keyword)){
                         themeIdSet.add(relation.getThemeId());
                     }
-//                    if(relation != null) {
-//                        if (relation.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)) {
-//                            System.out.println(relation.getId() + "  " + relation.getEntityId());
-//                            CourseResource courseResourceByResourceId = courseResourceService.getCourseResourceByResourceId(relation.getEntityId());
-//                            if (courseResourceByResourceId != null && courseResourceByResourceId.getTitle().contains(keyword)) {
-//                                themeIdSet.add(relation.getThemeId());
-//                            }
-//                        }
-//                    }
                 }
             }
         }
         // 获取包含关键字的已授权课程
         operationCourseList.stream().filter(operationCourse -> operationCourse.getTitle().contains(keyword)).map(operationCourse -> themeIdSet.add(operationCourse.getId())).collect(Collectors.toList());
-        Long[] themeIdArray = Arrays.stream(themeIdSet.toArray()).map(themeId -> Long.valueOf(themeId.toString())).toArray(Long[]::new);
+        Long[] themeIdArray = Arrays.stream(themeIdSet.toArray()).map(themeDetailId -> Long.valueOf(themeDetailId.toString())).toArray(Long[]::new);
         return themeIdArray;
     }
 
     @Override
     public Page<ThemeDetailVO> getCustomizeCourses(Pageable pageable, String keyword) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        Page<OperationCourse> operationCourseList = themeService.getCustomizeCourseList(pageable, userId, userId, "*", null, keyword);
+        Page<OperationCourse> operationCourseList = themeService.getCustomizeCourseList(pageable, userId, userId, User2Theme.AUTH_ALL, null, keyword);
         return operationCourseList.map(operationCourse -> {
             ThemeDetail convert = Converter.convert(ThemeDetail.class, operationCourse);
             return themeVOWrapper.wrapperThemeDetail(convert);
@@ -484,6 +547,12 @@ public class ThemeLogicImpl implements ThemeLogic {
 
     @Override
     public CourseVO getCourse(Long id, Long userId) {
+        // 判断教师是否具有查看查看当前课程的权限
+        List<User2Theme> user2ThemeList = user2ThemeService.findAllByUserIdAndThemeId(userId, id);
+        if(user2ThemeList.size() == 0){
+            throw new HttpForbiddenException("当前用户没有查看此课程的权限");
+        }
+
         ThemeDetail themeDetail = themeService.getThemeDetailById(id);
         userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
         // 对于自定义课程  检查用户查看权限
@@ -495,13 +564,27 @@ public class ThemeLogicImpl implements ThemeLogic {
             }
         }
 
+        // 获取课程所有信息 课程信息+实体信息
+        CourseVO courseVO = getCourseVO(themeDetail);
+
+        // 判断是否有权限编辑课程
+        courseVO.getThemeDetailVO().setCanEdit(false);
+        for (User2Theme user2Theme : user2ThemeList){
+            if(user2Theme.getOperation().equals(User2Theme.AUTH_ALL)){
+                courseVO.getThemeDetailVO().setCanEdit(true);
+                break;
+            }
+        }
+        return courseVO;
+    }
+
+    private CourseVO getCourseVO(ThemeDetail themeDetail) {
         //根据id查询,然后过滤存map
-        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(id);
+        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(themeDetail.getId());
         Theme theme = new Theme();
         theme.setThemeDetail(themeDetail);
         Map<EntityTypeEnum, List<ThemeEntityRelations>> map = themeEntityRelations.stream().collect(Collectors.groupingBy(ThemeEntityRelations::getEntityType));
-        for (EntityTypeEnum key: map.keySet()) {
-            List<ThemeEntityRelations> tmp = map.get(key);
+        for (EntityTypeEnum key : map.keySet()) {
             if (key == EntityTypeEnum.EXAM) {
                 List<Exam> examList = examService.getTasks(map.get(key).stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
                 theme.setExamList(examList);
@@ -512,22 +595,19 @@ public class ThemeLogicImpl implements ThemeLogic {
             }
         }
         CourseVO courseVO = themeVOWrapper.wrapTheme2CourseVO(theme, themeEntityRelations);
-        User2Theme user2Theme = user2ThemeService.findByUserIdAndThemeId(userId, id);
-        // 判断是否有权限编辑课程
-        if(user2Theme == null || !user2Theme.getOperation().equals("*")){
-            courseVO.getThemeDetailVO().setCanEdit(false);
-        }else{
-            courseVO.getThemeDetailVO().setCanEdit(true);
-        }
+        return courseVO;
+    }
+
+    @Override
+    public CourseVO getCourseByStudent(Long id, Long userId) {
+        ThemeDetail themeDetail = themeService.getThemeDetailById(id);
+        //根据id查询,然后过滤存map
+        CourseVO courseVO = getCourseVO(themeDetail);
 
         // 获取课程中的实体数量
         List<ThemeEntityRelations> entityRelationsList = themeService.getThemeEntityRelations(id);
-//        if(entityRelationsList.size() != 0){
-//            courseVO.setCompleteCourseRecourseId(studyCompletenessByThemeId.stream().map(StudyRecord::getEntityId).collect(Collectors.toList()));
-//        }else{
-//
-//        }
         // 获取课程中的考试练习实体
+        courseVO.setCompleteExamAndExerciseId(null);
         List<ThemeEntityRelations> ExamRelationsList = entityRelationsList.stream().filter(entityRelations -> entityRelations.getEntityType() == EntityTypeEnum.EXAM).collect(Collectors.toList());
         if(ExamRelationsList.size() != 0){
             List<Long> examIdList = ExamRelationsList.stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList());
@@ -535,11 +615,7 @@ public class ThemeLogicImpl implements ThemeLogic {
             List<SubmitRecord> submitRecordList = submitRecordService.getSubmitScoreList(userId, examIdList);
             if(submitRecordList.size() != 0){
                 courseVO.setCompleteExamAndExerciseId(submitRecordList.stream().map(SubmitRecord::getExamId).collect(Collectors.toList()));
-            }else{
-                courseVO.setCompleteExamAndExerciseId(null);
             }
-        }else{
-            courseVO.setCompleteExamAndExerciseId(null);
         }
 
         // 获取课程下的所有考试和练习id
@@ -547,20 +623,19 @@ public class ThemeLogicImpl implements ThemeLogic {
         courseVO.setTotalExamAndExerciseId(examIdList);
 
         // 获取课程下所有章节的id
-        courseVO.setTotalCourseRecourseId(entityRelationsList.stream().filter(entityRelations -> entityRelations.getEntityType() == EntityTypeEnum.COURSE_RESOURCE).map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
-        List<Long> totalCourseRecourseIds = new ArrayList<>(courseVO.getTotalCourseRecourseId());
+        courseVO.setTotalCourseResourceId(entityRelationsList.stream().filter(entityRelations -> entityRelations.getEntityType() == EntityTypeEnum.COURSE_RESOURCE).map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
+        List<Long> totalCourseResourceIds = new ArrayList<>(courseVO.getTotalCourseResourceId());
 
         // 获取当前学生课程所有章节学习记录(可能有已删除的章节的记录)
         List<StudyRecord> studyCompletenessByThemeId = themeService.getStudyCompletenessByThemeId(id, userId, EntityTypeEnum.COURSE_RESOURCE);
         List<Long> studyCompletenessEntityId = studyCompletenessByThemeId.stream().map(StudyRecord::getEntityId).collect(Collectors.toList());
 
         // 去重 获取当前学生课程有效的已完成的章节学习记录
-        List<Long> courseRecourseIdRest = new ArrayList<>(totalCourseRecourseIds);
-        List<Long> courseRecourseIdCommon = new ArrayList<>(totalCourseRecourseIds);
-        courseRecourseIdRest.removeAll(studyCompletenessEntityId);
-        courseRecourseIdCommon.removeAll(courseRecourseIdRest);
-        courseVO.setCompleteCourseRecourseId(courseRecourseIdCommon);
-
+        List<Long> courseResourceIdRest = new ArrayList<>(totalCourseResourceIds);
+        List<Long> courseResourceIdCommon = new ArrayList<>(totalCourseResourceIds);
+        courseResourceIdRest.removeAll(studyCompletenessEntityId);
+        courseResourceIdCommon.removeAll(courseResourceIdRest);
+        courseVO.setCompleteCourseResourceId(courseResourceIdCommon);
         return courseVO;
     }
 
@@ -603,7 +678,7 @@ public class ThemeLogicImpl implements ThemeLogic {
             themeDetailList = themeDetailList.stream().filter(themeDetail -> themeDetail.getCourseType() == type).collect(Collectors.toList());
         }
         List<CourseVO> courseVOS = themeDetailList.stream().map(themeDetail -> getCourseOfCourseResource(themeDetail.getId())).collect(Collectors.toList());
-        return courseVOS.stream().filter(courseVO -> courseVO.getEntityVOList() != null).peek(courseVO -> courseVO.getThemeDetailVO().setEntityVOList(courseVO.getEntityVOList())).collect(Collectors.toList());
+        return courseVOS.stream().filter(courseVO -> courseVO.getEntityVOList().size() != 0).peek(courseVO -> courseVO.getThemeDetailVO().setEntityVOList(courseVO.getEntityVOList())).collect(Collectors.toList());
     }
 
     @Override
@@ -625,10 +700,6 @@ public class ThemeLogicImpl implements ThemeLogic {
         Map<EntityTypeEnum, List<ThemeEntityRelations>> map = themeEntityRelations.stream().collect(Collectors.groupingBy(ThemeEntityRelations::getEntityType));
         for (EntityTypeEnum key: map.keySet()) {
             List<ThemeEntityRelations> tmp = map.get(key);
-//            if (key == EntityTypeEnum.EXAM) {
-//                List<Exam> examList = examService.getTasks(map.get(key).stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
-//                theme.setExamList(examList);
-//            }
             if (key == EntityTypeEnum.COURSE_RESOURCE) {
                 List<Long> courseIds = map.get(key).stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList());
                 List<CourseResource> courseResourceList = courseResourceService.getCourseResourceById(courseIds);
@@ -638,19 +709,6 @@ public class ThemeLogicImpl implements ThemeLogic {
         return themeVOWrapper.wrapTheme2CourseVO(theme,themeEntityRelations);
     }
 
-    /**
-     * 创建主题相关的实体
-     * @param themeId
-     * @param entityId
-     * @param entityType
-     * @return
-     */
-    @Override
-    public ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId, boolean previewable) {
-        ThemeEntityRelations themeEntityRelations = themeService.createEntityRelation(themeId, entityId, entityType, sortId, previewable);
-        return themeEntityRelationsVOWrapper.wrapperThemeEntityRelations(themeEntityRelations);
-    }
-
     @Override
     public CaseExtendsVO createRelationBetweenThemeAndCase(long themeId, long caseId) {
         ThemeEntityRelations themeEntityRelations = themeService.createRelationBetweenThemeAndEntity(themeId,caseId,EntityTypeEnum.CASE);
@@ -713,7 +771,8 @@ public class ThemeLogicImpl implements ThemeLogic {
 
     @Override
     public void buyTheme(long userId, long themeId) {
-        if(user2ThemeService.findByUserIdAndThemeId(userId,themeId)==null)
+//        if(user2ThemeService.findByUserIdAndThemeId(userId,themeId) == null)
+        if(user2ThemeService.findAllByUserIdAndThemeId(userId,themeId).size()==0)
             user2ThemeService.createRelation(userId,themeId);
     }
 
@@ -764,36 +823,61 @@ public class ThemeLogicImpl implements ThemeLogic {
         if (groupService.isUserInGroup(userId, theme2Group.getGroupId()))
             throw new HttpBadRequestException("请勿重复加入");
         groupService.addUserIntoGroup(userId, theme2Group.getGroupId());
-        user2ThemeService.createOperationRelation(userId, courseId, "view");
+        user2ThemeService.createOperationRelation(userId, courseId, User2Theme.AUTH_VIEW);
         return this.getCourse(courseId);
     }
 
     @Override
-    public CourseResourceVO createCourseRecourse(CourseResourceVO courseResourceVO) {
+    public CourseResourceVO createCourseResource(CourseResourceVO courseResourceVO) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        checkCourseResource(courseResourceVO);
+
+        // 检验章节信息
+        checkCourseResource(userId, courseResourceVO);
+        // 根据课程的公开类型 设置章节的公开类型
+        ThemeDetail themeDetail = themeService.getThemeDetailById(courseResourceVO.getThemeId());
+        courseResourceVO.setPublicStatus(themeDetail.getPublicStatus());
+        // 根据课程的公开类型 设置章节的默认预览状态
+        if(themeDetail.getPublicStatus() == ThemeDetail.IS_PUBLIC){
+            courseResourceVO.setPreviewable(false);
+        }else{
+            courseResourceVO.setPreviewable(true);
+        }
         courseResourceVO.setOwnerId(userId);
-        courseResourceVO.setPublicStatus(CourseResource.IS_PRIVATE);
-        CourseResource courseRecourse = courseResourceService.createCourseRecourse(courseResourceVOWrapper.unwrap(courseResourceVO));
-        ThemeEntityRelations entityRelation = themeService.createEntityRelation(courseResourceVO.getThemeId(), courseRecourse.getId(), EntityTypeEnum.COURSE_RESOURCE, 0, Boolean.TRUE);
-        CourseResourceVO wrapResult = courseResourceVOWrapper.wrap(courseRecourse);
+        CourseResource courseResource = courseResourceService.createCourseResource(courseResourceVOWrapper.unwrap(courseResourceVO));
+        Timestamp releasedTime = themeDetail.getBeginTime();
+        Integer updateStatus = themeEntityRelationsStatusService.updateStatusByTime(releasedTime.getTime());
+        ThemeEntityRelations entityRelation = themeService.createEntityRelation(courseResourceVO.getThemeId(), courseResource.getId(), EntityTypeEnum.COURSE_RESOURCE, 0, courseResourceVO.isPreviewable(), releasedTime, updateStatus);
+        themeEntityRelationsSchedulerService.createEntityRelationScheduler(entityRelation);
+        CourseResourceVO wrapResult = courseResourceVOWrapper.wrap(courseResource);
+        wrapResult.setCanEdit(true);
         wrapResult.setThemeId(entityRelation.getThemeId());
         wrapResult.setEntityId(entityRelation.getEntityId());
+        wrapResult.setPreviewable(entityRelation.isPreviewable());
+        wrapResult.setReleasedTime(releasedTime.getTime());
+        wrapResult.setReleasedStatus(entityRelation.getReleaseStatus());
         return wrapResult;
     }
 
     @Override
     public CourseResourceVO updateCourseResource(CourseResourceVO courseResourceVO) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        checkCourseResource(courseResourceVO);
+        // 检验章节信息
+        checkCourseResource(userId, courseResourceVO);
+        // 根据课程的公开类型 设置章节的公开类型
+        ThemeDetail themeDetail = themeService.getThemeDetailById(courseResourceVO.getThemeId());
+        courseResourceVO.setPublicStatus(themeDetail.getPublicStatus());
         CourseResourceVO resourceById = getCourseResourceById(courseResourceVO.getId());
         if(resourceById.getOwnerId() != null && !resourceById.getOwnerId().equals(userId)){
             throw new HttpBadRequestException("该章节不属于本用户,不可修改");
         }
         CourseResourceVO courseResourceById = getCourseResourceById(courseResourceVO.getId());
         courseResourceVO.setOwnerId(courseResourceById.getOwnerId());
-        CourseResource courseRecourse = courseResourceService.updateCourseRecourse(courseResourceVOWrapper.unwrap(courseResourceVO));
-        return courseResourceVOWrapper.wrap(courseRecourse);
+        CourseResource courseResource = courseResourceService.updateCourseResource(courseResourceVOWrapper.unwrap(courseResourceVO));
+        CourseResourceVO wrapResult = courseResourceVOWrapper.wrap(courseResource);
+        wrapResult.setCanEdit(true);
+        wrapResult.setReleasedTime(courseResourceVO.getReleasedTime());
+
+        return wrapResult;
     }
 
     @Override
@@ -806,10 +890,19 @@ public class ThemeLogicImpl implements ThemeLogic {
             if(courseResourceVO.getOwnerId() != null && courseResourceVO.getOwnerId().equals(userId) && courseResourceVO.getPublicStatus() == CourseResource.IS_PRIVATE){
                 // 删除关联表信息
                 result = themeService.deleteRelationBetweenThemeAndEntity(themeId, entityId, EntityTypeEnum.COURSE_RESOURCE);
-                CourseResource courseResource = courseResourceVOWrapper.unwrap(courseResourceVO);
-                courseResource.setDeleted(CourseResource.IS_Deleted);
-                // 更新章节表信息
-                courseResourceService.updateCourseRecourse(courseResource);
+//                CourseResource courseResource = courseResourceVOWrapper.unwrap(courseResourceVO);
+//                courseResource.setDeleted(CourseResource.IS_Deleted);
+//                // 更新章节表信息
+//                courseResourceService.updateCourseResource(courseResource);
+                // 对于与课程没有关联的章节进行伪删除(仅限于自己创建的章节)
+                List<CourseResource> courseResourceLists = courseResourceService.getCourseResourceByUserId(userId, CourseResource.NOT_Deleted);
+                courseResourceLists.stream().map(courseResource -> {
+                    if(operationRecourseService.getOperationResourceListByResourceId(courseResource.getId()).size() <= 0){
+                        courseResource.setDeleted(CourseResource.IS_Deleted);
+                        courseResourceService.updateCourseResource(courseResource);
+                    }
+                    return null;
+                }).collect(Collectors.toList());
             }else{
                 log.info("该章节不属于本用户,只能删除关联表");
                 result = themeService.deleteRelationBetweenThemeAndEntity(themeId, entityId, EntityTypeEnum.COURSE_RESOURCE);
@@ -823,88 +916,147 @@ public class ThemeLogicImpl implements ThemeLogic {
     }
 
     @Override
-    public ThemeEntityRelations addCourseRecourse(EntityVO entityVO) {
-        ThemeEntityRelations themeEntityRelations = themeService
-                .checkRelationBetweenThemeAndEntity(entityVO.getThemeId(), entityVO.getEntityId(), EntityTypeEnum.COURSE_RESOURCE);
-        if (themeEntityRelations != null) {
-            CourseResourceVO courseResourceById = getCourseResourceById(themeEntityRelations.getEntityId());
-            throw new HttpNotFoundException("课程中已包含章节\""+ courseResourceById.getTitle() +"\",请勿重复添加!");
-        }
-        ThemeEntityRelations entityRelation = themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), EntityTypeEnum.COURSE_RESOURCE, 0, Boolean.TRUE);
-        return entityRelation;
-    }
-
-    @Override
-    public List<ThemeEntityRelations> addRecourseList(List<EntityVO> entityVOList) {
-//        List<CourseResourceVO> resourceRepeatList = new ArrayList<>();
-//        // 对于章节实体
-//        List<ThemeEntityRelations> entityRelationsList = entityVOList.stream().filter(entityVO -> entityVO.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).map(entityVO -> {
-//            ThemeEntityRelations themeEntityRelations = themeService
-//                    .checkRelationBetweenThemeAndEntity(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType());
-//            if (themeEntityRelations != null) {
-//                CourseResourceVO courseResourceById = getCourseResourceById(themeEntityRelations.getEntityId());
-//                resourceRepeatList.add(courseResourceById);
-//            }
-//            return themeEntityRelations;
-//        }).collect(Collectors.toList());
-//        // 存在章节实体
-//        List<ThemeEntityRelations> entityRelations = new ArrayList<>();
-//
-//        if(entityRelationsList.size() > 0){
-//            if (resourceRepeatList.size() > 0) {
-//                List<Long> resourceRepeatIds = new ArrayList<>();
-//                for(CourseResourceVO courseResourceVO : resourceRepeatList){
-//                    resourceRepeatIds.add(courseResourceVO.getId());
-//                }
-//                entityRelations = entityVOList.stream().filter(entityVO -> !resourceRepeatIds.contains(entityVO.getEntityId()) && entityVO.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).map(entityVO -> themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType(), 0, Boolean.TRUE)).collect(Collectors.toList());
-//
-//            }else{
-//                entityRelations = entityVOList.stream().filter(entityVO ->  entityVO.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).map(entityVO -> themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType(), 0, Boolean.TRUE)).collect(Collectors.toList());
-//            }
-//        }
-        // 对于章节实体
+    public List<CourseResourceVO> addRecourseList(List<EntityVO> entityVOList) {
+        if(entityVOList.size() <= 0){
+            throw new HttpBadRequestException("您未选择章节进行导入,可能导入的章节列表已全部在课程中!");
+        }
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        // 章节实体
+        List<CourseResourceVO> courseResourceVOS = entityVOList.stream().filter(entityVO -> entityVO.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).map(entityVO -> {
+            // 判断对add的章节是否是 已授权的非自定义课程中的章节
+            boolean canAdd = true;
+            String message = "";
+            CourseResource resource = courseResourceService.getCourseResourceByResourceId(entityVO.getEntityId());
+            // 如果存在公开的章节则需要判断
+            if(resource.getPublicStatus() == CourseResource.IS_PUBLIC){
+                // 根据这些公开章节获取所有已使用当前章节的 公开课程
+                List<OperationResource> operationRecourseList = operationRecourseService.getOperationResourceList(userId, User2Theme.AUTH_VIEW, resource.getId(), CourseResource.NOT_Deleted, CourseResource.IS_PUBLIC, ThemeDetail.IS_PUBLIC, ThemeDetail.COURSE, ThemeDetail.NOT_DELETED);
+                if(operationRecourseList.size() <= 0){
+                    canAdd = false;
+                    message += " \"" + resource.getTitle() + "\"";
+                }
+            }
 
-        // 对于考试和练习
-//        List<EntityVO> entityVOS = entityVOList.stream().filter(entityVO -> entityVO.getEntityType().equals(EntityTypeEnum.EXAM)).collect(Collectors.toList());
-//        // 存在考试和练习实体
-//        if(entityVOS.size() > 0){
-//            entityRelations = entityVOList.stream().map(entityVO -> themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType(), 0, Boolean.TRUE)).collect(Collectors.toList());
-//        }
-        List<ThemeEntityRelations> entityRelations = entityVOList.stream().map(entityVO -> themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType(), 0, Boolean.TRUE)).collect(Collectors.toList());
-        return entityRelations;
+            if(!canAdd){
+                throw new HttpBadRequestException("添加"+message+"章节失败,您未此章节所属公开课程的使用权限!");
+            }
+
+            // entityVO.getThemeId()此时entityVO中传了当前课程的id
+            ThemeDetail themeDetail = themeService.getThemeDetailById(entityVO.getThemeId());
+            // 根据课程的公开类型 设置章节的默认预览状态
+            boolean canPreview = true;
+            if(themeDetail.getPublicStatus() == ThemeDetail.IS_PUBLIC){
+                canPreview = false;
+            }
+            Integer updateStatus = themeEntityRelationsStatusService.updateStatusByTime(themeDetail.getBeginTime().getTime());
+            ThemeEntityRelations entityRelation = themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType(), 0, canPreview, themeDetail.getBeginTime(), updateStatus);
+            if(entityRelation == null){
+                throw new HttpBadRequestException("此课程中已包含此章节!");
+            }
+            themeEntityRelationsSchedulerService.createEntityRelationScheduler(entityRelation);
+            CourseResourceVO courseResourceVO = courseResourceVOWrapper.wrap(courseResourceService.getCourseResourceByResourceId(entityRelation.getEntityId()));
+            courseResourceVO.setThemeId(entityRelation.getThemeId());
+            courseResourceVO.setEntityId(entityRelation.getEntityId());
+            courseResourceVO.setEntityType(entityRelation.getEntityType());
+            courseResourceVO.setReleasedTime(entityRelation.getReleaseTime().getTime());
+            courseResourceVO.setReleasedStatus(entityRelation.getReleaseStatus());
+            courseResourceVO.setPreviewable(entityRelation.isPreviewable());
+            // 根据课程的所属者 设置章节可编辑状态
+            if(courseResourceVO.getOwnerId().equals(userId)){
+                courseResourceVO.setCanEdit(true);
+            }else{
+                courseResourceVO.setCanEdit(false);
+            }
+            return courseResourceVO;
+        }).collect(Collectors.toList());
+        return courseResourceVOS;
+    }
+
+    @Override
+    public List<ThemeEntityRelationsVO> addExamRecourseList(List<EntityVO> entityVOList) {
+        // 考试和练习实体
+        List<ThemeEntityRelations> entityRelations = entityVOList.stream().filter(entityVO -> entityVO.getEntityType().equals(EntityTypeEnum.EXAM)).map(entityVO -> {
+            ThemeEntityRelations entityRelation = themeService.createEntityRelation(entityVO.getThemeId(), entityVO.getEntityId(), entityVO.getEntityType(), 0, Boolean.TRUE, null, ThemeEntityRelations.RELEASED);
+            return entityRelation;
+        }).collect(Collectors.toList());
+        List<ThemeEntityRelationsVO> relationsVOS = entityRelations.stream().map(themeEntityRelationsVOWrapper::wrapperThemeEntityRelations).collect(Collectors.toList());
+        return relationsVOS;
     }
 
     @Override
     public ThemeEntityRelationsVO updateCourseResourcePreview(Long themeId, Long entityId) {
-        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
-        ThemeDetailVO themeDetailByThemeId = getThemeDetailByThemeId(themeId);
-        if(themeDetailByThemeId.getPublicStatus() == ThemeDetail.IS_PRIVATE && !themeDetailByThemeId.getOwnerId().equals(userId)){
-            throw new HttpBadRequestException("当前用户并非此课程的创建者,对此课程没有编辑权限");
-        }else if(themeDetailByThemeId.getPublicStatus() == ThemeDetail.IS_PUBLIC && !isAdmin){
-            throw new HttpBadRequestException("当前用户并非管理员,对此公开课程没有编辑权限");
+        checkEditCourseAuth(themeId);
+        ThemeEntityRelations relation = themeService.getThemeEntityRelationsByThemeIdAndEntityIdAndEntityType(themeId, entityId, EntityTypeEnum.COURSE_RESOURCE);
+        if (relation == null){
+            throw new HttpNotFoundException("课程中不包含此章节");
         }
+        relation.setPreviewable(!relation.isPreviewable());
+        ThemeEntityRelations themeEntityRelations = themeService.updateEntityRelation(relation);
+        return themeEntityRelationsVOWrapper.wrapperThemeEntityRelations(themeEntityRelations);
+    }
+
+    @Override
+    public ThemeEntityRelationsVO updateCourseResourceReleasedTime(Long themeId, Long entityId, Long releasedTime){
+        checkEditCourseAuth(themeId);
         ThemeEntityRelations relation = themeService.getThemeEntityRelationsByThemeIdAndEntityIdAndEntityType(themeId, entityId, EntityTypeEnum.COURSE_RESOURCE);
         if (relation == null){
             throw new HttpNotFoundException("课程中不包含此章节");
         }
-        ThemeEntityRelations themeEntityRelations = themeService.updateEntityRelation(relation.getId(), themeId, entityId, EntityTypeEnum.COURSE_RESOURCE, relation.getSortId(), !relation.isPreviewable());
+        ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+        if(releasedTime < themeDetail.getBeginTime().getTime() || releasedTime > themeDetail.getEndTime().getTime()){
+            throw new HttpNotFoundException("章节的发布时间应该在课程有效时间区间内");
+        }
+        ThemeEntityRelations entityRelations = themeService.getThemeEntityRelationsByThemeIdAndEntityIdAndEntityType(themeId, entityId, EntityTypeEnum.COURSE_RESOURCE);
+        themeEntityRelationsSchedulerService.cancelEntityRelationScheduler(entityRelations);
+        Integer updateStatus = themeEntityRelationsStatusService.updateStatusByTime(releasedTime);
+        entityRelations.setReleaseStatus(updateStatus);
+        entityRelations.setReleaseTime(new Timestamp(releasedTime));
+        ThemeEntityRelations themeEntityRelations = themeService.updateEntityRelation(entityRelations);
+        themeEntityRelationsSchedulerService.createEntityRelationScheduler(themeEntityRelations);
         return themeEntityRelationsVOWrapper.wrapperThemeEntityRelations(themeEntityRelations);
     }
 
-    private void checkCourseResource(CourseResourceVO courseResourceVO) {
+    private void checkEditCourseAuth(Long themeId) {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
+        ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+        if (themeDetail.getPublicStatus() == ThemeDetail.IS_PRIVATE && !themeDetail.getOwnerId().equals(userId)) {
+            throw new HttpBadRequestException("当前用户并非此课程的创建者,对此课程没有编辑权限");
+        } else if (themeDetail.getPublicStatus() == ThemeDetail.IS_PUBLIC && !isAdmin) {
+            throw new HttpBadRequestException("当前用户并非管理员,对此公开课程没有编辑权限");
+        }
+    }
+
+    private void checkCourseResource(Long userId, CourseResourceVO courseResourceVO) {
         if(courseResourceVO.getTitle() == null){
             throw new HttpBadRequestException("章节标题不可为空");
         }
         if(courseResourceVO.getDescription() == null){
             throw new HttpBadRequestException("章节描述不可为空");
         }
+        List<CourseResource> courseResourceList = courseResourceService.getCourseResourceByUserId(userId, CourseResource.NOT_Deleted).stream()
+                .filter(courseResource -> courseResource.getTitle().trim().equals(courseResourceVO.getTitle().trim())).collect(Collectors.toList());
+        if(courseResourceList.size() > 0){
+            if(courseResourceVO.getId() == null)
+                throw new HttpBadRequestException("您已使用过当前章节名称建立章节,请修改章节名称!");
+
+            courseResourceList.stream().map(courseResource -> {
+                if(!courseResource.getId().equals(courseResourceVO.getId())){
+                    throw new HttpBadRequestException("您已使用过当前章节名称建立章节,请修改章节名称!");
+                }
+                return null;
+            }).collect(Collectors.toList());
+        }
+
     }
 
     @Override
     public ThemeInfoVO getThemeInfoByThemeId(Long themeId) {
         ThemeInfoVO themeInfoVO = new ThemeInfoVO();
         ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+        if(themeDetail.getReleased() == ThemeDetail.UNRELEASED){
+            throw new HttpBadRequestException("当前课程未发布,不可查看教学进度!");
+        }
         ThemeDetailVO themeDetailVO = themeVOWrapper.wrapperThemeDetail(themeDetail);
         themeInfoVO.setThemeDetailVO(themeDetailVO);
         // 获取课程时间进度
@@ -925,10 +1077,9 @@ public class ThemeLogicImpl implements ThemeLogic {
         for(User student : studentList){
             studentIds.add(student.getId());
         }
-
         CourseVO resource = getCourseOfCourseResource(themeId);
         List<EntityVO> entityResourceVOList = resource.getEntityVOList().stream().filter(resourceEntity -> resourceEntity.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).collect(Collectors.toList());
-        if(entityResourceVOList.size() > 0){
+        if(entityResourceVOList.size() > 0 && studentIds.size() > 0){
             // 课程中存在章节
             List<Long> resourceIds = entityResourceVOList.stream().map(EntityVO::getEntityId).collect(Collectors.toList());
             int studyRecordNumber = themeService.getStudyRecord(themeId, resourceIds, studentIds, EntityTypeEnum.COURSE_RESOURCE);
@@ -973,11 +1124,46 @@ public class ThemeLogicImpl implements ThemeLogic {
         }else{
             themeInfoVO.setExerciseCompletedRate((float) (totalExerciseCommitNumber/(exerciseCount*studentNumber*1.0)));
         }
-
         return themeInfoVO;
     }
 
     @Override
+    public  List<StudentCourseResultVO>  getStudentThemeInfoByThemeId(Long themeId, String keyword){
+        ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+        if(themeDetail.getReleased() == ThemeDetail.UNRELEASED){
+            throw new HttpBadRequestException("当前课程未发布,不可查看教学进度!");
+        }
+        ThemeDetailVO themeDetailVO = themeVOWrapper.wrapperThemeDetail(themeDetail);
+        CourseVO resource = getCourseOfCourseResource(themeId);
+        List<EntityVO> entityResourceVOList = resource.getEntityVOList().stream().filter(resourceEntity -> resourceEntity.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE)).collect(Collectors.toList());
+
+        // 获取每个学生的完整章节的信息
+        // 获取课程下所有章节的id
+        List<Long> totalCourseResourceIds = entityResourceVOList.stream().map(EntityVO::getEntityId).collect(Collectors.toList());
+        // 获取当前课程下的所有学生
+        List<User> userList = groupService.getUserByGroupId(themeDetailVO.getGroupId());
+        List<StudentCourseResultVO> studentCourseResultVOList = userList.stream().filter(user -> user.getName().contains(keyword.trim())).map(user -> {
+            StudentCourseResultVO studentCourseResultVO = new StudentCourseResultVO();
+            studentCourseResultVO.setUserVO(userVOWrapper.wrap(user));
+            // 获取当前学生课程所有章节学习记录(可能有已删除的章节的记录)
+            List<StudyRecord> studyCompletenessByThemeId = themeService.getStudyCompletenessByThemeId(themeId, user.getId(), EntityTypeEnum.COURSE_RESOURCE);
+            List<Long> studyCompletenessEntityId = studyCompletenessByThemeId.stream().map(StudyRecord::getEntityId).collect(Collectors.toList());
+
+            // 去重 获取当前学生课程有效的已完成的章节学习记录
+            List<Long> courseResourceIdRest = new ArrayList<>(totalCourseResourceIds);
+            List<Long> courseResourceIdCommon = new ArrayList<>(totalCourseResourceIds);
+            courseResourceIdRest.removeAll(studyCompletenessEntityId);
+            courseResourceIdCommon.removeAll(courseResourceIdRest);
+            studentCourseResultVO.setCompleteCourseResourceId(courseResourceIdCommon);
+            studentCourseResultVO.setCompleteCourseResourceCount(courseResourceIdCommon.size());
+            studentCourseResultVO.setTotalCourseResourceId(totalCourseResourceIds);
+            studentCourseResultVO.setTotalCourseResourceCount(totalCourseResourceIds.size());
+            return studentCourseResultVO;
+        }).collect(Collectors.toList());
+        return studentCourseResultVOList;
+    }
+
+    @Override
     public void createStudyRecord(Long themeId, Long courseId, Long userId) {
         StudyRecord studyRecord = themeService.getStudyRecordByThemeId(themeId, courseId, userId, EntityTypeEnum.COURSE_RESOURCE);
         if(studyRecord == null){
@@ -1003,4 +1189,151 @@ public class ThemeLogicImpl implements ThemeLogic {
         }
         return themeVOWrapper.wrapperThemeDetail(themeService.deleteThemeDetailById(themeId));
     }
+
+    @Override
+    public  List<ThemeDetailVO> searchCourse(String courseName, Long userId){
+        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
+        if(!isAdmin){
+            throw new HttpBadRequestException("当前用户无此操作权限");
+        }
+        List<ThemeDetail> themeDetailList = themeService.getThemeDetailByCourseName(courseName);
+        return themeDetailList.stream().map(themeVOWrapper::wrapperThemeDetail).collect(Collectors.toList());
+    }
+
+    @Override
+    public  List<ThemeDetailVO>  getTeacherAuthorisedCourses(Long teacherId){
+        List<OperationCourse> operationCourseList = themeService.getTeacherAuthorizedCourseListByUse(teacherId, User2Theme.AUTH_USE);
+        return operationCourseList.stream().filter(operationCourse -> operationCourse.getPublicStatus() == ThemeDetail.IS_PUBLIC).map(operationCourse -> {
+            ThemeDetail convert = Converter.convert(ThemeDetail.class, operationCourse);
+            return themeVOWrapper.wrapperThemeDetail(convert);
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public Boolean authorisedCoursesList(Long teacherId, List<Long> courseIdList){
+        // 判断教师是否具有自定义课程教师的角色
+        List<Role> roleIdsOfUser = roleService.getRolesOfUser(teacherId);
+        Role courseTeacher = roleService.getRoleByRoleName("courseTeacher");
+        if(!roleIdsOfUser.contains(courseTeacher)){
+            throw new HttpBadRequestException("当前教师并无自定义课程的权限,请先赋予教师权限后进行操作!");
+        }
+
+        if(courseIdList.size() <= 0){
+            throw new HttpBadRequestException("未选择授权课程!");
+        }
+        courseIdList.stream().peek(courseId -> {
+            List<User2Theme> user2ThemeList = user2ThemeService.findAllByUserIdAndThemeId(teacherId, courseId);
+            if(user2ThemeList.size() > 0){
+                ThemeDetail themeDetail = themeService.getThemeDetailById(courseId);
+                user2ThemeList.stream().peek(user2Theme -> {
+                    if(user2Theme.getOperation().equals(User2Theme.AUTH_USE)){
+                        throw new HttpBadRequestException("该教师已有\""+themeDetail.getTitle()+"\"课程的使用权限!");
+                    }else if(user2Theme.getOperation().equals(User2Theme.AUTH_ALL)){
+                        throw new HttpBadRequestException("该教师已有\""+themeDetail.getTitle()+"\"课程的所有权限!");
+                    }
+                }).collect(Collectors.toList());
+            }
+        }).collect(Collectors.toList());
+
+        courseIdList.stream().peek(courseId -> {
+            user2ThemeService.createOperationRelation(teacherId, courseId, User2Theme.AUTH_USE);
+        }).collect(Collectors.toList());
+        return true;
+    }
+
+    @Override
+    public void authorisedCourseTeacherRole(Long teacherId, Long userId){
+        // 为教师赋予慕测教师、教师自定义课程的角色
+        List<Role> roleIdsOfUser = roleService.getRolesOfUser(teacherId);
+        Role courseTeacher = roleService.findById(9L);
+//        Role mooctestTeacher = roleService.getRoleByRoleName("mooctestTeacher");
+//        if(!roleIdsOfUser.contains(mooctestTeacher)){
+//            user2RoleService.addRole2User(teacherId, mooctestTeacher.getId());
+//        }
+        if(!roleIdsOfUser.contains(courseTeacher)){
+            user2RoleService.addRole2User(teacherId, courseTeacher.getId());
+        }else{
+            List<User2Role> user2RoleList = user2RoleService.getRole2User(teacherId, courseTeacher.getId());
+            user2RoleService.deleteRole2User(user2RoleList);
+        }
+    }
+
+    @Override
+    public Boolean checkCourseTeacherRole(Long teacherId, Long userId){
+        // 查看教师是否具有教师自定义课程的角色
+        List<Role> roleIdsOfUser = roleService.getRolesOfUser(teacherId);
+        Role courseTeacher = roleService.findById(9L);
+        return roleIdsOfUser.contains(courseTeacher);
+    }
+
+    @Override
+    public Boolean deleteAuthorisedCourse(Long teacherId, Long courseId){
+        // 判断教师是否具有自定义课程教师的角色
+        List<Role> roleIdsOfUser = roleService.getRolesOfUser(teacherId);
+        Role courseTeacher = roleService.getRoleByRoleName("courseTeacher");
+        if(!roleIdsOfUser.contains(courseTeacher)){
+            throw new HttpBadRequestException("当前教师并无自定义课程的权限,请先赋予教师权限后进行操作!");
+        }
+
+        User2Theme user2Theme = user2ThemeService.findByUserIdAndThemeIdAndOperation(teacherId, courseId, User2Theme.AUTH_USE);
+        ThemeDetail themeDetail = themeService.getThemeDetailById(courseId);
+        if(user2Theme == null){
+            throw new HttpBadRequestException("当前教师并无\""+themeDetail.getTitle()+"\"的使用权限!");
+        }
+        user2ThemeService.delete(user2Theme);
+        return true;
+    }
+
+    @Override
+    public CourseVO orderThemeDetailEntity(Long themeId, List<EntityVO> entityVOList){
+        int index = 0;
+        for(EntityVO entityVO: entityVOList){
+            index++;
+            ThemeEntityRelations entityRelations = themeService.getThemeEntityRelationsByThemeIdAndEntityIdAndEntityType(themeId, entityVO.getEntityId(), entityVO.getEntityType());
+            entityRelations.setSortId(index);
+            themeService.updateEntityRelation(entityRelations);
+        }
+        ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+        return getCourseVO(themeDetail);
+    }
+
+    @Override
+    public boolean initEntityRelationReleaseStatus(Long userId){
+        boolean admin = SecurityUtils.getSubject().hasRole("admin");
+        if(!admin){
+            throw new HttpBadRequestException("不是管理员用户,没有此权限!");
+        }
+        List<ThemeDetail> themeDetailList = themeService.getThemeDetailByIdList(Lists.newArrayList(29L));
+        themeDetailList.stream().map(themeDetail -> {
+            return themeService.getThemeEntityRelations(themeDetail.getId()).stream().filter(themeEntityRelations -> themeEntityRelations.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE))
+                    .map(themeEntityRelations -> {
+                        themeEntityRelations.setReleaseTime(themeDetail.getBeginTime());
+                        themeEntityRelations.setReleaseStatus(ThemeEntityRelations.RELEASED);
+                        System.out.println(themeEntityRelations.getEntityId());
+                        themeService.updateEntityRelation(themeEntityRelations);
+                        return null;
+                    }).collect(Collectors.toList());
+        }).collect(Collectors.toList());
+        return true;
+    }
+
+    @Override
+    public boolean initEntityRelationListReleaseStatus(Long userId, List<Long> themeIdList){
+        boolean admin = SecurityUtils.getSubject().hasRole("admin");
+        if(!admin){
+            throw new HttpBadRequestException("不是管理员用户,没有此权限!");
+        }
+        List<ThemeDetail> themeDetailList = themeService.getThemeDetailByIdList(themeIdList);
+        themeDetailList.stream().map(themeDetail -> {
+            return themeService.getThemeEntityRelations(themeDetail.getId()).stream().filter(themeEntityRelations -> themeEntityRelations.getEntityType().equals(EntityTypeEnum.COURSE_RESOURCE))
+                    .map(themeEntityRelations -> {
+                        themeEntityRelations.setReleaseTime(themeDetail.getBeginTime());
+                        themeEntityRelations.setReleaseStatus(ThemeEntityRelations.RELEASED);
+                        System.out.println(themeEntityRelations.getEntityId());
+                        themeService.updateEntityRelation(themeEntityRelations);
+                        return null;
+                    }).collect(Collectors.toList());
+        }).collect(Collectors.toList());
+        return true;
+    }
 }

+ 288 - 0
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/ctrl/ThemeControllerTest.java

@@ -0,0 +1,288 @@
+package cn.iselab.mooctest.site.web.ctrl;
+
+import cn.iselab.mooctest.site.AbstractShiroTest;
+import cn.iselab.mooctest.site.common.enums.CourseType;
+import cn.iselab.mooctest.site.common.enums.CourseVisibility;
+import cn.iselab.mooctest.site.models.ThemeDetail;
+import cn.iselab.mooctest.site.models.instancePermission.CasePermission;
+import cn.iselab.mooctest.site.web.data.CourseVO;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import cn.iselab.mooctest.site.web.data.ThemeDetailVO;
+import cn.iselab.mooctest.site.web.logic.ExamLogic;
+import cn.iselab.mooctest.site.web.logic.GroupLogic;
+import cn.iselab.mooctest.site.web.logic.RoleLogic;
+import cn.iselab.mooctest.site.web.logic.ThemeLogic;
+import org.apache.poi.ss.formula.functions.T;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+/**
+ * @author guochao
+ * @date 2020-03-09 18:09
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ThemeControllerTest  extends AbstractShiroTest {
+
+    private MockMvc mockMvc;
+
+    @InjectMocks
+    private ThemeController themeController = new ThemeController();
+
+    @Mock
+    private ThemeLogic themeLogic;
+    @Mock
+    private ExamLogic examLogic;
+    @Mock
+    private GroupLogic groupLogic;
+    @Mock
+    private RoleLogic roleLogic;
+
+
+    private Subject subject;
+    private Session session;
+    private CourseVO courseVO;
+    private ThemeDetailVO themeDetailVO;
+
+    @Before
+    public void setupMockMvc() {
+        initMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(themeController).build();
+
+        courseVO = new CourseVO();
+        themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setOwnerId(39154L);
+        themeDetailVO.setTitle("测试课程");
+        themeDetailVO.setIntroduce("这是描述");
+        themeDetailVO.setBeginTime(1583749250172L);
+        themeDetailVO.setEndTime(1585614647000L);
+        themeDetailVO.setStatus(ThemeDetail.STATUS_ONGOING);
+        themeDetailVO.setCourseType(CourseType.CUSTOM.getType());
+        themeDetailVO.setVisibility(CourseVisibility.NONE_OPEN.getVisibility());
+        themeDetailVO.setReleased(ThemeDetail.RELEASED);
+        themeDetailVO.setCanEdit(true);
+        themeDetailVO.setGroupId(480L);
+        themeDetailVO.setGroupName("测试课程班级");
+        themeDetailVO.setType(0);
+        themeDetailVO.setPublicStatus(ThemeDetail.IS_PRIVATE);
+        themeDetailVO.setIsDeleted(0);
+        themeDetailVO.setHasPurchase(true);
+
+        courseVO.setThemeDetailVO(themeDetailVO);
+
+        subject=mock(Subject.class);
+        session=mock(Session.class);
+
+        when(subject.getSession()).thenReturn(session);
+        when(session.getAttribute("userId")).thenReturn(39154L);
+        when(subject.hasRole("admin")).thenReturn(true);
+        setSubject(subject);
+
+
+
+    }
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void should_return_courseVO_when_get_theme_detail_by_teacher_or_admin() {
+
+    }
+
+    @Test
+    public void getCourseByStudent() {
+    }
+
+    @Test
+    public void showThemeViews() {
+    }
+
+    @Test
+    public void getPageable() {
+    }
+
+    @Test
+    public void getOwnerGroups() {
+    }
+
+    @Test
+    public void getForkGroups() {
+    }
+
+    @Test
+    public void testGetForkGroups() {
+    }
+
+    @Test
+    public void changePreviewState() {
+    }
+
+    @Test
+    public void changeReleasedTime() {
+    }
+
+    @Test
+    public void createThemeCourse() {
+    }
+
+    @Test
+    public void updateThemeCourse() {
+    }
+
+    @Test
+    public void releaseThemeCourse() {
+    }
+
+    @Test
+    public void createCourseResource() {
+    }
+
+    @Test
+    public void updateCourseResource() {
+    }
+
+    @Test
+    public void importCourseResources() {
+    }
+
+    @Test
+    public void deleteCourseResource() {
+    }
+
+    @Test
+    public void searchCourseResource() {
+    }
+
+    @Test
+    public void searchThemeByCourseResource() {
+    }
+
+    @Test
+    public void getResourcePageable() {
+    }
+
+    @Test
+    public void getScoreListByExamId() {
+    }
+
+    @Test
+    public void createStudyRecord() {
+    }
+
+    @Test
+    public void searchExamByGroupId() {
+    }
+
+    @Test
+    public void deleteExamById() {
+    }
+
+    @Test
+    public void testShowThemeViews() {
+    }
+
+    @Test
+    public void showThemeViewById() {
+    }
+
+    @Test
+    public void createThemeDetails() {
+    }
+
+    @Test
+    public void addCases2theme() {
+    }
+
+    @Test
+    public void addPapers2theme() {
+    }
+
+    @Test
+    public void addExams2theme() {
+    }
+
+    @Test
+    public void removeCaseFromtheme() {
+    }
+
+    @Test
+    public void removePaperFromtheme() {
+    }
+
+    @Test
+    public void removeExamFromtheme() {
+    }
+
+    @Test
+    public void getThemesHasTheCase() {
+    }
+
+    @Test
+    public void getThemesHasThePaper() {
+    }
+
+    @Test
+    public void getThemesHasTheExam() {
+    }
+
+    @Test
+    public void getCourses() {
+    }
+
+    @Test
+    public void search() {
+    }
+
+    @Test
+    public void buyTheme() {
+    }
+
+    @Test
+    public void courses() {
+    }
+
+    @Test
+    public void getCustomizeCourses() {
+    }
+
+    @Test
+    public void getAuthorisedCourses() {
+    }
+
+    @Test
+    public void participantCourse() {
+    }
+
+    @Test
+    public void resource() {
+    }
+
+    @Test
+    public void getResource() {
+    }
+
+    @Test
+    public void joinGroup() {
+    }
+}

+ 511 - 29
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/logic/impl/ThemeLogicImplTest.java

@@ -1,18 +1,24 @@
 package cn.iselab.mooctest.site.web.logic.impl;
 
+import cn.iselab.mooctest.site.AbstractShiroTest;
+import cn.iselab.mooctest.site.common.enums.CourseType;
+import cn.iselab.mooctest.site.common.enums.CourseVisibility;
 import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
-import cn.iselab.mooctest.site.models.CourseResource;
-import cn.iselab.mooctest.site.models.ThemeDetail;
-import cn.iselab.mooctest.site.models.ThemeEntityRelations;
-import cn.iselab.mooctest.site.service.CourseResourceService;
-import cn.iselab.mooctest.site.service.ThemeService;
-import cn.iselab.mooctest.site.web.data.CourseResourceVO;
-import cn.iselab.mooctest.site.web.data.ThemeVO;
-import cn.iselab.mooctest.site.web.data.wrapper.CourseResourceVOWrapper;
-import cn.iselab.mooctest.site.web.data.wrapper.ThemeVOWrapper;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.service.*;
+import cn.iselab.mooctest.site.service.common.PdfService;
+import cn.iselab.mooctest.site.web.data.*;
+import cn.iselab.mooctest.site.web.data.wrapper.*;
+import cn.iselab.mooctest.site.web.exception.HttpBadRequestException;
 import cn.iselab.mooctest.site.web.exception.HttpForbiddenException;
+import cn.iselab.mooctest.site.web.exception.HttpNotFoundException;
+import cn.iselab.mooctest.site.web.logic.OSSLogic;
 import cn.iselab.mooctest.site.web.logic.ThemeLogic;
 import com.google.common.collect.Lists;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -21,12 +27,18 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.bind.annotation.ExceptionHandler;
 
+import java.sql.Timestamp;
+import java.util.ArrayList;
 import java.util.List;
 
 import static org.junit.Assert.*;
 import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
 
 /**
  * @program: mooctest-site
@@ -37,43 +49,168 @@ import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 @WebAppConfiguration
-public class ThemeLogicImplTest {
+public class ThemeLogicImplTest extends AbstractShiroTest {
 
     @InjectMocks
     private ThemeLogic themeLogic = new ThemeLogicImpl();
     @Mock
-    private CourseResourceService courseResourceService;
+    private ThemeVOWrapper themeVOWrapper;
     @Mock
-    private CourseResourceVOWrapper courseResourceVOWrapper;
+    private CaseExtendsVOWrapper caseExtendsVOWrapper;
+    @Mock
+    private PaperVOWrapper paperVOWrapper;
+    @Mock
+    ExamVOWrapper examVOWrapper;
     @Mock
     private ThemeService themeService;
     @Mock
-    private ThemeVOWrapper themeVOWrapper;
+    private CaseService caseService;
+    @Mock
+    private ExamService examService;
+    @Mock
+    private PaperService paperService;
+    @Mock
+    private User2ThemeService user2ThemeService;
+    @Mock
+    private ThemeSchedulerService themeSchedulerService;
+    @Mock
+    private ThemeEntityRelationsSchedulerService themeEntityRelationsSchedulerService;
+    @Mock
+    private ThemeEntityRelationsStatusService themeEntityRelationsStatusService;
+    @Mock
+    private ThemeStatusService themeStatusService;
+    @Mock
+    private PdfService pdfService;
+    @Mock
+    GroupService groupService;
+    @Mock
+    VisitControlService visitControlService;
+    @Mock
+    Theme2GroupService theme2GroupService;
+    @Mock
+    SubmitRecordService submitRecordService;
+    @Mock
+    private OSSLogic ossLogic;
+    @Mock
+    private CourseResourceService courseResourceService;
 
+    @Mock
+    private CourseResourceVOWrapper courseResourceVOWrapper;
+    @Mock
+    private ThemeEntityRelationsVOWrapper themeEntityRelationsVOWrapper;
+    @Mock
+    private GroupVOWrapper groupVOWrapper;
+    @Mock
+    private UserVOWrapper userVOWrapper;
     @Rule
     public ExpectedException thrown= ExpectedException.none();
 
-    @Test
-    public void test_getTheme() {
-        ThemeDetail themeDetail = new ThemeDetail();
+
+    private Subject subject;
+    private Session session;
+    private ThemeDetail themeDetail;
+    private CourseResource courseResource;
+    private ThemeEntityRelations themeEntityRelations;
+    private CourseVO courseVO;
+    private ThemeDetailVO themeDetailVO;
+    private EntityVO entityVO;
+    private List<EntityVO> entityVOS;
+    private CourseResourceVO courseResourceVO;
+
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+        themeDetail = new ThemeDetail();
         themeDetail.setId(1L);
-        themeDetail.setOwnerId(2L);
-        themeDetail.setIntroduce("哈哈哈");
-        themeDetail.setPublicStatus(0);
+        themeDetail.setOwnerId(39154L);
+        themeDetail.setTitle("测试课程");
+        themeDetail.setIntroduce("这是描述");
+        themeDetail.setBeginTime(new Timestamp(1583749250172L));
+        themeDetail.setEndTime(new Timestamp(1585614647000L));
+        themeDetail.setStatus(ThemeDetail.STATUS_ONGOING);
+        themeDetail.setCourseType(CourseType.CUSTOM.getType());
+        themeDetail.setVisibility(CourseVisibility.NONE_OPEN.getVisibility());
+        themeDetail.setReleased(ThemeDetail.RELEASED);
         themeDetail.setType(0);
+        themeDetail.setPublicStatus(ThemeDetail.IS_PRIVATE);
+        themeDetail.setIsDeleted(0);
 
-        ThemeEntityRelations themeEntityRelations1 = new ThemeEntityRelations();
-        themeEntityRelations1.setId(1L);
-        themeEntityRelations1.setEntityId(1L);
-        themeEntityRelations1.setEntityType(EntityTypeEnum.COURSE_RESOURCE);
-        themeEntityRelations1.setThemeId(1L);
-        List<ThemeEntityRelations> themeEntityRelations = Lists.newArrayList(themeEntityRelations1);
+        themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setOwnerId(39154L);
+        themeDetailVO.setTitle("测试课程");
+        themeDetailVO.setIntroduce("这是描述");
+        themeDetailVO.setBeginTime(1583749250172L);
+        themeDetailVO.setEndTime(1585614647000L);
+        themeDetailVO.setStatus(ThemeDetail.STATUS_ONGOING);
+        themeDetailVO.setCourseType(CourseType.CUSTOM.getType());
+        themeDetailVO.setVisibility(CourseVisibility.NONE_OPEN.getVisibility());
+        themeDetailVO.setReleased(ThemeDetail.RELEASED);
+        themeDetailVO.setType(0);
+        themeDetailVO.setPublicStatus(ThemeDetail.IS_PRIVATE);
+        themeDetailVO.setIsDeleted(0);
 
-        CourseResource courseResource = new CourseResource();
+        courseResource = new CourseResource();
         courseResource.setId(1L);
+        courseResource.setOwnerId(39154L);
+        courseResource.setTitle("课程第一章节");
+        courseResource.setDescription("章节描述");
+        courseResource.setDeleted(0);
+
+        courseResourceVO = new CourseResourceVO();
+        courseResourceVO.setId(1L);
+        courseResourceVO.setOwnerId(39154L);
+        courseResourceVO.setTitle("课程第一章节");
+        courseResourceVO.setDescription("章节描述");
+
+        themeEntityRelations = new ThemeEntityRelations();
+        themeEntityRelations.setId(1L);
+        themeEntityRelations.setEntityId(1L);
+        themeEntityRelations.setEntityType(EntityTypeEnum.COURSE_RESOURCE);
+        themeEntityRelations.setThemeId(1L);
+        themeEntityRelations.setPreviewable(true);
+
+        entityVO = new EntityVO();
+        entityVO.setEntityId(1L);
+        entityVO.setEntityType(EntityTypeEnum.COURSE_RESOURCE);
+        entityVO.setThemeId(1L);
+        entityVO.setSortedId(1);
+        entityVO.setReleasedTime(1583286720000L);
+        entityVO.setReleasedStatus(1);
+
+        EntityVO entityVOExam = new EntityVO();
+        entityVOExam.setEntityId(111L);
+        entityVOExam.setEntityType(EntityTypeEnum.EXAM);
+        entityVOExam.setThemeId(1L);
+        entityVOExam.setSortedId(2);
+
+        entityVOS = new ArrayList<>();
+        entityVOS.add(entityVO);
+        entityVOS.add(entityVOExam);
+
+        courseVO = new CourseVO();
+        courseVO.setThemeDetailVO(themeDetailVO);
+        courseVO.setEntityVOList(entityVOS);
+        courseVO.setCompleteCourseResourceId(Lists.newArrayList(1L));
+        courseVO.setTotalCourseResourceId(Lists.newArrayList(1L));
+        courseVO.setCompleteExamAndExerciseId(Lists.newArrayList(111L));
+        courseVO.setTotalExamAndExerciseId(Lists.newArrayList(111L));
+        subject=mock(Subject.class);
+        session=mock(Session.class);
+
+        when(subject.getSession()).thenReturn(session);
+        when(session.getAttribute("userId")).thenReturn(39154L);
+        when(subject.hasRole("admin")).thenReturn(true);
+        setSubject(subject);
+    }
+
+    @Test
+    public void test_getTheme() {
+        List<ThemeEntityRelations> themeEntityRelationsList = Lists.newArrayList(themeEntityRelations);
         List<CourseResource> courseResources  = Lists.newArrayList(courseResource);
         when(themeService.getThemeDetailById(anyLong())).thenReturn(themeDetail);
-        when(themeService.getThemeEntityRelations(anyLong())).thenReturn(themeEntityRelations);
+        when(themeService.getThemeEntityRelations(anyLong())).thenReturn(themeEntityRelationsList);
         when(courseResourceService.getCourseResourceById(anyList())).thenReturn(courseResources);
         ThemeVO result = new ThemeVO();
         when(themeVOWrapper.wrapper(any())).thenReturn(result);
@@ -83,8 +220,6 @@ public class ThemeLogicImplTest {
 
     @Test
     public void test_getCourseResourceById() {
-        CourseResource courseResource = new CourseResource();
-        CourseResourceVO courseResourceVO = new CourseResourceVO();
         ThemeEntityRelations themeEntityRelations = new ThemeEntityRelations();
         ThemeDetail course = new ThemeDetail();
         course.setIsDeleted(0);
@@ -96,4 +231,351 @@ public class ThemeLogicImplTest {
         thrown.expectMessage("This Course should buy or hasn't open, and resource can't preview");
         themeLogic.getCourseResource(1L,1L);
     }
+
+    @Test
+    public void show_throw_when_get_course_resource_when_is_deleted() {
+        ThemeEntityRelations themeEntityRelations = new ThemeEntityRelations();
+        ThemeDetail course = new ThemeDetail();
+        course.setIsDeleted(1);
+        course.setReleased(0);
+        when(themeService.getThemeDetailById(1L)).thenReturn(course);
+        when(courseResourceVOWrapper.wrap(courseResource)).thenReturn(courseResourceVO);
+        when(themeService.checkRelationBetweenThemeAndEntity(1L, 1L, EntityTypeEnum.COURSE_RESOURCE)).thenReturn(themeEntityRelations);
+        thrown.expect(HttpForbiddenException.class);
+        thrown.expectMessage("Course can't view now");
+        themeLogic.getCourseResource(1L,1L);
+    }
+
+    @Test
+    public void show_throw_when_get_course_resource_when_relation_null() {
+        ThemeDetail course = new ThemeDetail();
+        course.setIsDeleted(0);
+        course.setReleased(0);
+        course.setStatus(0);
+        themeEntityRelations.setPreviewable(true);
+        when(themeService.getThemeDetailById(1L)).thenReturn(course);
+        when(courseResourceVOWrapper.wrap(courseResource)).thenReturn(courseResourceVO);
+        when(themeService.checkRelationBetweenThemeAndEntity(1L, 1L, EntityTypeEnum.COURSE_RESOURCE)).thenReturn(null);
+        thrown.expect(HttpNotFoundException.class);
+        thrown.expectMessage("This Course does't contain this resource");
+        themeLogic.getCourseResource(1L,1L);
+    }
+
+    @Test
+    public void show_throw_when_get_course_resource_when_course_resource_vo() {
+        ThemeDetail course = new ThemeDetail();
+        course.setIsDeleted(0);
+        course.setReleased(0);
+        course.setStatus(0);
+        themeEntityRelations.setPreviewable(true);
+        when(themeService.getThemeDetailById(1L)).thenReturn(course);
+        when(courseResourceVOWrapper.wrap(courseResource)).thenReturn(courseResourceVO);
+        when(themeService.checkRelationBetweenThemeAndEntity(1L, 1L, EntityTypeEnum.COURSE_RESOURCE)).thenReturn(themeEntityRelations);
+        when(courseResourceService.getCourseResourceByResourceId(anyLong())).thenReturn(courseResource);
+        when(courseResourceVOWrapper.wrap(courseResource)).thenReturn(courseResourceVO);
+        themeLogic.getCourseResource(1L,1L);
+    }
+
+    @Test
+    public void show_throw_when_new_theme_detail_null(){
+        courseVO.setThemeDetailVO(null);
+        thrown.expect(HttpBadRequestException.class);
+        thrown.expectMessage("主题内容不可为空");
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_throw_when_new_theme_detail_not_admin(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1583749250172L);
+        themeDetailVO.setEndTime(1585614647000L);
+        courseVO.setThemeDetailVO(themeDetailVO);
+        when(subject.hasRole("admin")).thenReturn(false);
+        thrown.expect(HttpBadRequestException.class);
+        thrown.expectMessage("当前用户只能新建自定义课程");
+        themeLogic.newThemeCourse(courseVO, 39154L);
+
+    }
+
+    @Test
+    public void show_return_courseVO_when_new_theme_detail(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1683749250172L);
+        themeDetailVO.setEndTime(1685614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+        themeDetailVO.setGroupId(1L);
+        themeDetailVO.setVisibility(0);
+        courseVO.setThemeDetailVO(themeDetailVO);
+        Group group = new Group();
+        group.setId(1L);
+        when(groupService.getGroupsByOwnerId(39154L)).thenReturn(Lists.newArrayList(group));
+        when(themeVOWrapper.unWrapperThemeDetail(themeDetailVO)).thenReturn(themeDetail);
+        when(themeService.createThemeDetail(themeDetail)).thenReturn(themeDetail);
+        when(themeSchedulerService.createNewThemeScheduler(themeDetail)).thenReturn(true);
+        when(themeVOWrapper.wrapperThemeDetail(themeDetail)).thenReturn(themeDetailVO);
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_return_courseVO_when_new_theme_detail_private(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1683749250172L);
+        themeDetailVO.setEndTime(1685614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+        themeDetailVO.setGroupId(1L);
+        themeDetailVO.setVisibility(2);
+        courseVO.setThemeDetailVO(themeDetailVO);
+        Group group = new Group();
+        group.setId(1L);
+        when(groupService.getGroupsByOwnerId(39154L)).thenReturn(Lists.newArrayList(group));
+        when(themeVOWrapper.unWrapperThemeDetail(themeDetailVO)).thenReturn(themeDetail);
+        when(themeService.createThemeDetail(themeDetail)).thenReturn(themeDetail);
+        when(themeSchedulerService.createNewThemeScheduler(themeDetail)).thenReturn(true);
+        when(themeVOWrapper.wrapperThemeDetail(themeDetail)).thenReturn(themeDetailVO);
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_return_courseVO_when_new_theme_detail_currentTimeMillis_lg_end_time(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1183749250172L);
+        themeDetailVO.setEndTime(1185614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+        themeDetailVO.setGroupId(1L);
+        themeDetailVO.setVisibility(2);
+        courseVO.setThemeDetailVO(themeDetailVO);
+        Group group = new Group();
+        group.setId(1L);
+        when(groupService.getGroupsByOwnerId(39154L)).thenReturn(Lists.newArrayList(group));
+        when(themeVOWrapper.unWrapperThemeDetail(themeDetailVO)).thenReturn(themeDetail);
+        when(themeService.createThemeDetail(themeDetail)).thenReturn(themeDetail);
+        when(themeSchedulerService.createNewThemeScheduler(themeDetail)).thenReturn(true);
+        when(themeVOWrapper.wrapperThemeDetail(themeDetail)).thenReturn(themeDetailVO);
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_throw_when_new_theme_detail_title_too_long(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1683749250172L);
+        themeDetailVO.setEndTime(1685614647000L);
+        themeDetailVO.setTitle("测试一下课程测试一下课程测试一下课程测试一下课程测试一下课程测课一下课程测试一下课程");
+//        themeDetailVO.setGroupId(1L);
+        themeDetailVO.setVisibility(0);
+        courseVO.setThemeDetailVO(themeDetailVO);
+        thrown.expect(HttpBadRequestException.class);
+        thrown.expectMessage("主题课程名称不允许超过30字符");
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_throw_when_new_theme_detail_title_existed(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1683749250172L);
+        themeDetailVO.setEndTime(1685614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+//        themeDetailVO.setGroupId(1L);
+        themeDetailVO.setVisibility(0);
+        themeDetail = new ThemeDetail();
+        themeDetail.setTitle("测试一下课程");
+        courseVO.setThemeDetailVO(themeDetailVO);
+        when(themeService.getThemeDetailListByOwnerId(39154L)).thenReturn(Lists.newArrayList(themeDetail));
+        thrown.expect(HttpBadRequestException.class);
+        thrown.expectMessage("主题课程名称已存在");
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_throw_when_new_theme_detail_not_group(){
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(1);
+        themeDetailVO.setBeginTime(1683749250172L);
+        themeDetailVO.setEndTime(1685614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+//        themeDetailVO.setGroupId(1L);
+        themeDetailVO.setVisibility(0);
+        courseVO.setThemeDetailVO(themeDetailVO);
+        Group group = new Group();
+        group.setId(2L);
+        when(groupService.getGroupsByOwnerId(39154L)).thenReturn(Lists.newArrayList(group));
+        thrown.expect(HttpBadRequestException.class);
+        thrown.expectMessage("No right for some group");
+        themeLogic.newThemeCourse(courseVO, 39154L);
+    }
+
+    @Test
+    public void show_throw_when_fork_course(){
+        when(session.getAttribute("userId")).thenReturn(39154L);
+        ThemeDetailVO forkThemeDetailVO = new ThemeDetailVO();
+        forkThemeDetailVO.setId(1L);
+        forkThemeDetailVO.setVisibility(2);
+        forkThemeDetailVO.setBeginTime(1183749250172L);
+        forkThemeDetailVO.setEndTime(1185614647000L);
+        forkThemeDetailVO.setTitle("测试一下课程");
+        forkThemeDetailVO.setGroupId(1L);
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(2);
+        themeDetailVO.setBeginTime(1183749250172L);
+        themeDetailVO.setEndTime(1185614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+        themeDetailVO.setGroupId(1L);
+        themeDetail.setVisibility(2);
+        themeDetail.setId(1L);
+        themeDetail.setBeginTime(new Timestamp(2483749250172L));
+        themeDetail.setEndTime(new Timestamp(2485614647000L));
+        themeDetail.setTitle("测试一下课程");
+
+        ThemeDetail themeDetailRes = new ThemeDetail();
+        themeDetailRes.setVisibility(2);
+        themeDetailRes.setId(1L);
+        themeDetailRes.setBeginTime(new Timestamp(2483749250172L));
+        themeDetailRes.setEndTime(new Timestamp(2485614647000L));
+        themeDetailRes.setTitle("测试一下课程");
+        Group group = new Group();
+        group.setId(1L);
+        when(groupService.getGroupsByOwnerId(39154L)).thenReturn(Lists.newArrayList(group));
+        when(themeVOWrapper.unWrapperThemeDetail(themeDetailVO)).thenReturn(themeDetail);
+        when(themeService.getThemeDetailById(1L)).thenReturn(themeDetail);
+        when(themeVOWrapper.wrapperThemeDetail(themeDetail)).thenReturn(themeDetailVO);
+        when(themeStatusService.updateStatus(themeDetail)).thenReturn(0);
+        when(themeService.createThemeDetail(themeDetail)).thenReturn(themeDetailRes);
+        when(themeSchedulerService.createNewThemeScheduler(themeDetail)).thenReturn(true);
+
+        ThemeEntityRelations themeEntityRelationsExam = new ThemeEntityRelations();
+        themeEntityRelationsExam.setEntityId(111L);
+        themeEntityRelationsExam.setEntityType(EntityTypeEnum.EXAM);
+        themeEntityRelationsExam.setThemeId(1L);
+        themeEntityRelationsExam.setSortId(2);
+        List<ThemeEntityRelations> themeEntityRelationsList = new ArrayList<>();
+        themeEntityRelationsList.add(themeEntityRelations);
+        themeEntityRelationsList.add(themeEntityRelationsExam);
+        when(themeService.getThemeEntityRelations(1L)).thenReturn(themeEntityRelationsList);
+        when(themeEntityRelationsStatusService.updateStatusByTime(themeDetailRes.getBeginTime().getTime())).thenReturn(1);
+        when(themeService.createEntityRelation(themeDetailRes.getId(),themeEntityRelations.getEntityId(),themeEntityRelations.getEntityType(),themeEntityRelations.getSortId(),themeEntityRelations.isPreviewable(),themeDetail.getBeginTime(),1)).thenReturn(themeEntityRelations);
+        when(themeEntityRelationsSchedulerService.createEntityRelationScheduler(themeEntityRelations)).thenReturn(true);
+        themeLogic.forkThemeCourse(forkThemeDetailVO);
+    }
+
+    @Test
+    public void show_throw_when_fork_course_not_admin(){
+        when(session.getAttribute("userId")).thenReturn(39154L);
+        when(subject.hasRole("admin")).thenReturn(false);
+        ThemeDetailVO forkThemeDetailVO = new ThemeDetailVO();
+        forkThemeDetailVO.setId(1L);
+        forkThemeDetailVO.setVisibility(2);
+        forkThemeDetailVO.setBeginTime(1183749250172L);
+        forkThemeDetailVO.setEndTime(1185614647000L);
+        forkThemeDetailVO.setTitle("测试一下课程");
+        forkThemeDetailVO.setGroupId(1L);
+        ThemeDetailVO themeDetailVO = new ThemeDetailVO();
+        themeDetailVO.setId(1L);
+        themeDetailVO.setVisibility(2);
+        themeDetailVO.setBeginTime(1183749250172L);
+        themeDetailVO.setEndTime(1185614647000L);
+        themeDetailVO.setTitle("测试一下课程");
+        themeDetailVO.setGroupId(1L);
+        themeDetail.setVisibility(2);
+        themeDetail.setId(1L);
+        themeDetail.setBeginTime(new Timestamp(2483749250172L));
+        themeDetail.setEndTime(new Timestamp(2485614647000L));
+        themeDetail.setTitle("测试一下课程");
+
+        ThemeDetail themeDetailRes = new ThemeDetail();
+        themeDetailRes.setVisibility(2);
+        themeDetailRes.setId(1L);
+        themeDetailRes.setBeginTime(new Timestamp(2483749250172L));
+        themeDetailRes.setEndTime(new Timestamp(2485614647000L));
+        themeDetailRes.setTitle("测试一下课程");
+        Group group = new Group();
+        group.setId(1L);
+        when(groupService.getGroupsByOwnerId(39154L)).thenReturn(Lists.newArrayList(group));
+        when(themeVOWrapper.unWrapperThemeDetail(themeDetailVO)).thenReturn(themeDetail);
+        when(themeService.getThemeDetailById(1L)).thenReturn(themeDetail);
+        when(themeVOWrapper.wrapperThemeDetail(themeDetail)).thenReturn(themeDetailVO);
+        when(themeStatusService.updateStatus(themeDetail)).thenReturn(0);
+        when(themeService.createThemeDetail(themeDetail)).thenReturn(themeDetailRes);
+        when(themeSchedulerService.createNewThemeScheduler(themeDetail)).thenReturn(true);
+
+        ThemeEntityRelations themeEntityRelationsExam = new ThemeEntityRelations();
+        themeEntityRelationsExam.setEntityId(111L);
+        themeEntityRelationsExam.setEntityType(EntityTypeEnum.EXAM);
+        themeEntityRelationsExam.setThemeId(1L);
+        themeEntityRelationsExam.setSortId(2);
+        List<ThemeEntityRelations> themeEntityRelationsList = new ArrayList<>();
+        themeEntityRelationsList.add(themeEntityRelations);
+        themeEntityRelationsList.add(themeEntityRelationsExam);
+        when(themeService.getThemeEntityRelations(1L)).thenReturn(themeEntityRelationsList);
+        when(themeEntityRelationsStatusService.updateStatusByTime(themeDetailRes.getBeginTime().getTime())).thenReturn(1);
+        when(themeService.createEntityRelation(themeDetailRes.getId(),themeEntityRelations.getEntityId(),themeEntityRelations.getEntityType(),themeEntityRelations.getSortId(),themeEntityRelations.isPreviewable(),themeDetail.getBeginTime(),1)).thenReturn(themeEntityRelations);
+        when(themeEntityRelationsSchedulerService.createEntityRelationScheduler(themeEntityRelations)).thenReturn(true);
+        themeLogic.forkThemeCourse(forkThemeDetailVO);
+    }
+
+    @Test
+    public void show_success_when_getCourseByStudent(){
+        when(themeService.getThemeDetailById(1L)).thenReturn(themeDetail);
+        ThemeEntityRelations themeEntityRelationsExam = new ThemeEntityRelations();
+        themeEntityRelationsExam.setEntityId(111L);
+        themeEntityRelationsExam.setEntityType(EntityTypeEnum.EXAM);
+        themeEntityRelationsExam.setThemeId(1L);
+        themeEntityRelationsExam.setSortId(2);
+        List<ThemeEntityRelations> themeEntityRelationsList = new ArrayList<>();
+        themeEntityRelationsList.add(themeEntityRelations);
+        themeEntityRelationsList.add(themeEntityRelationsExam);
+        when(themeService.getThemeEntityRelations(1L)).thenReturn(themeEntityRelationsList);
+        List<CourseResource> courseResources = Lists.newArrayList(courseResource);
+
+        Exam exam = new Exam();
+        exam.setId(111L);
+        when(examService.getTasks(Lists.newArrayList(111L))).thenReturn(Lists.newArrayList(exam));
+        when(courseResourceService.getCourseResourceById(Lists.newArrayList(1L))).thenReturn(courseResources);
+        when(themeVOWrapper.wrapTheme2CourseVO(anyObject(), anyObject())).thenReturn(courseVO);
+
+        SubmitRecord submitRecord = new SubmitRecord();
+        submitRecord.setExamId(111L);
+        submitRecord.setId(1L);
+        submitRecord.setOwnerId(39154L);
+        List<SubmitRecord> submitRecordList = new ArrayList<>();
+        submitRecordList.add(submitRecord);
+        when(submitRecordService.getSubmitScoreList(39154L, Lists.newArrayList(111L))).thenReturn(submitRecordList);
+        themeLogic.getCourseByStudent(1L, 39154L);
+    }
+
+    @Test
+    public void show_success_when_getCourseByStudent_submitRecord_null(){
+        when(themeService.getThemeDetailById(1L)).thenReturn(themeDetail);
+        ThemeEntityRelations themeEntityRelationsExam = new ThemeEntityRelations();
+        themeEntityRelationsExam.setEntityId(111L);
+        themeEntityRelationsExam.setEntityType(EntityTypeEnum.EXAM);
+        themeEntityRelationsExam.setThemeId(1L);
+        themeEntityRelationsExam.setSortId(2);
+        List<ThemeEntityRelations> themeEntityRelationsList = new ArrayList<>();
+        themeEntityRelationsList.add(themeEntityRelations);
+        themeEntityRelationsList.add(themeEntityRelationsExam);
+        when(themeService.getThemeEntityRelations(1L)).thenReturn(themeEntityRelationsList);
+        List<CourseResource> courseResources = Lists.newArrayList(courseResource);
+
+        Exam exam = new Exam();
+        exam.setId(111L);
+        when(examService.getTasks(Lists.newArrayList(111L))).thenReturn(Lists.newArrayList(exam));
+        when(courseResourceService.getCourseResourceById(Lists.newArrayList(1L))).thenReturn(courseResources);
+        when(themeVOWrapper.wrapTheme2CourseVO(anyObject(), anyObject())).thenReturn(courseVO);
+
+        List<SubmitRecord> submitRecordList = new ArrayList<>();
+        when(submitRecordService.getSubmitScoreList(39154L, Lists.newArrayList(111L))).thenReturn(submitRecordList);
+        themeLogic.getCourseByStudent(1L, 39154L);
+    }
 }