Procházet zdrojové kódy

Merge remote-tracking branch 'origin/master' into ScrmStore

yjwang před 2 dny
rodič
revize
9c530cba5f

+ 1 - 0
fs-qw-task/src/main/java/com/fs/app/task/CourseWatchLogScheduler.java

@@ -68,6 +68,7 @@ public class CourseWatchLogScheduler {
         try {
             log.info("检查看课中任务执行>>>>>>>>>>>>");
             courseWatchLogService.scheduleBatchUpdateToDatabase();
+            courseWatchLogService.scheduleBatchUpdateToDatabaseIsOpen();
             courseWatchLogService.checkWatchStatus();
             log.info("检查看课中任务执行完成>>>>>>>>>>>>");
         }catch (Exception e) {

+ 2 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java

@@ -127,4 +127,6 @@ public interface FsCourseAnswerLogsMapper
 
     Long selectFsCourseAnswerLogsListVONewCount(FsCourseAnswerLogsParam param);
 
+    @Select("select * from fs_course_answer_logs where video_id = #{videoId} and user_id = #{userId} and is_right = 1 limit 1")
+    FsCourseAnswerLogs selectRightLogByCourseVideoIsOpen(@Param("videoId") Long videoId,@Param("userId") Long userId);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -457,4 +457,12 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "video_id = #{videoId} " +
             "and user_id = #{userId} limit 1 ")
     FsCourseWatchLog getWatchCourseVideoByUserId(@Param("userId") Long userId,@Param("videoId") Long videoId);
+
+    @Select("select * from fs_course_watch_log " +
+            "where " +
+            "video_id = #{videoId} " +
+            "and user_id = #{userId} limit 1 ")
+    FsCourseWatchLog getWatchCourseVideoIsOpen(@Param("userId") Long userId,@Param("videoId") Long videoId);
+
+    void batchUpdateWatchLogIsOpen(@Param("list") List<FsCourseWatchLog> batchList);
 }

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseQuestionAnswerUParam.java

@@ -31,5 +31,6 @@ public class FsCourseQuestionAnswerUParam implements Serializable
     private Long qwExternalId;
     private List<FsCourseQuestionBank> questions;
     private Long periodId;
+    private Integer isOpen;
 
 }

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsUserCourseVideoAddKfUParam.java

@@ -61,6 +61,7 @@ public class FsUserCourseVideoAddKfUParam implements Serializable {
     private String link;
 
     private Integer isRoom;
+    private Integer isOpen;//是否公开
     private String chatId;
     private String nickName;
 

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsUserCourseVideoFinishUParam.java

@@ -21,4 +21,5 @@ public class FsUserCourseVideoFinishUParam implements Serializable {
     private Long qwExternalId;
     private Integer linkType;
     private Integer isRoom;//是否群聊
+    private Integer isOpen;//是否公开课
 }

+ 2 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java

@@ -90,4 +90,6 @@ public interface IFsCourseQuestionBankService
     List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank);
 
     R courseAnswerByFsUser(FsCourseQuestionAnswerUParam param);
+
+    R courseAnswerIsOpen(FsCourseQuestionAnswerUParam param, boolean isH5User);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java

@@ -128,4 +128,8 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
      * @return
      */
     List<FsCourseWatchLogListVO> selectListBytrainingCampId(PeriodStatisticCountParam param);
+
+    FsCourseWatchLog getWatchCourseVideoIsOpen(Long userId, Long videoId);
+
+    void scheduleBatchUpdateToDatabaseIsOpen();
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -187,4 +187,10 @@ public interface IFsUserCourseVideoService
     R checkUserInfo(Long userId);
 
     FsUserCourseVideoQVO selectFsUserCourseVideoByVideoIdVO(Long videoId);
+
+    R updateWatchDurationIsOpen(FsUserCourseVideoFinishUParam param);
+
+    R isAddKfIsOpen(FsUserCourseVideoAddKfUParam param);
+
+    R getInternetTrafficIsOpen(FsUserCourseVideoFinishUParam param);
 }

+ 105 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java

@@ -239,6 +239,111 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
         }
     }
 
+    @Override
+    @Transactional
+    public R courseAnswerIsOpen(FsCourseQuestionAnswerUParam param, boolean isH5User) {
+        //获取配置参数
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        //本次答题正确个数
+        int thisRightCount = 0;
+        //用户答错次数
+        int errorCount = 0;
+        List<FsCourseQuestionBank> incorrectQuestions = new ArrayList<>();
+        //日志id
+        Long logId = null;
+
+        FsCourseAnswerLogs rightLog = new FsCourseAnswerLogs();
+        //判断短链类型
+        if (param.getLinkType()!=null&&param.getLinkType()==1){
+            rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(),null);
+            if (rightLog!=null){
+                return R.error("该课程已答题完成,不可重复答题");
+            }
+            errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),null);
+
+        }else {
+            FsCourseWatchLog log;
+            if(isH5User){
+                log = courseWatchLogMapper.getWatchLogByFsUser(param.getVideoId(), param.getUserId(), param.getCompanyUserId());
+            } else {
+                log = courseWatchLogMapper.getWatchCourseVideoIsOpen(param.getUserId(), param.getVideoId());
+            }
+            if (log==null){
+                return R.error("无记录");
+            }
+            if (log.getLogType()!=2){
+                return R.error("未完课");
+            }
+            logId = log.getLogId();
+
+            rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideoIsOpen(param.getVideoId(), param.getUserId());
+            if (rightLog != null) {
+                return R.ok("答题成功");
+            }
+            errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),param.getQwUserId());
+        }
+
+
+        if (errorCount >= config.getAnswerErrorCount()) {
+            return R.error("该课题到达答错次数限制");
+        }
+        int remainCount = config.getAnswerErrorCount()-errorCount-1;
+
+        // 一次性获取所有问题的正确答案
+        Map<Long, FsCourseQuestionBank> correctAnswersMap = fsCourseQuestionBankMapper.selectFsCourseQuestionBankByIds(
+                param.getQuestions().stream().map(FsCourseQuestionBank::getId).collect(Collectors.toList())
+        ).stream().collect(Collectors.toMap(FsCourseQuestionBank::getId, question -> question));
+
+        for (FsCourseQuestionBank questionBank : param.getQuestions()) {
+            FsCourseQuestionBank correctAnswer = correctAnswersMap.get(questionBank.getId());
+            if (correctAnswer.getType() == 1) {
+                if (questionBank.getAnswer().equals(correctAnswer.getAnswer())) {
+                    thisRightCount++;
+                } else {
+                    correctAnswer.setAnswer(null);
+                    incorrectQuestions.add(correctAnswer);
+                }
+            } else if (correctAnswer.getType() == 2) {
+                String[] userAnswers = convertStringToArray(questionBank.getAnswer());
+                String[] correctAnswers = convertStringToArray(correctAnswer.getAnswer());
+
+                Arrays.sort(userAnswers);
+                Arrays.sort(correctAnswers);
+
+                if (Arrays.equals(userAnswers, correctAnswers)) {
+                    thisRightCount++;
+                } else {
+                    correctAnswer.setAnswer(null);
+                    incorrectQuestions.add(correctAnswer);
+                }
+            }
+        }
+
+        FsCourseAnswerLogs logs = new FsCourseAnswerLogs();
+        logs.setWatchLogId(logId);
+        logs.setUserId(param.getUserId());
+        logs.setVideoId(param.getVideoId());
+        logs.setCourseId(param.getCourseId());
+        /*logs.setCompanyId(param.getCompanyId());
+        logs.setCompanyUserId(param.getCompanyUserId());
+        logs.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null );*/
+        logs.setQuestionJson(JSONObject.toJSONString(param.getQuestions()));
+        logs.setCreateTime(new Date());
+        logs.setPeriodId(param.getPeriodId());
+
+        if (thisRightCount == param.getQuestions().size()) {
+            logs.setIsRight(1);
+            courseAnswerLogsMapper.insertFsCourseAnswerLogs(logs);
+            return R.ok("答题成功");
+        } else {
+            logs.setIsRight(0);
+            courseAnswerLogsMapper.insertFsCourseAnswerLogs(logs);
+            return R.ok("答题失败").put("incorrectQuestions", incorrectQuestions).put("remain",remainCount);
+        }
+    }
+
     @Override
     @Transactional
     public R courseAnswer(FsCourseQuestionAnswerUParam param,Boolean isH5User) {

+ 118 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -452,6 +452,112 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return baseMapper.selectListBytrainingCampId(param);
     }
 
+    @Override
+    public FsCourseWatchLog getWatchCourseVideoIsOpen(Long userId, Long videoId) {
+        return fsCourseWatchLogMapper.getWatchCourseVideoIsOpen(userId, videoId);
+    }
+
+    @Override
+    public void scheduleBatchUpdateToDatabaseIsOpen() {
+        log.info("开始更新公开课看课时长,检查完课>>>>>>");
+        //读取所有的key
+        Collection<String> keys = redisCache.keys("h5OpenUser:watch:duration:*");
+        //读取看课配置
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        List<FsCourseWatchLog> logs = new ArrayList<>();
+        for (String key : keys) {
+            //取key中数据
+            Long videoId=null;
+            Long userId=null;
+            try {
+                String[] parts = key.split(":");
+                userId = Long.parseLong(parts[3]);
+                videoId = Long.parseLong(parts[4]);
+            }catch (Exception e){
+                log.error("key中id为null:{}", key);
+                continue;
+            }
+            String durationStr = redisCache.getCacheObject(key);
+            if (com.fs.common.utils.StringUtils.isEmpty(durationStr)) {
+                redisCache.deleteObject(key);
+                log.error("key中数据为null:{}", key);
+                continue;  // 如果 Redis 中没有记录,跳过
+            }
+            Long duration = Long.valueOf(durationStr);
+
+            FsCourseWatchLog watchLog = new FsCourseWatchLog();
+            watchLog.setUserId(userId);
+            watchLog.setVideoId(videoId);
+            watchLog.setDuration(duration);
+
+            //取对应视频的时长
+            Long videoDuration = getVideoDurationIsOpen(videoId);
+            if (videoDuration != null && videoDuration != 0) {
+                boolean complete = false;
+                // 判断百分比
+                if(config.getCompletionMode() == 1 && config.getAnswerRate() != null){
+                    long percentage = (duration * 100 / videoDuration);
+                    complete = percentage >= config.getAnswerRate();
+                }
+                // 判断分钟数
+                if(config.getCompletionMode() == 2 && config.getMinutesNum() != null){
+                    int i = config.getMinutesNum() * 60;
+                    complete = videoDuration > i;
+                }
+                //判断是否完课
+                if (complete) {
+                    watchLog.setLogType(2); // 设置状态为“已完成”
+                    watchLog.setFinishTime(new Date());
+                    String heartbeatKey ="h5OpenUser:watch:heartbeat:" + userId + ":" + videoId;
+                    // 完课删除心跳记录
+                    redisCache.deleteObject(heartbeatKey);
+                    // 完课删除看课时长记录
+                    redisCache.deleteObject(key);
+                }
+            }
+            //集合中增加
+            logs.add(watchLog);
+        }
+
+        batchUpdateFsCourseWatchLogIsOpen(logs,100);
+    }
+
+    public void batchUpdateFsCourseWatchLogIsOpen(List<FsCourseWatchLog> logs, int batchSize) {
+        // 记录开始时间
+        long startTime = System.currentTimeMillis();
+        log.info("开始批量更新日志,总日志数量: {}", logs == null ? 0 : logs.size());
+
+        if (logs == null || logs.isEmpty()) {
+            log.info("待更新的日志列表为空,无需处理");
+            return;
+        }
+
+        // 记录总日志数量
+        log.info("开始批量更新日志,总日志数量: {}", logs.size());
+
+        // 分批处理
+        for (int i = 0; i < logs.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, logs.size());
+            List<FsCourseWatchLog> batchList = logs.subList(i, end);
+            // 执行批量更新
+            try {
+                fsCourseWatchLogMapper.batchUpdateWatchLogIsOpen(batchList);
+            } catch (Exception e) {
+                log.error("第 {} 批日志更新失败:{}",(i / batchSize) + 1,e.getMessage(),e);
+                throw new RuntimeException(e);
+            }
+        }
+
+        // 计算总耗时
+        long totalCost = System.currentTimeMillis() - startTime;
+        log.info("所有日志更新完成,总数量: {}, 总耗时: {}ms, 平均速度: {}/s",
+                logs.size(),
+                totalCost,
+                totalCost > 0 ? String.format("%.2f", logs.size() * 1000.0 / totalCost) : "∞");
+    }
+
 
     public void batchUpdateFsUserCourseWatchLog(List<FsCourseWatchLog> logs, int batchSize) {
         if (logs == null || logs.isEmpty()) {
@@ -847,6 +953,18 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return videoDuration;
     }
 
+    public Long getVideoDurationIsOpen(Long videoId){
+        //将视频时长也存到redis
+        String videoRedisKey = "h5OpenUser:video:duration:" + videoId;
+        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
+        if (videoDuration==null){
+            FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
+            videoDuration=video.getDuration();
+            redisCache.setCacheObject(videoRedisKey,video.getDuration());
+        }
+        return videoDuration;
+    }
+
 
     public void batchUpdateFsCourseWatchLog(List<FsCourseWatchLog> logs, int batchSize) {
         // 记录开始时间

+ 111 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -69,6 +69,7 @@ import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -2472,4 +2473,114 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return fsUserCourseVideoQVO;
     }
 
+    @Override
+    public R updateWatchDurationIsOpen(FsUserCourseVideoFinishUParam param) {
+        //临时短链不做记录
+        if (param.getLinkType()!=null && param.getLinkType()==1){
+            return R.ok();
+        }
+
+        if (param.getIsRoom()!=null&&param.getIsRoom()==1){
+            return R.ok();
+        }
+
+        // 从Redis中获取观看时长
+        String redisKey = "h5OpenUser:watch:duration:" + param.getUserId()+ ":" + param.getVideoId();
+        //        log.info("看课redis-key:{}", redisKey);
+        try {
+            String durationStr = redisCache.getCacheObject(redisKey);
+            Long duration = durationStr != null ? Long.parseLong(durationStr) : 0L;
+
+            // 更新Redis中的观看时长
+            if (param.getDuration() != null && param.getDuration() > duration) {
+                //24小时过期
+                redisCache.setCacheObject(redisKey, param.getDuration().toString(),2,TimeUnit.HOURS);
+            }
+            updateHeartbeatIsOpen(param);
+            return R.ok();
+        }catch (Exception e){
+            e.printStackTrace();
+            logger.error("更新时长失败:{}",redisKey,e.getMessage());
+            return R.error();
+        }
+    }
+    //更新心跳时间
+    public void updateHeartbeatIsOpen(FsUserCourseVideoFinishUParam param) {
+        String redisKey = "h5OpenUser:watch:heartbeat:" + param.getUserId() + ":" + param.getVideoId();
+        redisCache.setCacheObject(redisKey, LocalDateTime.now().toString());
+        // 设置 Redis 记录的过期时间(例如 5 分钟)
+        redisCache.expire(redisKey, 300, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public R isAddKfIsOpen(FsUserCourseVideoAddKfUParam param) {
+        logger.info("【判断添加客服】:{}",param);
+        //查询用户
+        FsUser fsUser = fsUserMapper.selectFsUserByUserId(param.getUserId());
+        //用户不存在唤起重新授权
+        if (fsUser==null){
+            return R.error(504,"未授权");
+        }
+
+        if (fsUser.getStatus()==0){
+            return R.error("会员被停用,无权限,请联系客服!");
+        }
+        return createWatchIsOpen(param);
+    }
+
+    @Override
+    public R getInternetTrafficIsOpen(FsUserCourseVideoFinishUParam param) {
+        try {
+            if (param.getBufferRate()==null){
+                logger.error("zyp \n【缓冲值空】参数: {}",param);
+                return R.error("缓冲值空");
+            }
+            FsCourseTrafficLog trafficLog = new FsCourseTrafficLog();
+            trafficLog.setCreateTime(new Date());
+            BeanUtils.copyProperties(param, trafficLog);
+
+            FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
+            if (video == null) {
+                return R.error("视频不存在");
+            }
+
+            // 计算流量
+            BigDecimal result = param.getBufferRate().divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
+            BigDecimal longAsBigDecimal = BigDecimal.valueOf(video.getFileSize());
+            long roundedResult = result.multiply(longAsBigDecimal).setScale(0, RoundingMode.HALF_UP).longValue();
+            trafficLog.setInternetTraffic(roundedResult);
+
+            // 处理 UUID 为空的情况
+            if (StringUtils.isNotEmpty(trafficLog.getUuId())) {
+                // 直接插入或更新
+//                logger.error("zyp \n【插入或更新流量】:{}",trafficLog);
+                fsCourseTrafficLogMapper.insertOrUpdateTrafficLog(trafficLog);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            // 打印参数param和异常信息
+            logger.error("zyp \n【插入或更新流量失败】参数: {}, 错误信息:{}", param, e.getMessage(), e);
+            return R.error();
+        }
+        return R.ok();
+    }
+
+    private R createWatchIsOpen(FsUserCourseVideoAddKfUParam param) {
+        try {
+            FsCourseWatchLog log = new FsCourseWatchLog();
+            BeanUtils.copyProperties(param,log);
+            log.setSendType(2);
+            log.setUserId(param.getUserId());
+            log.setVideoId(param.getVideoId());
+            log.setDuration(0L);
+            log.setCreateTime(new Date());
+            log.setLogType(3);
+            logger.info("zyp \n【群聊生成看课记录】:{}",param);
+            courseWatchLogMapper.insertFsCourseWatchLog(log);
+        } catch (BeansException e) {
+            return R.error("群聊生成看课记录失败!");
+        }
+        return R.ok("生成看课记录成功!");
+    }
+
 }

+ 47 - 0
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -641,6 +641,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             (#{item.videoId}, #{item.userId}, #{item.companyUserId})
         </foreach>
     </update>
+    <update id="batchUpdateWatchLogIsOpen">
+        UPDATE fs_course_watch_log
+        SET
+        duration = CASE
+        <foreach collection="list" item="item" index="index">
+            WHEN video_id = #{item.videoId} AND user_id = #{item.userId} THEN
+            CASE
+            <!-- 仅当传入的duration > 当前值时才更新 -->
+            WHEN #{item.duration} IS NOT NULL AND #{item.duration} > duration THEN #{item.duration}
+            ELSE duration <!-- 如果 duration 为 null,保持原值 -->
+            END
+        </foreach>
+        END,
+        last_heartbeat_time = CASE
+        <foreach collection="list" item="item" index="index">
+            WHEN video_id = #{item.videoId} AND user_id = #{item.userId} THEN
+            CASE
+            WHEN #{item.lastHeartbeatTime} IS NOT NULL THEN #{item.lastHeartbeatTime}
+            ELSE last_heartbeat_time <!-- 如果 last_heartbeat_time 为 null,保持原值 -->
+            END
+        </foreach>
+        END,
+        finish_time = CASE
+        <foreach collection="list" item="item" index="index">
+            WHEN video_id = #{item.videoId} AND user_id = #{item.userId} THEN
+            CASE
+            WHEN finish_time IS NULL THEN #{item.finishTime} <!-- 如果表中 finish_time 为 null,更新为传入的值 -->
+            ELSE finish_time <!-- 如果表中 finish_time 不为 null,保持原值 -->
+            END
+        </foreach>
+        END,
+        log_type = CASE
+        <foreach collection="list" item="item" index="index">
+            WHEN video_id = #{item.videoId} AND user_id = #{item.userId} THEN
+            CASE
+            WHEN log_type = 2 THEN log_type <!-- 如果 log_type 已经是 2,保持原值 -->
+            WHEN #{item.logType} IS NOT NULL AND log_type != 2 THEN #{item.logType} <!-- 如果 log_type 不是 2,更新为传入的值 -->
+            ELSE log_type <!-- 其他情况保持原值 -->
+            END
+        </foreach>
+        END
+        WHERE
+        (video_id, user_id) IN
+        <foreach collection="list" item="item" index="index" open="(" separator="," close=")">
+            (#{item.videoId}, #{item.userId})
+        </foreach>
+    </update>
 
 
     <select id="selectListBytrainingCampId" resultType="com.fs.course.vo.FsCourseWatchLogVO">

+ 96 - 45
fs-user-app/src/main/java/com/fs/app/controller/course/CourseQwController.java

@@ -121,52 +121,78 @@ public class CourseQwController extends AppBaseController {
                 }
             }
         }
-        Long duration = 0L;
-        long tipsTime = 0L;
-        int isFinish = 0;
-        if (param.getLinkType()!=null&&param.getLinkType()==1){
-            return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
-        }
 
-        if (param.getIsRoom()!=null&&param.getIsRoom()==1&&param.getQwExternalId()==null){
-            return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
-        }
-        // 从Redis中获取观看时长
-        String redisKey = "h5user:watch:duration:" + param.getQwUserId()+ ":" + param.getQwExternalId() + ":" + param.getVideoId();
-        String durationStr = redisCache.getCacheObject(redisKey);
-        FsCourseWatchLog log = courseWatchLogService.getWatchCourseVideo(param.getUserId(),param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
-        if (log==null){
-            return R.error("记录不存在,请联系客服!");
-        }
-        //redis取不到查库
-        if (durationStr != null) {
-            duration = Long.parseLong(durationStr);
-        }else {
-            duration = log.getDuration();
-        }
+        if(param.getIsOpen() == null || param.getIsOpen() == 0){
+            Long duration = 0L;
+            long tipsTime = 0L;
+            int isFinish = 0;
+            if (param.getLinkType()!=null&&param.getLinkType()==1){
+                return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
+            }
 
-        if (course.getDuration()!=null){
-            tipsTime = course.getDuration()/2;
-        }
-        //判断是否完课
-        if (log.getLogType()==2){
-            isFinish=1;
-        }
+            if (param.getIsRoom()!=null&&param.getIsRoom()==1&&param.getQwExternalId()==null){
+                return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
+            }
+            // 从Redis中获取观看时长
+            String redisKey = "h5user:watch:duration:" + param.getQwUserId()+ ":" + param.getQwExternalId() + ":" + param.getVideoId();
+            String durationStr = redisCache.getCacheObject(redisKey);
+            FsCourseWatchLog log = courseWatchLogService.getWatchCourseVideo(param.getUserId(),param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
+            if (log==null){
+                return R.error("记录不存在,请联系客服!");
+            }
+            //redis取不到查库
+            if (durationStr != null) {
+                duration = Long.parseLong(durationStr);
+            }else {
+                duration = log.getDuration();
+            }
 
-        //将视频时长也存到redis
-        String videoRedisKey = "h5user:video:duration:" + param.getVideoId();
-        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
-        if (videoDuration==null){
+            if (course.getDuration()!=null){
+                tipsTime = course.getDuration()/2;
+            }
+            //判断是否完课
+            if (log.getLogType()==2){
+                isFinish=1;
+            }
 
-            redisCache.setCacheObject(videoRedisKey,course.getDuration());
-        }
-        // 返回是否开启评论/弹幕。优先获取sop任务配置的是否开启评论/弹幕,如果没有配置就取总后台的配置;
-        QwSop qwSop = qwSopService.selectQwSopById(log.getSopId());
-        if(qwSop != null && qwSop.getOpenCommentStatus() != null) {
-            config.setOpenCommentStatus(qwSop.getOpenCommentStatus());
+            //将视频时长也存到redis
+            String videoRedisKey = "h5user:video:duration:" + param.getVideoId();
+            Long videoDuration = redisCache.getCacheObject(videoRedisKey);
+            if (videoDuration==null){
+
+                redisCache.setCacheObject(videoRedisKey,course.getDuration());
+            }
+            // 返回是否开启评论/弹幕。优先获取sop任务配置的是否开启评论/弹幕,如果没有配置就取总后台的配置;
+            QwSop qwSop = qwSopService.selectQwSopById(log.getSopId());
+            if(qwSop != null && qwSop.getOpenCommentStatus() != null) {
+                config.setOpenCommentStatus(qwSop.getOpenCommentStatus());
+            }
+
+            return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime).put("isFinish",isFinish);
+        }else{
+            Long duration = 0L;
+            int isFinish = 0;
+            // 从Redis中获取观看时长
+            String redisKey = "h5OpenUser:watch:duration:" + param.getUserId() + ":" + param.getVideoId();
+            String durationStr = redisCache.getCacheObject(redisKey);
+            FsCourseWatchLog log = courseWatchLogService.getWatchCourseVideoIsOpen(param.getUserId(),param.getVideoId());
+            if (log==null){
+                return R.error("记录不存在,请联系客服!");
+            }
+            //redis取不到查库
+            if (durationStr != null) {
+                duration = Long.parseLong(durationStr);
+            }else {
+                duration = log.getDuration();
+            }
+            //判断是否完课
+            if (log.getLogType()==2){
+                isFinish=1;
+            }
+
+            return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("isFinish",isFinish);
         }
 
-        return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime).put("isFinish",isFinish);
     }
 
     @ApiOperation("答题")
@@ -178,8 +204,14 @@ public class CourseQwController extends AppBaseController {
         if (param.getDuration()==null){
             logger.info("zyp \n【未识别到时长】:{}",param.getUserId());
         }
+        R r = null;
         param.setUserId(Long.parseLong(getUserId()));
-        return questionBankService.courseAnswer(param,false);
+        if(param.getIsOpen() == null || param.getIsOpen() == 0){
+            r = questionBankService.courseAnswer(param,false);
+        }else{
+            r = questionBankService.courseAnswerIsOpen(param,false);
+        }
+        return r;
     }
 
 
@@ -206,8 +238,14 @@ public class CourseQwController extends AppBaseController {
     @PostMapping("/updateWatchDuration")
     public R updateWatchDuration(@RequestBody FsUserCourseVideoFinishUParam param)
     {
+        R r = null;
         param.setUserId(Long.parseLong(getUserId()));
-        return courseVideoService.updateWatchDuration(param);
+        if(param.getIsOpen() == null || param.getIsOpen() == 0){
+            r = courseVideoService.updateWatchDuration(param);
+        }else{
+            r = courseVideoService.updateWatchDurationIsOpen(param);
+        }
+        return r;
     }
 
     @Login
@@ -226,18 +264,31 @@ public class CourseQwController extends AppBaseController {
     @ApiOperation("是否添加客服")
     @PostMapping("/isAddKf")
     public R isAddKf(@RequestBody FsUserCourseVideoAddKfUParam param) {
+
+        R r = null;
         Long userId = Long.parseLong(getUserId());
         param.setUserId(userId);
-        return courseVideoService.isAddKf(param);
+        if(param.getIsOpen() == null || param.getIsOpen() == 0){
+            r = courseVideoService.isAddKf(param);
+        }else{
+            r = courseVideoService.isAddKfIsOpen(param);
+        }
+        return r;
     }
 
     @Login
     @ApiOperation("获取缓冲流量")
     @PostMapping("/getInternetTraffic")
     public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        R r = null;
         Long userId = Long.parseLong(getUserId());
         param.setUserId(userId);
-        return courseVideoService.getInternetTraffic(param);
+        if(param.getIsOpen() == null || param.getIsOpen() == 0){
+            r = courseVideoService.getInternetTraffic(param);
+        }else{
+            r = courseVideoService.getInternetTrafficIsOpen(param);
+        }
+        return r;
     }
 
 
@@ -288,7 +339,7 @@ public class CourseQwController extends AppBaseController {
 
     @GetMapping("/test2")
     public void test2() {
-
+        courseWatchLogService.scheduleBatchUpdateToDatabaseIsOpen();
 //        courseVideoService.updateVideoUrl();
     }