Parcourir la source

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

Feature theme course



See merge request !1158

menduo il y a 5 ans
Parent
commit
04eb6330dd
26 fichiers modifiés avec 718 ajouts et 167 suppressions
  1. 31 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/StudyRecordDao.java
  2. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/Theme2GroupDao.java
  3. 15 2
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThemeDetailDao.java
  4. 3 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/CourseResource.java
  5. 44 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/StudyRecord.java
  6. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeDetail.java
  7. 10 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeEntityRelations.java
  8. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/Theme2GroupService.java
  9. 19 4
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeService.java
  10. 5 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/Theme2GroupServiceImpl.java
  11. 94 9
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeServiceImpl.java
  12. 86 111
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThemeController.java
  13. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseExamVO.java
  14. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseResourceVO.java
  15. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseVO.java
  16. 1 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/EntityVO.java
  17. 19 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ExamResultVO.java
  18. 6 2
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeDetailVO.java
  19. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeEntityRelationsVO.java
  20. 26 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeInfoVO.java
  21. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/CourseResourceVOWrapper.java
  22. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ExamVOWrapper.java
  23. 12 12
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ThemeVOWrapper.java
  24. 28 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ThemeLogic.java
  25. 9 4
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ExamLogicImpl.java
  26. 283 19
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ThemeLogicImpl.java

+ 31 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/StudyRecordDao.java

@@ -0,0 +1,31 @@
+package cn.iselab.mooctest.site.dao;
+
+import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
+import cn.iselab.mooctest.site.dao.adapter.UpdateAdapter;
+import cn.iselab.mooctest.site.models.Group2Worker;
+import cn.iselab.mooctest.site.models.StudyRecord;
+import org.mockito.internal.matchers.Find;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Component;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-02-12 19:59
+ */
+@Transactional
+public interface StudyRecordDao extends CrudRepository<StudyRecord, Long>, JpaSpecificationExecutor<StudyRecord>, UpdateAdapter<StudyRecord, Long> {
+    @Query("select s from StudyRecord s where s.themeId =:themeId and s.userId in (:studentsIds) and s.entityType =:entityType")
+    List<StudyRecord> countByThemeId(@Param("themeId") Long themeId, @Param("studentsIds") List<Long> studentsIds,@Param("entityType") EntityTypeEnum entityType);
+
+    StudyRecord findByThemeIdAndEntityIdAndUserIdAndEntityType(Long themeId, Long entityId, Long userId, EntityTypeEnum entityType);
+
+    List<StudyRecord> findByThemeIdAndUserIdAndEntityType(Long themeId, Long userId, EntityTypeEnum entityType);
+//    @Query("SELECT count(distinct tp.userId) FROM ExamPermission tp WHERE tp.instanceId = :instanceId and tp.operation=:operation")
+//    Integer countByHasPermission(@Param("instanceId")Long instanceId, @Param("operation")String operation);
+}

+ 1 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/Theme2GroupDao.java

@@ -15,6 +15,7 @@ import java.util.List;
 public interface Theme2GroupDao extends CrudRepository<Theme2Group, Long> {
     Theme2Group findByThemeIdAndGroupId(Long themeId, Long groupId);
     Theme2Group findByThemeId(Long themeId);
+    List<Theme2Group> findByGroupId(Long groupId);
 //    List<Theme2Group> findByUserId(Long userId);
 }
 

+ 15 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThemeDetailDao.java

@@ -7,9 +7,14 @@ import cn.iselab.mooctest.site.models.ThemeDetail;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 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;
 import java.util.List;
 
 /**
@@ -18,12 +23,20 @@ import java.util.List;
  * @author: menduo
  * @create: 2019-04-23 13:13
  **/
-@Component
-public interface ThemeDetailDao extends PagingAndSortingRepository<ThemeDetail,Long>, JpaSpecificationExecutor<ThemeDetail>, UpdateAdapter<ThemeDetail,Long> {
+@Transactional
+public interface ThemeDetailDao extends PagingAndSortingRepository<ThemeDetail,Long>, CrudRepository<ThemeDetail, Long>,JpaSpecificationExecutor<ThemeDetail>, UpdateAdapter<ThemeDetail,Long> {
     Page<ThemeDetail> findAllBy(Pageable pageable);
     List<ThemeDetail> findAll();
+
+    Iterable<ThemeDetail> findAllByIdAndType(Iterable<Long> iterable, int type);
+
     List<ThemeDetail> findAllByOwnerIdAndIsDeleted(Long ownerId, int isDeleted);
     List<ThemeDetail> findAllByOwnerId(Long ownerId);
     Page<ThemeDetail> findAllByTypeAndAndPublicStatus(Pageable pageable,int type, int publicStatus);
+
+    @Modifying
+    @Query("update ThemeDetail t set t.status = :status where t.id= :id")
+    void updateStatusById(@Param("id") Long id, @Param("status") int status);
+
     List<ThemeDetail> findAllByVisibilityIsNot(int visibility);
 }

+ 3 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/CourseResource.java

@@ -27,6 +27,9 @@ public class CourseResource {
     @Column(name = "video")
     private String video;
 
+    @Column(name = "video_duration")
+    private String videoDuration;
+
     @Column(name = "extra_file")
     private String extraFile;
 

+ 44 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/StudyRecord.java

@@ -0,0 +1,44 @@
+package cn.iselab.mooctest.site.models;
+
+import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+/**
+ * @program: mooctest-site
+ * @author: guochao
+ * @create: 2020-02-09 15:04
+ **/
+@Data
+@Entity
+@Table(name = "study_record")
+@NoArgsConstructor
+@AllArgsConstructor
+public class StudyRecord {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(name = "theme_id")
+    private Long themeId;
+
+    @Column(name = "user_id")
+    private Long userId;
+
+    @Column(name = "entity_id")
+    private Long entityId;
+
+    @Column(name = "entity_type")
+    @Enumerated(EnumType.STRING)
+    private EntityTypeEnum entityType;
+
+    public StudyRecord(Long themeId, Long userId, Long entityId, EntityTypeEnum entityType) {
+        this.themeId = themeId;
+        this.userId = userId;
+        this.entityId = entityId;
+        this.entityType = entityType;
+    }
+}

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThemeDetail.java

@@ -16,6 +16,16 @@ import java.sql.Timestamp;
 @Table(name = "theme_detail")
 @Data
 public class ThemeDetail {
+
+    public static final Integer STATUS_UPCOMING = 0;
+    public static final Integer STATUS_ONGOING = 1;
+//    public static final Integer STATUS_SCORING = 2;
+    public static final Integer STATUS_FINISHED = 2;
+
+//    public static final Byte TYPE_Web = 0;
+//    public static final Byte TYPE_mobile = 1;
+//    public static final Byte TYPE_python = 2;
+
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
@@ -35,12 +45,18 @@ public class ThemeDetail {
     @Column(name = "end_time")
     private Timestamp endTime;
 
+    @Column(name = "status")
+    private Integer status;
+
     @Column(name = "public")
     private int publicStatus;
 
     @Column(name = "type")
     private int type;
 
+    @Column(name = "course_type")
+    private int courseType;
+
     @Column(name = "is_deleted")
     private int isDeleted;
     @Column(name = "visibility")

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

@@ -31,9 +31,19 @@ public class ThemeEntityRelations {
     @Enumerated(EnumType.STRING)
     private EntityTypeEnum entityType;
 
+    @Column(name = "sort_id")
+    private int sortId;
+
     @Column(name = "is_previewable")
     private boolean previewable;
 
+    public ThemeEntityRelations(long themeId, long entityId, EntityTypeEnum entityType, int sortId) {
+        this.themeId = themeId;
+        this.entityId = entityId;
+        this.entityType = entityType;
+        this.sortId = sortId;
+    }
+
     public ThemeEntityRelations(long themeId, long entityId, EntityTypeEnum entityType) {
         this.themeId = themeId;
         this.entityId = entityId;

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

@@ -14,4 +14,5 @@ public interface Theme2GroupService {
     void create(Long groupId, Long themeId);
     Theme2Group findByGroupIdAndThemeId(Long groupId, Long themeId);
     Theme2Group findByThemeId(Long themeId);
+    List<Theme2Group> findByGroupId(Long groupId);
 }

+ 19 - 4
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeService.java

@@ -2,9 +2,11 @@ package cn.iselab.mooctest.site.service;
 
 import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
 import cn.iselab.mooctest.site.models.CourseResource;
-import cn.iselab.mooctest.site.models.Theme;
+import cn.iselab.mooctest.site.models.StudyRecord;
 import cn.iselab.mooctest.site.models.ThemeDetail;
 import cn.iselab.mooctest.site.models.ThemeEntityRelations;
+import cn.iselab.mooctest.site.web.data.CourseVO;
+import cn.iselab.mooctest.site.web.data.ThemeDetailVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
@@ -19,15 +21,15 @@ import java.util.List;
 public interface ThemeService {
     ThemeDetail createThemeDetail(ThemeDetail themeDetail);
     ThemeDetail updateThemeDetail(ThemeDetail themeDetail);
-    void deleteThemeDetailById(long id);
+    ThemeDetail deleteThemeDetailById(long id);
     ThemeDetail getThemeDetailById(long id);
     Page<ThemeDetail> getThemeDetails(Pageable pageable);
-    Page<ThemeDetail> getThemeDetails(Pageable pageable, String keyword, int type, int publicStatus);
+    Page<ThemeDetail> getThemeDetails(Pageable pageable, String keyword, int type);
     Page<ThemeDetail> getThemeDetailsByOwnerId(Long ownerId, String keyword, Pageable pageable);
     Page<CourseResource> getThemeCourseResources(Pageable pageable, String keyword);
     List<ThemeDetail> getThemeDetails();
     List<ThemeDetail> getThemeDetailList();
-    void getThemeDetailByIds(Object[] toArray);
+    List<ThemeDetail> getThemeDetailByIds(Long[] ids);
     List<ThemeDetail> getThemeDetailListByOwnerId(Long ownerId);
     List<ThemeEntityRelations> getThemeEntityRelations(long themeId);
     List<ThemeEntityRelations> getThemeEntityRelations(long themeId, EntityTypeEnum entityTypeEnum);
@@ -39,6 +41,19 @@ public interface ThemeService {
     ThemeEntityRelations addEntity(ThemeEntityRelations themeEntityRelations);
     void rmEntity(long id);
 
+    ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId);
+
+    void createStudyViewRecord(Long themeId, Long courseId, Long userId, EntityTypeEnum entityType);
+
+    int getStudyRecord(Long themeId, List<Long> studentIds, EntityTypeEnum entityType);
+
+    StudyRecord getStudyRecordByThemeId(Long themeId, Long courseId, Long userId, EntityTypeEnum entityType);
+
+    List<StudyRecord> getStudyCompletenessByThemeId(Long themeId, Long userId, EntityTypeEnum entityType);
+
+    Integer updateStatus(ThemeDetail themeDetail);
+
+    boolean canBeDeleted(Long themeId);
     ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType);
     List<ThemeDetail> getPlatformThemeDetails();
     List<ThemeDetail> getCourseByParticipant(Long participantId);

+ 5 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/Theme2GroupServiceImpl.java

@@ -33,4 +33,9 @@ public class Theme2GroupServiceImpl implements Theme2GroupService {
     public Theme2Group findByThemeId(Long themeId) {
         return theme2GroupDao.findByThemeId(themeId);
     }
+
+    @Override
+    public List<Theme2Group> findByGroupId(Long groupId) {
+        return theme2GroupDao.findByGroupId(groupId);
+    }
 }

+ 94 - 9
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeServiceImpl.java

@@ -1,5 +1,8 @@
 package cn.iselab.mooctest.site.service.impl;
 
+import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
+import cn.iselab.mooctest.site.dao.CourseResourceDao;
+import cn.iselab.mooctest.site.dao.StudyRecordDao;
 import cn.iselab.mooctest.site.common.constant.OwningPartyConstants;
 import cn.iselab.mooctest.site.common.enums.CourseVisibility;
 import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
@@ -9,9 +12,12 @@ import cn.iselab.mooctest.site.dao.ThemeDetailDao;
 import cn.iselab.mooctest.site.dao.ThemeEntityRelationsDao;
 import cn.iselab.mooctest.site.models.*;
 import cn.iselab.mooctest.site.service.ThemeService;
+import cn.iselab.mooctest.site.web.data.CourseVO;
+import cn.iselab.mooctest.site.web.exception.HttpBadRequestException;
 import com.google.common.collect.Lists;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.HttpResponseException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -23,6 +29,9 @@ import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -45,6 +54,8 @@ public class ThemeServiceImpl implements ThemeService {
     @Autowired
     private CourseResourceDao courseResourceDao;
     @Autowired
+    private StudyRecordDao studyRecordDao;
+    @Autowired
     private ParticipantCourseDao participantCourseDao;
 
     @Override
@@ -58,13 +69,26 @@ public class ThemeServiceImpl implements ThemeService {
     }
 
     @Override
-    public void deleteThemeDetailById(long id) {
-        themeDetailDao.deleteById(id);
+    public ThemeDetail deleteThemeDetailById(long id) {
+        Optional<ThemeDetail> themeDetailOptional = themeDetailDao.findById(id);
+        if(!themeDetailOptional.isPresent()){
+            throw new HttpBadRequestException(String.format("Theme[%d] doesn't exist.", id));
+        }else{
+            ThemeDetail themeDetail = themeDetailOptional.get();
+            themeDetail.setIsDeleted(1);
+            return themeDetailDao.save(themeDetail);
+        }
     }
 
     @Override
     public ThemeDetail getThemeDetailById(long id) {
-        return themeDetailDao.findOne(id);
+//        return themeDetailDao.findOne(id);
+        Optional<ThemeDetail> optionalThemeDetail = themeDetailDao.findById(id);
+        if(!optionalThemeDetail.isPresent()){
+            return null;
+        }else{
+            return optionalThemeDetail.get();
+        }
     }
 
     @Override
@@ -73,8 +97,8 @@ public class ThemeServiceImpl implements ThemeService {
     }
 
     @Override
-    public Page<ThemeDetail> getThemeDetails(Pageable pageable, String keyword, int type, int publicStatus) {
-        Specifications<ThemeDetail> where =  Specifications.where(getThemeDetailWhereClause(keyword, type, publicStatus));
+    public Page<ThemeDetail> getThemeDetails(Pageable pageable, String keyword, int type) {
+        Specifications<ThemeDetail> where =  Specifications.where(getThemeDetailWhereClause(keyword, type));
         return themeDetailDao.findAll(where, pageable);
     }
 
@@ -95,6 +119,7 @@ public class ThemeServiceImpl implements ThemeService {
                     );
                 }
                 predicate.getExpressions().add(cb.equal(a.get("ownerId"),ownerId));
+                predicate.getExpressions().add(cb.equal(a.get("isDeleted"),0));
                 return predicate;
             }
         };
@@ -106,7 +131,7 @@ public class ThemeServiceImpl implements ThemeService {
         return courseResourceDao.findAll(where, pageable);
     }
 
-    private Specification<ThemeDetail> getThemeDetailWhereClause(String keyword, int type, int publicStatus) {
+    private Specification<ThemeDetail> getThemeDetailWhereClause(String keyword, int type) {
         return new Specification<ThemeDetail>() {
             @Override
             public Predicate toPredicate(Root<ThemeDetail> a, CriteriaQuery<?> q, CriteriaBuilder cb) {
@@ -117,7 +142,8 @@ public class ThemeServiceImpl implements ThemeService {
                     );
                 }
                 predicate.getExpressions().add(cb.equal(a.get("type"),type));
-                predicate.getExpressions().add(cb.equal(a.get("publicStatus"),publicStatus));
+                predicate.getExpressions().add(cb.equal(a.get("isDeleted"),0));
+//                predicate.getExpressions().add(cb.equal(a.get("publicStatus"),publicStatus));
                 return predicate;
             }
         };
@@ -150,8 +176,8 @@ public class ThemeServiceImpl implements ThemeService {
     }
 
     @Override
-    public void getThemeDetailByIds(Object[] toArray) {
-
+    public List<ThemeDetail> getThemeDetailByIds(Long[] ids) {
+        return (List<ThemeDetail>) themeDetailDao.findAllById(Arrays.asList(ids));
     }
 
     @Override
@@ -175,6 +201,20 @@ public class ThemeServiceImpl implements ThemeService {
     }
 
     @Override
+    public ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId) {
+        ThemeEntityRelations themeEntityRelations = new ThemeEntityRelations(themeId, entityId, entityType, sortId);
+        return themeEntityRelationsDao.save(themeEntityRelations);
+//        ThemeEntityRelations themeEntityRelations = checkRelationBetweenThemeAndEntity(themeId, entityId, entityType);
+//        if(themeEntityRelations !=null) {
+//            log.error(String.format("themeId:{%s}-entityId:{%s}-type:{%s} 已经建立关联",themeId, entityId, entityType.toString()));
+//            throw new NullPointerException("该theme中不存在该" + entityType.toString());
+//        }else {
+//            themeEntityRelations = new ThemeEntityRelations(themeId, entityId, entityType, sortId);
+//            return themeEntityRelationsDao.save(themeEntityRelations);
+//        }
+    }
+
+    @Override
     public ThemeEntityRelations createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType) {
         ThemeEntityRelations themeEntityRelations = checkRelationBetweenThemeAndEntity(themeId, entityId, entityType);
         if(themeEntityRelations !=null) {
@@ -186,6 +226,28 @@ public class ThemeServiceImpl implements ThemeService {
         }
     }
 
+
+
+    @Override
+    public void createStudyViewRecord(Long themeId, Long courseId, Long userId, EntityTypeEnum entityType) {
+        studyRecordDao.save(new StudyRecord(themeId, userId, courseId, entityType));
+    }
+
+    @Override
+    public int getStudyRecord(Long themeId, List<Long> studentIds, EntityTypeEnum entityType) {
+        return studyRecordDao.countByThemeId(themeId, studentIds, entityType).size();
+    }
+
+    @Override
+    public StudyRecord getStudyRecordByThemeId(Long themeId, Long courseResourceId, Long userId, EntityTypeEnum entityType) {
+        return studyRecordDao.findByThemeIdAndEntityIdAndUserIdAndEntityType(themeId, courseResourceId, userId, entityType);
+    }
+
+    @Override
+    public List<StudyRecord> getStudyCompletenessByThemeId(Long themeId, Long userId, EntityTypeEnum entityType) {
+        return studyRecordDao.findByThemeIdAndUserIdAndEntityType(themeId, userId, entityType);
+    }
+
     @Override
     public List<ThemeDetail> getPlatformThemeDetails() {
         return themeDetailDao.findAllByVisibilityIsNot(CourseVisibility.NONE_OPEN.getVisibility());
@@ -243,4 +305,27 @@ public class ThemeServiceImpl implements ThemeService {
         themeEntityRelationsDao.deleteById(id);
     }
 
+    @Override
+    public Integer updateStatus(ThemeDetail themeDetail) {
+        int status = 0;
+        if(themeDetail.getStatus() == ThemeDetail.STATUS_FINISHED){
+            return  themeDetail.getStatus();
+        }
+        long curr = System.currentTimeMillis();
+        if (themeDetail.getBeginTime().getTime() > curr) {
+            status = ThemeDetail.STATUS_UPCOMING;
+        } else if (themeDetail.getEndTime().getTime() > curr) {
+            status = ThemeDetail.STATUS_ONGOING;
+        } else {
+            status = ThemeDetail.STATUS_FINISHED;
+        }
+        themeDetailDao.updateStatusById(themeDetail.getId(),status);
+        return status;
+    }
+
+    @Override
+    public boolean canBeDeleted(Long themeId) {
+        return true;
+    }
+
 }

+ 86 - 111
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThemeController.java

@@ -3,7 +3,9 @@ package cn.iselab.mooctest.site.web.ctrl;
 import cn.iselab.mooctest.site.common.constant.Constants;
 import cn.iselab.mooctest.site.common.constant.UrlConstants;
 import cn.iselab.mooctest.site.models.Theme;
+import cn.iselab.mooctest.site.models.Theme2Group;
 import cn.iselab.mooctest.site.models.ThemeDetail;
+import cn.iselab.mooctest.site.models.instancePermission.ExamPermission;
 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;
@@ -13,13 +15,17 @@ import cn.iselab.mooctest.site.web.logic.ThemeLogic;
 import com.google.gson.Gson;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.UnauthenticatedException;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.*;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.xml.ws.Response;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @program: mooctest-site
@@ -61,7 +67,7 @@ public class ThemeController extends BaseSearchController {
      * @return
      */
     @RequestMapping(value = UrlConstants.API + "theme/views/{ownerId}",method = RequestMethod.GET)
-//    @RequiresPermissions(value = "theme:view")
+    @RequiresPermissions(value = "theme:view")
     public ResponseVO<Page<ThemeDetailVO>> showThemeViews(@PathVariable("ownerId")Long ownerId, @RequestParam(name = "searchCondition") String searchCondition){
         Gson gson = new Gson();
         SearchConditionVO searchConditionVO = gson.fromJson(searchCondition, SearchConditionVO.class);
@@ -72,12 +78,12 @@ public class ThemeController extends BaseSearchController {
     }
 
     /**
-     * 查看指定的theme
+     * 查看指定的theme  舍弃
      * @param themeId
      * @return
      */
     @RequestMapping(value = UrlConstants.API + "theme/view/{themeId}",method = RequestMethod.GET)
-//    @RequiresPermissions(value = "theme:view")
+    @RequiresPermissions(value = "theme:view")
     public ResponseVO<ThemeVO> showThemeViewById(@PathVariable("themeId")Long themeId){
         ThemeVO themeVO = themeLogic.getTheme(themeId);
         return new ResponseVO<>(ServerCode.SUCCESS, themeVO);
@@ -102,27 +108,11 @@ public class ThemeController extends BaseSearchController {
      * @return
      */
     @RequestMapping(value = UrlConstants.API + "theme/create", method = RequestMethod.POST)
-//    @RequiresPermissions(value = "theme:create")
+    @RequiresPermissions(value = "theme:create")
     public ResponseVO<CourseVO> createThemeCourse(@RequestBody CourseVO courseVO) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        // 主题信息
-        ThemeDetailVO themeDetailVO = courseVO.getThemeDetailVO();
-        if (themeDetailVO == null) return new ResponseVO<>(ServerCode.SYSTEM_ERROR,null);
-        themeDetailVO.setOwnerId(userId);
-        themeDetailVO = themeLogic.newThemeDetail(themeDetailVO);
-
-        // 课程中的实体信息(课程信息、考试信息、练习信息) 可否为空
-        List<EntityVO> entityVOList = courseVO.getEntityVOList();
-        if(entityVOList !=null){
-            for(EntityVO entityVO: entityVOList){
-                entityVO.setThemeId(themeDetailVO.getId());
-                ThemeEntityRelationsVO entityRelation = themeLogic.createEntityRelation(themeDetailVO.getId(), entityVO.getEntityId(), entityVO.getEntityType());
-                entityVO.setSortedId(entityRelation.getId());
-            }
-        }else{
-            log.info("课程中没有实体信息");
-        }
-        return new ResponseVO<>(ServerCode.SUCCESS,courseVO);
+        CourseVO courseResultVO  = themeLogic.newThemeCourse(courseVO, userId);
+        return new ResponseVO<>(ServerCode.SUCCESS,courseResultVO);
     }
 
     /**
@@ -131,28 +121,12 @@ public class ThemeController extends BaseSearchController {
      * @return
      */
     @RequestMapping(value = UrlConstants.API + "theme/update", method = RequestMethod.PUT)
-//    @RequiresPermissions(value = "theme:update")
+    @RequiresPermissions(value = "theme:create")
     public ResponseVO<CourseVO> updateThemeCourse(@RequestBody CourseVO courseVO) {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        // 主题信息
-        ThemeDetailVO themeDetailVO = courseVO.getThemeDetailVO();
-        if (themeDetailVO == null) return new ResponseVO<>(ServerCode.SYSTEM_ERROR,null);
-        themeDetailVO = themeLogic.updateThemeDetail(themeDetailVO);
-
-        // 删除课程中的实体信息
-        boolean b = themeLogic.deleteEntityRelationByThemeId(themeDetailVO.getId());
-        // 课程中的实体信息(课程信息、考试信息、练习信息) 可否为空
-        List<EntityVO> entityVOList = courseVO.getEntityVOList();
-        if(entityVOList !=null){
-            for(EntityVO entityVO: entityVOList){
-                entityVO.setThemeId(themeDetailVO.getId());
-                ThemeEntityRelationsVO entityRelation = themeLogic.createEntityRelation(themeDetailVO.getId(), entityVO.getEntityId(), entityVO.getEntityType());
-                entityVO.setSortedId(entityRelation.getId());
-            }
-        }else{
-            log.info("课程中没有实体信息");
-        }
-        return new ResponseVO<>(ServerCode.SUCCESS,courseVO);
+        themeLogic.updateThemeCourse(courseVO, userId);
+        CourseVO courseResultVO = themeLogic.getCourse(courseVO.getThemeDetailVO().getId(), userId);
+        return new ResponseVO<>(ServerCode.SUCCESS,courseResultVO);
     }
 
     /**
@@ -161,7 +135,7 @@ public class ThemeController extends BaseSearchController {
      * @return
      */
     @GetMapping(value = UrlConstants.API + "search/course/resource")
-//    @RequiresPermissions(value = "theme:create")
+    @RequiresPermissions(value = "theme:create")
     public ResponseVO<Page<CourseResourceVO>> searchCourseResource(@RequestParam(name = "searchCondition") String searchCondition) {
         Gson gson = new Gson();
         SearchConditionVO searchConditionVO = gson.fromJson(searchCondition, SearchConditionVO.class);
@@ -178,19 +152,52 @@ public class ThemeController extends BaseSearchController {
      * @param searchCondition
      * @return
      */
-    @GetMapping(value = UrlConstants.API + "search/theme/course")
-//    @RequiresPermissions(value = "theme:create")
-    public ResponseVO<Page<CourseVO>> searchThemeByCourseResource(@RequestParam(name = "searchCondition") String searchCondition) {
+    @GetMapping(value = UrlConstants.API + "search/theme/type/{type}")
+    @RequiresPermissions(value = "theme:create")
+    public ResponseVO<Page<CourseVO>> searchThemeByCourseResource(@PathVariable("type") int type,@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());
-        Page<CourseResourceVO> courseResourceVOS = themeLogic.getThemeCourseResourceList(pageable, keyword);
-        Object[] themeIdListByCourse = themeLogic.getThemeListByCourse(pageable, keyword);
-//        return new ResponseVO<>(ServerCode.SUCCESS,courseVO);
-        return null;
+//        Page<CourseResourceVO> courseResourceVOS = themeLogic.getThemeCourseResourceList(pageable, keyword);
+        Long[] themeIdListByCourse = themeLogic.getThemeListByCourse(pageable, keyword);
+        System.out.println(themeIdListByCourse);
+        // 获取所有themeId的课程章节信息
+        Page<CourseVO> courseVOList = themeLogic.getCoursesByThemeIds(themeIdListByCourse, pageable, (long)themeIdListByCourse.length, type);// type 0 代表所有课程
+        return new ResponseVO<>(ServerCode.SUCCESS,courseVOList);
+    }
+
+    /**
+     * 获取课程教学信息 分数
+     * @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");
+//        }
+        return themeLogic.getThemeInfoByThemeId(themeId);
+    }
+
+    /**
+     * 学生点击视频记录
+     */
+    @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);
     }
 
     /**
@@ -199,7 +206,7 @@ public class ThemeController extends BaseSearchController {
      * @return
      */
     @GetMapping(value = UrlConstants.API + "search/type/{type}/groupId/{groupId}")
-//    @RequiresPermissions(value = "theme:create")
+    @RequiresPermissions(value = "theme:create")
     public ResponseVO<Page<ExamVO>> searchExamByGroupId(@PathVariable("type") int type,
                                                         @PathVariable("groupId") long groupId,
                                                         @RequestParam(name = "searchCondition") String searchCondition) {
@@ -214,70 +221,27 @@ public class ThemeController extends BaseSearchController {
     }
 
     /**
-     * 添加主题时搜索考试或者练习(仅限于自己创建的未开始的考试,需要检验班级是否一致)
-     * @param searchCondition
+     * 删除主题课程
+     * @param themeId
      * @return
      */
-    @GetMapping(value = UrlConstants.API + "search/{type}")
-//    @RequiresPermissions(value = "theme:create")
-    public ResponseVO<Page<ExamVO>> searchExam(@PathVariable("type") int type, @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());
-        Page<ExamVO> examVOS = examLogic.getExamListByOrganizerId(userId, type, keyword, pageable); //exam: 0, exercise: 1, activity: 2
-        return new ResponseVO<>(ServerCode.SUCCESS,examVOS);
-    }
-
-//    /**
-//     * 添加主题时搜索练习
-//     * @param searchCondition
-//     * @return
-//     */
-//    @GetMapping(value = UrlConstants.API + "search/exercise")
-////    @RequiresPermissions(value = "theme:create")
-//    public ResponseVO<Page<ExamVO>> searchExercise(@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());
-//        Page<ExamVO> examVOS = examLogic.getExamListByOrganizerId(userId, 1, keyword, pageable); //exam: 0, exercise: 1, activity: 2
-//        return new ResponseVO<>(ServerCode.SUCCESS,examVOS);
-//    }
-//    @RequestMapping(value = UrlConstants.API + "theme/create", method = RequestMethod.POST)
-//    @RequiresPermissions(value = "theme:create")
-//    public ResponseVO<ThemeDetailVO> createThemeDetails(@RequestBody ThemeDetailVO themeDetailVO) {
-//        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-//        themeDetailVO.setOwnerId(userId);
-//        themeDetailVO = themeLogic.newThemeDetail(themeDetailVO);
-//        if (themeDetailVO == null) return new ResponseVO<>(ServerCode.SYSTEM_ERROR,null);
-//        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}", method = RequestMethod.DELETE)
+    public ResponseVO<ThemeDetailVO> deleteExamById(@PathVariable("themeId") Long themeId){
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+//        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 {
+//            throw new UnauthenticatedException("Operation forbidden!");
+//        }
     }
 
-    @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:remove")
     public ResponseVO<Boolean> removeCaseFromtheme(@PathVariable("themeId")long themeId, @PathVariable("caseId") long caseId) {
@@ -356,6 +320,7 @@ public class ThemeController extends BaseSearchController {
     @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);
         if(courseVO.getThemeDetailVO().getType() != 0) {
             log.info("这个不是一个课程:{}, 访问用户为:{}",courseId ,userId);
@@ -364,7 +329,6 @@ public class ThemeController extends BaseSearchController {
         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);
@@ -375,4 +339,15 @@ public class ThemeController extends BaseSearchController {
         }
         return new ResponseVO<>(ServerCode.SUCCESS, courseResourceVO);
     }
+
+    @GetMapping(value = UrlConstants.API + "course/resource/{resourceId}")
+    public ResponseVO<CourseResourceVO> getResource(@PathVariable("resourceId")Long resourceId) {
+        CourseResourceVO courseResourceVO = themeLogic.getCourseResourceById(resourceId);
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        log.info("访问resource:{},用户为:{}",resourceId ,userId);
+        if(courseResourceVO == null) {
+            return new ResponseVO<>(ServerCode.ENTITY_NOT_A_COURSE_RESOURCE, null);
+        }
+        return new ResponseVO<>(ServerCode.SUCCESS, courseResourceVO);
+    }
 }

+ 1 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/CourseExamVO.java

@@ -15,4 +15,5 @@ public class CourseExamVO extends EntityVO {
     private Long beginTime;
     private Long endTime;
     private String info;
+    private int type;
 }

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

@@ -12,8 +12,10 @@ import lombok.Data;
 public class CourseResourceVO extends EntityVO {
     private Long id;
     private String title;
+    private String name;
     private String slider;
     private String video;
+    private String videoDuration;
     private String extraFile;
     private String sliderImagesPath;
     private Integer sliderImagesCount;

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

@@ -13,4 +13,6 @@ import lombok.Data;
 public class CourseVO {
     ThemeDetailVO themeDetailVO;
     List<EntityVO> entityVOList;
+    List<Long> completeCourseRecourseId;
+    List<Long> totalCourseRecourseId;
 }

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

@@ -14,5 +14,5 @@ public class EntityVO {
     private long themeId;
     private long entityId;
     private EntityTypeEnum entityType;
-    private long sortedId;
+    private int sortedId;
 }

+ 19 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ExamResultVO.java

@@ -0,0 +1,19 @@
+package cn.iselab.mooctest.site.web.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-02-12 17:13
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ExamResultVO {
+    private ExamVO examVO;
+    private List<Double> scoreList;
+}

+ 6 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeDetailVO.java

@@ -2,6 +2,8 @@ package cn.iselab.mooctest.site.web.data;
 
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * @program: mooctest-site
  * @mail: menduo96@gmail.com
@@ -16,12 +18,14 @@ public class ThemeDetailVO {
     private String introduce;
     private Long beginTime;
     private Long endTime;
+    private Integer status;
     private Long groupId;
     private String groupName;
+    private int courseType;
     private boolean hasPurchase;
     private int publicStatus;
     private int type;
-
     private int visibility;
-
+    private int isDeleted;
+    List<EntityVO> entityVOList;
 }

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

@@ -15,4 +15,5 @@ public class ThemeEntityRelationsVO {
     private long themeId;
     private long entityId;
     private EntityTypeEnum entityType;
+    private int sortId;
 }

+ 26 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/ThemeInfoVO.java

@@ -0,0 +1,26 @@
+package cn.iselab.mooctest.site.web.data;
+
+import cn.iselab.mooctest.site.models.Exam;
+import cn.iselab.mooctest.site.models.ThemeDetail;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author guochao
+ * @date 2020-02-12 16:31
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ThemeInfoVO {
+    private ThemeDetailVO themeDetailVO;
+    private int studentNum;
+    private float teacherCompletedRate;
+    private float learningCompletedRate;
+    private float ExerciseCompletedRate;
+    private float ExamCompletedRate;
+    private List<ExamResultVO> examResultVOList;
+}

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

@@ -23,7 +23,9 @@ public class CourseResourceVOWrapper {
         CourseResourceVO courseResourceVO = new CourseResourceVO();
         courseResourceVO.setId(courseResource.getId());
         courseResourceVO.setTitle(courseResource.getTitle());
+        courseResourceVO.setName(courseResource.getTitle());
         courseResourceVO.setVideo(courseResource.getVideo());
+        courseResourceVO.setVideoDuration(courseResource.getVideoDuration());
         courseResourceVO.setSlider(courseResource.getSlider());
         courseResourceVO.setSliderImagesPath(courseResource.getSliderImagesPath());
         courseResourceVO.setSliderImagesCount(courseResource.getSliderImagesCount());

+ 1 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/ExamVOWrapper.java

@@ -74,6 +74,7 @@ public class ExamVOWrapper extends BaseWrapper<ExamVO, Exam> {
         vo.setBeginTime(exam.getBeginTime().getTime());
         vo.setEndTime(exam.getEndTime().getTime());
         vo.setInfo(exam.getInformation());
+        vo.setType(exam.getType());
         return vo;
     }
 

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

@@ -13,6 +13,8 @@ import cn.iselab.mooctest.site.models.ThemeEntityRelations;
 import cn.iselab.mooctest.site.service.User2ThemeService;
 import cn.iselab.mooctest.site.web.data.*;
 import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.util.SystemOutLogger;
 import com.google.common.collect.Maps;
 import com.oracle.tools.packager.Log;
 import lombok.extern.slf4j.Slf4j;
@@ -22,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.sql.Timestamp;
-import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
@@ -71,21 +72,20 @@ public class ThemeVOWrapper {
         themeDetailVO.setBeginTime(themeDetail.getBeginTime().getTime());
         themeDetailVO.setEndTime(themeDetail.getEndTime().getTime());
         BeanUtils.copyProperties(themeDetail,themeDetailVO);
-//        themeDetailVO.setId(themeDetail.getId());
-//        themeDetailVO.setTitle(themeDetail.getTitle());
-//        themeDetailVO.setOwnerId(themeDetail.getOwnerId());
-//        themeDetailVO.setIntroduce(themeDetail.getIntroduce());
-//        themeDetailVO.setType(themeDetail.getType());
         //是否购买了该主题
         if(themeDetail.getPublicStatus() == 0) {
             themeDetailVO.setHasPurchase(varifyIfPurchase(themeDetail.getId()));
         }
 
         Theme2Group theme2Group = theme2GroupService.findByThemeId(themeDetail.getId());
-        Group group = groupService.getGroupById(theme2Group.getGroupId());
-        themeDetailVO.setGroupId(group.getId());
-        themeDetailVO.setGroupName(group.getName());
-        System.out.println(themeDetailVO.toString());
+        if(theme2Group != null){
+            Group group = groupService.getGroupById(theme2Group.getGroupId());
+            themeDetailVO.setGroupId(group.getId());
+            themeDetailVO.setGroupName(group.getName());
+        }else{
+            log.info("无对应班级实体");
+
+        }
         return themeDetailVO;
     }
 
@@ -138,7 +138,7 @@ public class ThemeVOWrapper {
                     .map(examVOWrapper::wrap2CourseExam)
                     .peek(courseExamVO -> {
                         ThemeEntityRelations themeEntityRelations = examRelationMap.get(courseExamVO.getId());
-                        courseExamVO.setSortedId(themeEntityRelations.getId());
+                        courseExamVO.setSortedId(themeEntityRelations.getSortId());
                         courseExamVO.setEntityId(themeEntityRelations.getEntityId());
                         courseExamVO.setEntityType(themeEntityRelations.getEntityType());
                     }).collect(Collectors.toList());
@@ -149,7 +149,7 @@ public class ThemeVOWrapper {
                     .stream().map(courseResourceVOWrapper::wrap)
                     .peek(courseExamVO -> {
                         ThemeEntityRelations themeEntityRelations = courseRelationMap.get(courseExamVO.getId());
-                        courseExamVO.setSortedId(themeEntityRelations.getId());
+                        courseExamVO.setSortedId(themeEntityRelations.getSortId());
                         courseExamVO.setEntityId(themeEntityRelations.getEntityId());
                         courseExamVO.setEntityType(themeEntityRelations.getEntityType());
                         courseExamVO.setPreviewable(themeEntityRelations.isPreviewable());

+ 28 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ThemeLogic.java

@@ -1,10 +1,13 @@
 package cn.iselab.mooctest.site.web.logic;
 
 import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
+import cn.iselab.mooctest.site.models.Theme2Group;
 import cn.iselab.mooctest.site.models.ThemeEntityRelations;
 import cn.iselab.mooctest.site.web.data.*;
 
 import java.util.List;
+
+import com.sun.org.apache.xpath.internal.operations.Bool;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
@@ -18,12 +21,17 @@ import org.springframework.data.domain.Pageable;
 public interface ThemeLogic{
 
     //new a theme
-    ThemeDetailVO newThemeDetail(ThemeDetailVO themeDetailVO);
+    CourseVO newThemeCourse(CourseVO courseVO, Long userId);
+
     Page<ThemeDetailVO> getThemeDetailList(Pageable pageable);
 
+    CourseVO updateThemeCourse(CourseVO courseVO, Long userId);
+
     ThemeDetailVO updateThemeDetail(ThemeDetailVO themeDetailVO);
 
     //list themes
+    Page<ThemeDetailVO> getThemeList(Pageable pageable, String keyword, int type);
+
     List<ThemeDetailVO> getThemeList(Pageable pageable, String keyword);
 
     Page<ThemeDetailVO> getThemeDetailListByOwnerId(Long ownerId, String keyword, Pageable pageable);
@@ -31,18 +39,23 @@ public interface ThemeLogic{
     // list themeCourseResource
     Page<CourseResourceVO> getThemeCourseResourceList(Pageable pageable, String keyword);
 
-    Object[] getThemeListByCourse(Pageable pageable, String keyword);
+    Long[] getThemeListByCourse(Pageable pageable, String keyword);
 
     List<ThemeDetailVO> getThemeDetailListByCase(long caseId);
     List<ThemeDetailVO> getThemeDetailListByPaper(long paperId);
     List<ThemeDetailVO> getThemeDetailListByExam(long examId);
 
     ThemeVO getTheme(Long id);
+
     CourseVO getCourse(Long id);
 
+    CourseVO getCourse(Long id, Long userId);
+
+    Page<CourseVO> getCoursesByThemeIds(Long[] themeIdListByCourse, Pageable pageable, long total, int type);
+
     List<CourseVO> getCourses();
 
-    ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType);
+    ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId);
 
     CaseExtendsVO createRelationBetweenThemeAndCase(long themeId, long caseId);
 
@@ -60,6 +73,18 @@ public interface ThemeLogic{
 
     void buyTheme(long userId,long themeId);
 
+    CourseResourceVO getCourseResourceById(long resourceId);
+
+    ThemeInfoVO getThemeInfoByThemeId(Long themeId);
+
+    void createStudyRecord(Long themeId, Long courseId, Long userId);
+
+    List<Theme2Group> getThemeListByGroup(Long groupId);
+
+    ThemeDetailVO getThemeDetailByThemeId(Long themeId);
+
+    ThemeDetailVO deleteThemeDetailById(Long themeId);
+
     CourseResourceVO getCourseResource(long courseId, long resourceId);
 
 }

+ 9 - 4
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ExamLogicImpl.java

@@ -157,10 +157,15 @@ public class ExamLogicImpl extends BaseLogic implements ExamLogic {
 
     @Override
     public Page<ExamVO> getExamListByOrganizerIdAndGroupId(Long userId, int type, long groupId, String keyword, Pageable pageable) {
-        Page<Exam> taskPage = examService.getExamsByOrganizerIdAndGroupId(userId, type, groupId, keyword, pageable);
-//        List<Exam> examList = examService.getTaskListByGroupId(groupId);
-        Page<ExamVO> examVOPage = taskPage.map( task -> examVOWrapper.wrapForList(task));
-        return examVOPage;
+        Group group = groupService.getGroup(groupId);
+        if (group == null) {
+            throw new HttpBadRequestException("未选择班级");
+        }else{
+            Page<Exam> taskPage = examService.getExamsByOrganizerIdAndGroupId(userId, type, groupId, keyword, pageable);
+            Page<ExamVO> examVOPage = taskPage.map( task -> examVOWrapper.wrapForList(task));
+            return examVOPage;
+        }
+
     }
 
     @Override

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

@@ -1,15 +1,19 @@
 package cn.iselab.mooctest.site.web.logic.impl;
 
+import cn.iselab.mooctest.site.common.enums.EntityType;
 import cn.iselab.mooctest.site.common.enums.CourseVisibility;
 import cn.iselab.mooctest.site.common.enums.EntityTypeEnum;
 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.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;
@@ -18,6 +22,7 @@ import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.crypto.hash.Hash;
 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;
@@ -26,10 +31,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -49,7 +51,9 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Autowired
     private PaperVOWrapper paperVOWrapper;
     @Autowired
-    private ExamVOWrapper themeDetailVOWrapper;
+    private ThemeVOWrapper themeDetailVOWrapper;
+    @Autowired
+    ExamVOWrapper examVOWrapper;
     @Autowired
     private ThemeService themeService;
     @Autowired
@@ -69,6 +73,8 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Autowired
     Theme2GroupService theme2GroupService;
     @Autowired
+    SubmitRecordService submitRecordService;
+    @Autowired
     private OSSLogic ossLogic;
     @Autowired
     private CourseResourceService courseResourceService;
@@ -80,9 +86,49 @@ public class ThemeLogicImpl implements ThemeLogic {
 
 
     private static final String BASE_DIR = "theme";
+
     @Override
-    public ThemeDetailVO newThemeDetail(ThemeDetailVO themeDetailVO) {
+    public CourseVO newThemeCourse(CourseVO courseVO, Long userId) {
+        // 主题信息
+        ThemeDetailVO themeDetailVO = courseVO.getThemeDetailVO();
+        if (themeDetailVO == null) throw new HttpBadRequestException("主题内容为空");;
+        themeDetailVO.setOwnerId(userId);
+        themeDetailVO.setStatus(ThemeDetail.STATUS_UPCOMING);
+        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){
+                checkThemeCourseGroupAndExam(themeDetailVO, entityVOList);
+                // 条件符合,可以创建
+                ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
+                themeDetail = themeService.createThemeDetail(themeDetail);
+                themeDetailVO.setId(themeDetail.getId());
+                theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
+                int index = 0;
+                for(EntityVO entityVO: entityVOList){
+                    index++;
+                    entityVO.setThemeId(themeDetailVO.getId());
+                    ThemeEntityRelationsVO entityRelation = createEntityRelation(themeDetailVO.getId(), entityVO.getEntityId(), entityVO.getEntityType(), index);
+                    entityVO.setSortedId(entityRelation.getSortId());
+                }
+            }else{
+                // 条件符合,可以创建
+                ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
+                themeDetail = themeService.createThemeDetail(themeDetail);
+                themeDetailVO.setId(themeDetail.getId());
+                theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
+                log.info("课程中没有实体信息");
+            }
+        } else {
+            throw new HttpBadRequestException("No right for some group");
+        }
+        return courseVO;
+    }
 
+    private void checkThemeInfo(ThemeDetailVO themeDetailVO) {
+        // 检查主题信息
         long currTime = System.currentTimeMillis();
         boolean isThemeTimeInvalid = currTime > themeDetailVO.getBeginTime() || themeDetailVO.getBeginTime() > themeDetailVO.getEndTime();
         if (isThemeTimeInvalid) {
@@ -99,21 +145,68 @@ public class ThemeLogicImpl implements ThemeLogic {
                 throw new HttpBadRequestException("主题课程名称已存在");
             }
         }
+    }
 
-        if (themeDetailVO.getGroupId() != null && groupService.getGroupsByOwnerId(themeDetailVO.getOwnerId()).stream()
-                .map(Group::getId).collect(Collectors.toList()).contains(themeDetailVO.getGroupId())) {
 
+    @Override
+    public CourseVO updateThemeCourse(CourseVO courseVO, Long userId) {
+        // 主题信息
+        ThemeDetailVO themeDetailVO = courseVO.getThemeDetailVO();
+        if (!themeDetailVO.getOwnerId().equals(userId)) throw new HttpBadRequestException("当前用户无修改此课程的权限");
+        if (themeDetailVO == null) throw new HttpBadRequestException("主题内容为空");
+//        long currTime = System.currentTimeMillis();
+//        boolean isThemeTimeInvalid = currTime > themeDetailVO.getBeginTime() || themeDetailVO.getBeginTime() > themeDetailVO.getEndTime();
+//        if (isThemeTimeInvalid) {
+//            throw new HttpBadRequestException("主题课程时间无效");
+//        }
+
+        // 课程中的实体信息(课程信息、考试信息、练习信息)
+        List<EntityVO> entityVOList = courseVO.getEntityVOList();
+        // 删除原有的实体
+        deleteEntityRelationByThemeId(themeDetailVO.getId());
+        if(entityVOList.size() != 0){
+            checkThemeCourseGroupAndExam(themeDetailVO, entityVOList);
+            ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
+            Integer status = themeService.updateStatus(themeDetail);
+            themeDetail.setStatus(status);
+            themeService.updateThemeDetail(themeDetail);
+            // 条件符合,可以更新
+            int index = 0;
+            for(EntityVO entityVO: entityVOList){
+                index++;
+                entityVO.setThemeId(themeDetailVO.getId());
+                ThemeEntityRelationsVO entityRelation = createEntityRelation(themeDetailVO.getId(), entityVO.getEntityId(), entityVO.getEntityType(), index);
+                entityVO.setSortedId(entityRelation.getSortId());
+            }
+        }else{
             ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
-            themeDetail = themeService.createThemeDetail(themeDetail);
-            themeDetailVO.setId(themeDetail.getId());
-            theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
+            Integer status = themeService.updateStatus(themeDetail);
+            themeDetail.setStatus(status);
+            themeService.updateThemeDetail(themeDetail);
+            log.info("课程中没有实体信息");
+        }
+        return courseVO;
+    }
 
-            return themeVOWrapper.wrapperThemeDetail(themeDetail);
-        } else {
-            throw new HttpBadRequestException("No right for some group");
+    private void checkThemeCourseGroupAndExam(ThemeDetailVO themeDetailVO, List<EntityVO> entityVOList) {
+        List<Long> examIdList = entityVOList.stream().filter(entityVO -> entityVO.getEntityType() == EntityTypeEnum.EXAM).map(EntityVO::getEntityId).collect(Collectors.toList());
+        for (Long examId : examIdList) {
+            Exam exam = examService.getTask(examId);
+            // 判断考试所属班级是否一致
+            if (!groupService.getByExamId(examId).get(0).getId().equals(themeDetailVO.getGroupId())) {
+                throw new HttpBadRequestException(exam.getName() + "不属于当前班级");
+            } else {
+                //判断考试的时间段在课程时间段之间
+                if (themeDetailVO.getBeginTime() < exam.getBeginTime().getTime() && themeDetailVO.getEndTime() > exam.getEndTime().getTime()) {
+                    continue;
+                } else {
+                    throw new HttpBadRequestException(exam.getName() + "考试时间不在课程时间范围内");
+                }
+            }
         }
     }
 
+
     @Override
     public ThemeDetailVO updateThemeDetail(ThemeDetailVO themeDetailVO) {
         long currTime = System.currentTimeMillis();
@@ -132,6 +225,11 @@ public class ThemeLogicImpl implements ThemeLogic {
         }
     }
 
+    @Override
+    public Page<ThemeDetailVO> getThemeList(Pageable pageable, String keyword, int type) {
+        return null;
+    }
+
     @Async("generateUpload2Oss")
     protected void uploadImages2Oss(String localDir,String parentDir){
         File file = new File(localDir);
@@ -181,7 +279,7 @@ public class ThemeLogicImpl implements ThemeLogic {
     }
 
     @Override
-    public Object[] getThemeListByCourse(Pageable pageable, String keyword) {
+    public Long[] getThemeListByCourse(Pageable pageable, String keyword) {
         Page<CourseResource> courseResources = themeService.getThemeCourseResources(pageable, keyword);
         List<CourseResource> content = courseResources.getContent();
 //        Set<Stream<Long>> listSet = content.stream().map(courseResource -> themeService.getThemeEntityRelationsByEntityId(courseResource.getId(), EntityTypeEnum.COURSE_RESOURCE)
@@ -194,7 +292,8 @@ public class ThemeLogicImpl implements ThemeLogic {
                 themeIdSet.add(themeEntityRelations.getThemeId());
             }
         }
-        return themeIdSet.toArray();
+        Long[] themeIdArray = Arrays.stream(themeIdSet.toArray()).map(themeId -> Long.valueOf(themeId.toString())).toArray(Long[]::new);
+        return themeIdArray;
     }
 
 
@@ -261,6 +360,42 @@ public class ThemeLogicImpl implements ThemeLogic {
     }
 
     @Override
+    public CourseVO getCourse(Long id, Long userId) {
+        ThemeDetail themeDetail = themeService.getThemeDetailById(id);
+        userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        //检查用户查看权限
+        if (themeDetail.getVisibility()==CourseVisibility.NONE_OPEN.getVisibility()){
+            Theme2Group t2g = theme2GroupService.findByThemeId(themeDetail.getId());
+            if (!groupService.isUserInGroup(userId,t2g.getGroupId())
+                    && !userId.equals(themeDetail.getOwnerId())){
+                throw new HttpForbiddenException(String.format("User Cannot Access This Course, UserId: %s", userId));
+            }
+        }
+        //根据id查询,然后过滤存map
+        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(id);
+        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);
+            if (key == EntityTypeEnum.EXAM) {
+                List<Exam> examList = examService.getTasks(map.get(key).stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
+                theme.setExamList(examList);
+            } else if (key == EntityTypeEnum.COURSE_RESOURCE) {
+                List<Long> courseIds = map.get(key).stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList());
+                List<CourseResource> courseResourceList = courseResourceService.getCourseResourceById(courseIds);
+                theme.setCourseResourceList(courseResourceList);
+            }
+        }
+        CourseVO courseVO = themeVOWrapper.wrapTheme2CourseVO(theme, themeEntityRelations);
+        List<StudyRecord> studyCompletenessByThemeId = themeService.getStudyCompletenessByThemeId(id, userId, EntityTypeEnum.COURSE_RESOURCE);
+        List<ThemeEntityRelations> entityRelationsList = themeService.getThemeEntityRelations(id).stream().filter(entityRelations -> entityRelations.getEntityType() == EntityTypeEnum.COURSE_RESOURCE).collect(Collectors.toList());
+        courseVO.setCompleteCourseRecourseId(studyCompletenessByThemeId.stream().map(StudyRecord::getEntityId).collect(Collectors.toList()));
+        courseVO.setTotalCourseRecourseId(entityRelationsList.stream().map(ThemeEntityRelations::getEntityId).collect(Collectors.toList()));
+        return courseVO;
+    }
+
+    @Override
     public CourseVO getCourse(Long id) {
         ThemeDetail themeDetail = themeService.getThemeDetailById(id);
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
@@ -291,6 +426,41 @@ public class ThemeLogicImpl implements ThemeLogic {
         return themeVOWrapper.wrapTheme2CourseVO(theme,themeEntityRelations);
     }
 
+
+    // 获取themeIds的所有主题课程信息
+    @Override
+    public Page<CourseVO> getCoursesByThemeIds(Long[] ids, Pageable pageable, long total, int type) {
+        List<ThemeDetail> themeDetailList = themeService.getThemeDetailByIds(ids);
+        if(type > 0){
+            themeDetailList = themeDetailList.stream().filter(themeDetail -> themeDetail.getCourseType() == type).collect(Collectors.toList());
+        }
+        List<CourseVO> courseVOS = themeDetailList.stream().map(themeDetail -> getCourseOfCourseResource(themeDetail.getId())).collect(Collectors.toList());
+        courseVOS = courseVOS.stream().filter(courseVO -> courseVO.getEntityVOList() != null).peek(courseVO -> courseVO.getThemeDetailVO().setEntityVOList(courseVO.getEntityVOList())).collect(Collectors.toList());
+        return new PageImpl<CourseVO>(courseVOS, pageable, total);
+    }
+
+    public CourseVO getCourseOfCourseResource(Long id) {
+        ThemeDetail themeDetail = themeService.getThemeDetailById(id);
+        //根据id查询,然后过滤存map
+        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(id);
+        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);
+//            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);
+                theme.setCourseResourceList(courseResourceList);
+            }
+        }
+        return themeVOWrapper.wrapTheme2CourseVO(theme,themeEntityRelations);
+    }
+
     /**
      * 创建主题相关的实体
      * @param themeId
@@ -299,8 +469,8 @@ public class ThemeLogicImpl implements ThemeLogic {
      * @return
      */
     @Override
-    public ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType) {
-        ThemeEntityRelations themeEntityRelations = themeService.createEntityRelation(themeId, entityId, entityType);
+    public ThemeEntityRelationsVO createEntityRelation(long themeId, long entityId, EntityTypeEnum entityType, int sortId) {
+        ThemeEntityRelations themeEntityRelations = themeService.createEntityRelation(themeId, entityId, entityType, sortId);
         return themeEntityRelationsVOWrapper.wrapperThemeEntityRelations(themeEntityRelations);
     }
 
@@ -334,7 +504,7 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Override
     public ExamVO createRelationBetweenThemeAndExam(long themeId, long caseId) {
         ThemeEntityRelations themeEntityRelations = themeService.createRelationBetweenThemeAndEntity(themeId,caseId,EntityTypeEnum.EXAM);
-        return themeDetailVOWrapper.wrap(examService.getTask(themeEntityRelations.getEntityId()));
+        return examVOWrapper.wrap(examService.getTask(themeEntityRelations.getEntityId()));
     }
 
     @Override
@@ -370,6 +540,12 @@ public class ThemeLogicImpl implements ThemeLogic {
     }
 
     @Override
+    public CourseResourceVO getCourseResourceById(long resourceId) {
+        CourseResource courseResource = courseResourceService.getCourseResourceByResourceId(resourceId);
+        return courseResourceVOWrapper.wrap(courseResource);
+    }
+
+    @Override
     public CourseResourceVO getCourseResource(long courseId, long resourceId) {
         ThemeEntityRelations themeEntityRelations = themeService
                 .checkRelationBetweenThemeAndEntity(courseId, resourceId, EntityTypeEnum.COURSE_RESOURCE);
@@ -381,4 +557,92 @@ public class ThemeLogicImpl implements ThemeLogic {
         CourseResource courseResource = courseResourceService.getCourseResourceByResourceId(resourceId);
         return courseResourceVOWrapper.wrap(courseResource);
     }
+
+    @Override
+    public ThemeInfoVO getThemeInfoByThemeId(Long themeId) {
+        ThemeInfoVO themeInfoVO = new ThemeInfoVO();
+        ThemeDetail themeDetail = themeService.getThemeDetailById(themeId);
+        ThemeDetailVO themeDetailVO = themeVOWrapper.wrapperThemeDetail(themeDetail);
+        themeInfoVO.setThemeDetailVO(themeDetailVO);
+        long restTime = System.currentTimeMillis() - themeDetailVO.getBeginTime();
+        long totalTime = themeDetailVO.getEndTime() - themeDetailVO.getBeginTime();
+        themeInfoVO.setTeacherCompletedRate((float) restTime / totalTime);
+
+        int studentNumber = groupService.getWorkerCount(themeDetailVO.getGroupId());
+        themeInfoVO.setStudentNum(studentNumber);
+        List<User> studentList = groupService.getUserByGroupId(themeDetailVO.getGroupId());
+        List<Long> studentIds = new ArrayList<>();
+        for(User student : studentList){
+            studentIds.add(student.getId());
+        }
+
+        CourseVO resource = getCourseOfCourseResource(themeId);
+        int courseResourceCount = resource.getEntityVOList().size();
+        int studyRecordNumber = themeService.getStudyRecord(themeId, studentIds, EntityTypeEnum.COURSE_RESOURCE);
+        themeInfoVO.setLearningCompletedRate((float)(studyRecordNumber/(courseResourceCount*studentNumber*1.0)));
+        int totalExamCommitNumber = 0;
+        int examCount = 0;
+        int totalExerciseCommitNumber = 0;
+        int exerciseCount = 0;
+
+        // 获取练习和考试结果信息
+        List<ExamResultVO> examResultVOS = new ArrayList<>();
+        List<ThemeEntityRelations> themeEntityRelations = themeService.getThemeEntityRelations(themeId).stream()
+                .filter(entityRelation -> entityRelation.getEntityType() == EntityTypeEnum.EXAM).collect(Collectors.toList());
+        for(ThemeEntityRelations relation : themeEntityRelations){
+            List<Double> scoreList = submitRecordService.getScoreList(relation.getEntityId());
+            Exam exam = examService.getTask(relation.getEntityId());
+            if (exam == null) {
+                throw new HttpNotFoundException("exam doesn't exists");
+            }
+            ExamVO examVO = examVOWrapper.wrapExamFrom(exam);
+            examResultVOS.add(new ExamResultVO(examVO, scoreList));
+            if(examVO.getType() == 0){
+                totalExamCommitNumber += scoreList.size();
+                examCount++;
+            }else{
+                totalExerciseCommitNumber += scoreList.size();
+                exerciseCount++;
+            }
+        }
+        themeInfoVO.setExamResultVOList(examResultVOS);
+        if(examCount == 0){
+            themeInfoVO.setExamCompletedRate(-1);
+        }else{
+            themeInfoVO.setExamCompletedRate(totalExamCommitNumber/examCount*studentNumber);
+        }
+        if(exerciseCount == 0){
+            themeInfoVO.setExerciseCompletedRate(-1);
+        }else{
+            themeInfoVO.setExerciseCompletedRate((float) (totalExerciseCommitNumber/(exerciseCount*studentNumber*1.0)));
+        }
+
+        return themeInfoVO;
+    }
+
+    @Override
+    public void createStudyRecord(Long themeId, Long courseId, Long userId) {
+        StudyRecord studyRecord = themeService.getStudyRecordByThemeId(themeId, courseId, userId, EntityTypeEnum.COURSE_RESOURCE);
+        if(studyRecord == null){
+            themeService.createStudyViewRecord(themeId, courseId, userId, EntityTypeEnum.COURSE_RESOURCE);
+        }
+    }
+
+    @Override
+    public List<Theme2Group> getThemeListByGroup(Long groupId) {
+        return theme2GroupService.findByGroupId(groupId);
+    }
+
+    @Override
+    public ThemeDetailVO getThemeDetailByThemeId(Long themeId) {
+        return themeVOWrapper.wrapperThemeDetail(themeService.getThemeDetailById(themeId));
+    }
+
+    @Override
+    public ThemeDetailVO deleteThemeDetailById(Long themeId) {
+        if(!themeService.canBeDeleted(themeId)){
+            throw new HttpBadRequestException("Cannot delete this themeCourse.");
+        }
+        return themeVOWrapper.wrapperThemeDetail(themeService.deleteThemeDetailById(themeId));
+    }
 }