Explorar o código

增加课程状态的定时任务

guochao %!s(int64=4) %!d(string=hai) anos
pai
achega
b4d60e4f81

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

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

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

@@ -56,7 +56,7 @@ public interface ThemeService {
     List<ThemeDetail> getPlatformThemeDetails();
     List<ThemeDetail> getCourseByParticipant(Long participantId);
     Page<ParticipantCourse> getCourseByParticipant(Long userId, Map<String, String> extraCondition, String keyword, Pageable pageable);
-    Page<OperationCourse> getOperationCourseList(Pageable pageable, Long userId, Long ownerId, String operation, Map<String, String> excludeCondition, String keyword);
+    Page<OperationCourse> getOperationCourseList(Pageable pageable, Long userId, Long ownerId, String operation, Map<String, String> extraCondition, String keyword);
     List<OperationCourse> getOperationCourseList(Long userId, String operation);
     boolean isUserJoinCourse(Long courseId, Long participantId);
     Page<ThemeDetail> getPlatformThemeDetails(Map<String, String> extraCondition, Map<String, String> excludeCondition, Pageable pageable, String keyword);

+ 11 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThemeStatusService.java

@@ -0,0 +1,11 @@
+package cn.iselab.mooctest.site.service;
+
+import cn.iselab.mooctest.site.models.ThemeDetail;
+
+/**
+ * @author guochao
+ * @date 2020-02-17 10:57
+ */
+public interface ThemeStatusService {
+    Integer updateStatus(ThemeDetail themeDetail);
+}

+ 127 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThemeSchedulerServiceImpl.java

@@ -0,0 +1,127 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.models.ThemeDetail;
+import cn.iselab.mooctest.site.service.ThemeSchedulerService;
+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 ThemeSchedulerServiceImpl implements ThemeSchedulerService {
+
+    @Autowired
+    private ThemeStatusService themeStatusService;
+
+    private ConcurrentHashMap<ThemeDetail, ThemeScheduler> map = new ConcurrentHashMap<>();
+
+    @Override
+    public void clearAllSchedulers() {
+        this.map = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public boolean createNewThemeScheduler(ThemeDetail themeDetail) {
+        if(! this.map.containsKey(themeDetail)) {
+            String cronExp = this.generateNextCronExp(themeDetail);
+            if(cronExp==null){
+                return false;
+            }
+            ThemeScheduler themeDetailScheduler = new ThemeScheduler(themeDetail, cronExp);
+            themeDetailScheduler.startCron();
+            map.put(themeDetail, themeDetailScheduler);
+            System.out.println("map.size: " + map.size());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean cancelThemeScheduler(ThemeDetail themeDetail) {
+        if(this.map.containsKey(themeDetail)) {
+            ThemeScheduler themeDetailScheduler = map.get(themeDetail);
+            themeDetailScheduler.stopCron();
+            map.remove(themeDetail);
+            return true;
+        }
+        return false;
+    }
+
+    public String generateNextCronExp(ThemeDetail themeDetail) {
+        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(themeDetail.getBeginTime())) {
+            cronExp = sdf.format(themeDetail.getBeginTime());
+        } else if(now.before(themeDetail.getEndTime())){
+            cronExp = sdf.format(themeDetail.getEndTime());
+        }
+        return cronExp;
+    }
+
+    private class ThemeScheduler {
+        private ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
+        private ScheduledFuture<?> future;
+        private ThemeDetail themeDetail = null;
+        private String cronExp = null;
+
+        ThemeScheduler() {}
+
+        ThemeScheduler (ThemeDetail themeDetail, String cronExp) {
+            this.themeDetail = themeDetail;
+            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.themeDetail), new CronTrigger(this.cronExp));
+            System.out.println("ThemeScheduler " + this.themeDetail.getTitle() + ".startCron()");
+            return "startCron";
+        }
+
+        public String stopCron() {
+            if (future != null) {
+                future.cancel(true);
+            }
+            System.out.println("ThemeScheduler" + this.themeDetail.getTitle() + ".stopCron()");
+            return "stopCron";
+        }
+
+        private class MyRunnable implements Runnable {
+
+            private ThemeDetail themeDetail;
+
+            public MyRunnable() {}
+
+            public MyRunnable (ThemeDetail themeDetail) {
+                this.themeDetail = themeDetail;
+            }
+
+            @Override
+            public void run() {
+                System.out.println("ThemeScheduler " + this.themeDetail.getTitle() + " updateStatus," + new Date());
+                Integer result = themeStatusService.updateStatus(this.themeDetail);
+                map.remove(themeDetail);
+                if(result.equals(ThemeDetail.STATUS_ONGOING) || result.equals(ThemeDetail.STATUS_UPCOMING)) {
+                    createNewThemeScheduler(themeDetail);
+                }
+            }
+        }
+    }
+}

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

@@ -269,8 +269,8 @@ public class ThemeServiceImpl implements ThemeService {
     }
 
     @Override
-    public Page<OperationCourse> getOperationCourseList(Pageable pageable, Long userId, Long ownerId, String operation, Map<String, String> excludeCondition, String keyword) {
-        Specifications<OperationCourse> where = Specifications.where(this.getWhereClause(OperationCourse.class, userId, ownerId, operation, excludeCondition, keyword));
+    public Page<OperationCourse> getOperationCourseList(Pageable pageable, Long userId, Long ownerId, String operation, Map<String, String> extraCondition, String keyword) {
+        Specifications<OperationCourse> where = Specifications.where(this.getWhereClause(OperationCourse.class, userId, ownerId, operation, extraCondition, keyword));
         return operationCourseDao.findAll(where, pageable);
     }
 
@@ -279,7 +279,7 @@ public class ThemeServiceImpl implements ThemeService {
         return operationCourseDao.findAllByUserIdAndOperation(userId, operation);
     }
 
-    private  <T> Specification<T> getWhereClause(Class<T> model, Long userId, Long ownerId, String operation, Map<String, String> excludeCondition, String keyword) {
+    private  <T> Specification<T> getWhereClause(Class<T> model, Long userId, Long ownerId, String operation, Map<String, String> extraCondition, String keyword) {
         return (root, query, builder) -> {
             Predicate predicate = builder.conjunction();
             try {
@@ -305,10 +305,10 @@ public class ThemeServiceImpl implements ThemeService {
                         builder.equal(root.<Long>get("operation"), operation)
                 );
             }
-            if (excludeCondition != null){
-                for(String key:excludeCondition.keySet()) {
+            if (extraCondition != null){
+                for(String key:extraCondition.keySet()) {
                     predicate.getExpressions().add(
-                            builder.notEqual(root.get(key), excludeCondition.get(key))
+                            builder.equal(root.get(key), extraCondition.get(key))
                     );
                 }
             }

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

@@ -0,0 +1,36 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.dao.ThemeDetailDao;
+import cn.iselab.mooctest.site.models.ThemeDetail;
+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 ThemeStatusServiceImpl implements ThemeStatusService {
+    @Autowired
+    private ThemeDetailDao themeDetailDao;
+
+    @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 if (themeDetail.getEndTime().getTime() < curr){
+            status = ThemeDetail.STATUS_FINISHED;
+        }
+        System.out.println("课程状态 " + status);
+        themeDetailDao.updateStatusById(themeDetail.getId(),status);
+        return status;
+    }
+}

+ 3 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThemeController.java

@@ -15,6 +15,7 @@ import com.google.gson.Gson;
 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.*;
@@ -274,7 +275,7 @@ public class ThemeController extends BaseSearchController {
         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");
+//        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
         // 判断删除权限
         ThemeDetailVO themeDetailVO = themeLogic.deleteThemeDetailById(themeId);
         return new ResponseVO<>(ServerCode.SUCCESS, themeDetailVO);
@@ -402,7 +403,7 @@ public class ThemeController extends BaseSearchController {
         Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
         log.info("userId: {}获取课程列表,参数:{}",userId,searchConditionVO.toString());
         return new ResponseVO<>(ServerCode.SUCCESS, themeLogic.getAuthorisedCourses(pageable, extraCondition, keyword));
-    }
+}
 
     @GetMapping(value = UrlConstants.API + "courses/participant")
     public ResponseVO<Page<ThemeDetailVO>> participantCourse(@RequestParam(name = "searchCondition") String searchCondition){

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

@@ -22,6 +22,7 @@ import com.google.common.collect.Lists;
 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;
@@ -66,6 +67,10 @@ public class ThemeLogicImpl implements ThemeLogic {
     @Autowired
     private User2ThemeService user2ThemeService;
     @Autowired
+    private ThemeSchedulerService themeSchedulerService;
+    @Autowired
+    private ThemeStatusService themeStatusService;
+    @Autowired
     private PdfService pdfService;
     @Autowired
     GroupService groupService;
@@ -94,8 +99,19 @@ public class ThemeLogicImpl implements ThemeLogic {
         ThemeDetailVO themeDetailVO = courseVO.getThemeDetailVO();
         if (themeDetailVO == null) throw new HttpBadRequestException("主题内容不可为空");;
         themeDetailVO.setOwnerId(userId);
-//        themeDetailVO.setStatus(ThemeDetail.STATUS_UPCOMING);//??
-//        themeDetailVO.setVisibility(CourseVisibility.NONE_OPEN.getVisibility());//??
+        // 设置课程状态
+        if(System.currentTimeMillis() < themeDetailVO.getBeginTime())
+            themeDetailVO.setStatus(ThemeDetail.STATUS_UPCOMING);
+        else
+            themeDetailVO.setStatus(ThemeDetail.STATUS_ONGOING);
+        if(System.currentTimeMillis() > themeDetailVO.getEndTime())
+            themeDetailVO.setStatus(ThemeDetail.STATUS_FINISHED);
+        // 判断可见性
+        boolean admin = SecurityUtils.getSubject().hasRole("admin");
+        if(!admin && themeDetailVO.getVisibility() != CourseVisibility.NONE_OPEN.getVisibility()){
+            throw new HttpBadRequestException("当前用户只能新建自定义课程");
+        }
+        // 检验课程信息
         checkThemeInfo(themeDetailVO);
         if (themeDetailVO.getGroupId() != null && groupService.getGroupsByOwnerId(themeDetailVO.getOwnerId()).stream()
                 .map(Group::getId).collect(Collectors.toList()).contains(themeDetailVO.getGroupId())) {
@@ -106,6 +122,9 @@ public class ThemeLogicImpl implements ThemeLogic {
                 // 条件符合,可以创建
                 ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
                 themeDetail = themeService.createThemeDetail(themeDetail);
+
+                themeSchedulerService.createNewThemeScheduler(themeDetail);
+
                 themeDetailVO.setId(themeDetail.getId());
                 theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
                 user2ThemeService.createOperationRelation(userId,themeDetail.getId(),"*");
@@ -120,8 +139,10 @@ public class ThemeLogicImpl implements ThemeLogic {
                 // 条件符合,可以创建
                 ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
                 themeDetail = themeService.createThemeDetail(themeDetail);
+                themeSchedulerService.createNewThemeScheduler(themeDetail);
                 themeDetailVO.setId(themeDetail.getId());
                 theme2GroupService.create(themeDetailVO.getGroupId(), themeDetailVO.getId());
+                user2ThemeService.createOperationRelation(userId,themeDetail.getId(),"*");
             }
         } else {
             throw new HttpBadRequestException("No right for some group");
@@ -164,14 +185,17 @@ public class ThemeLogicImpl implements ThemeLogic {
 
         // 课程中的实体信息(课程信息、考试信息、练习信息)
         List<EntityVO> entityVOList = courseVO.getEntityVOList();
-        // 删除原有的实体
-        deleteEntityRelationByThemeId(themeDetailVO.getId());
+
         if(entityVOList.size() != 0){
+            // 删除原有的实体
+            deleteEntityRelationByThemeId(themeDetailVO.getId());
             checkThemeCourseGroupAndExam(themeDetailVO, entityVOList);
             ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
-            Integer status = themeService.updateStatus(themeDetail);
-            themeDetail.setStatus(status);
-            themeService.updateThemeDetail(themeDetail);
+            Integer updateStatus = themeStatusService.updateStatus(themeDetail);
+            themeDetail.setStatus(updateStatus);
+            ThemeDetail result = themeService.updateThemeDetail(themeDetail);
+            themeSchedulerService.cancelThemeScheduler(themeDetail);
+            themeSchedulerService.createNewThemeScheduler(result);
             // 条件符合,可以更新
             int index = 0;
             for(EntityVO entityVO: entityVOList){
@@ -182,9 +206,11 @@ public class ThemeLogicImpl implements ThemeLogic {
             }
         }else{
             ThemeDetail themeDetail = themeVOWrapper.unWrapperThemeDetail(themeDetailVO);
-            Integer status = themeService.updateStatus(themeDetail);
-            themeDetail.setStatus(status);
-            themeService.updateThemeDetail(themeDetail);
+            Integer updateStatus = themeStatusService.updateStatus(themeDetail);
+            themeDetail.setStatus(updateStatus);
+            ThemeDetail result = themeService.updateThemeDetail(themeDetail);
+            themeSchedulerService.cancelThemeScheduler(themeDetail);
+            themeSchedulerService.createNewThemeScheduler(result);
         }
         return courseVO;
     }
@@ -638,8 +664,13 @@ public class ThemeLogicImpl implements ThemeLogic {
 
         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)));
+        if(courseResourceCount != 0){
+            int studyRecordNumber = themeService.getStudyRecord(themeId, studentIds, EntityTypeEnum.COURSE_RESOURCE);
+            themeInfoVO.setLearningCompletedRate((float)(studyRecordNumber/(courseResourceCount*studentNumber*1.0)));
+        }else{
+            themeInfoVO.setLearningCompletedRate(0);
+        }
+
         int totalExamCommitNumber = 0;
         int examCount = 0;
         int totalExerciseCommitNumber = 0;